PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.9.2
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.9.2
4.0.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 +805 -115 3.1.33.9.2 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 + global $sitepress;
465 + if ( Visualizer_Module::is_pro() && ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \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,115 @@
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 ( ! empty( $_POST ) ) {
536 + $_POST = map_deep( $_POST, 'wp_strip_all_tags' );
537 + }
538 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
539 + if ( empty( $_GET['lang'] ) || empty( $_GET['parent_chart_id'] ) ) {
540 + $this->deleteOldCharts();
541 + $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
542 + $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
543 + $source->fetch();
544 + $chart_id = wp_insert_post(
302 545 array(
303 - 'focusTarget' => 'datum',
546 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
547 + 'post_title' => 'Visualization',
548 + 'post_author' => get_current_user_id(),
549 + 'post_status' => 'auto-draft',
550 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
304 551 )
305 552 );
553 + if ( $chart_id && ! is_wp_error( $chart_id ) ) {
554 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
555 + add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
556 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
557 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
558 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
559 + add_post_meta(
560 + $chart_id,
561 + Visualizer_Plugin::CF_SETTINGS,
562 + array(
563 + 'focusTarget' => 'datum',
564 + )
565 + );
566 +
567 + do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
568 + }
569 + } else {
570 + if ( current_user_can( 'edit_posts' ) ) {
571 + $parent_chart_id = isset( $_GET['parent_chart_id'] ) ? filter_var( $_GET['parent_chart_id'], FILTER_VALIDATE_INT ) : '';
572 + $success = false;
573 + if ( $parent_chart_id ) {
574 + $parent_chart = get_post( $parent_chart_id );
575 + $success = $parent_chart && $parent_chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
576 + }
577 + if ( $success ) {
578 + $new_chart_id = wp_insert_post(
579 + array(
580 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
581 + 'post_title' => 'Visualization',
582 + 'post_author' => get_current_user_id(),
583 + 'post_status' => $parent_chart->post_status,
584 + 'post_content' => $parent_chart->post_content,
585 + )
586 + );
587 +
588 + if ( is_wp_error( $new_chart_id ) ) {
589 + 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__ );
590 + } else {
591 + $post_meta = get_post_meta( $parent_chart_id );
592 + $chart_id = $new_chart_id;
593 + foreach ( $post_meta as $key => $value ) {
594 + if ( strpos( $key, 'visualizer-' ) !== false ) {
595 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
596 + }
597 + }
598 + }
599 + }
600 + }
306 601 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
307 602 }
308 - wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
309 - defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
603 + wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
604 +
605 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
606 + wp_die();
607 + }
608 + exit();
310 609 }
311 610
312 - $this->load_chart_type( $chart_id );
611 + $_POST['save_chart_image'] = isset( $_POST['save_chart_image'] ) && 'yes' === $_POST['save_chart_image'] ? true : false;
612 + $_POST['lazy_load_chart'] = isset( $_POST['lazy_load_chart'] ) && 'yes' === $_POST['lazy_load_chart'] ? true : false;
313 613
614 + if ( isset( $_POST['chart-img'] ) && ! empty( $_POST['chart-img'] ) ) {
615 + $attachment_id = $this->save_chart_image( $_POST['chart-img'], $chart_id, $_POST['save_chart_image'] );
616 + if ( $attachment_id ) {
617 + update_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, $attachment_id );
618 + }
619 + }
620 + $lib = $this->load_chart_type( $chart_id );
621 +
622 + // the alpha color picker (RGBA) is not supported by google.
623 + $color_picker_dep = 'wp-color-picker';
624 + if ( in_array( $lib, array( 'chartjs', 'datatables' ), true ) && ! wp_script_is( 'wp-color-picker-alpha', 'registered' ) ) {
625 + wp_register_script( 'wp-color-picker-alpha', VISUALIZER_ABSURL . 'js/lib/wp-color-picker-alpha.min.js', array( 'wp-color-picker' ), Visualizer_Plugin::VERSION );
626 + $color_picker_dep = 'wp-color-picker-alpha';
627 + }
628 +
314 629 // enqueue and register scripts and styles
315 630 wp_register_script( 'visualizer-chosen', VISUALIZER_ABSURL . 'js/lib/chosen.jquery.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
316 631 wp_register_style( 'visualizer-chosen', VISUALIZER_ABSURL . 'css/lib/chosen.min.css', array(), Visualizer_Plugin::VERSION );
317 632
318 633 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 );
634 + wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen', 'jquery-ui-accordion', 'jquery-ui-tabs' ), Visualizer_Plugin::VERSION, true );
320 635 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
321 636 wp_register_script(
322 637 'visualizer-render',
323 638 VISUALIZER_ABSURL . 'js/render-facade.js',
@@ -328,19 +643,23 @@
328 643 wp_register_script(
329 644 'visualizer-preview',
330 645 VISUALIZER_ABSURL . 'js/preview.js',
331 646 array(
332 - 'wp-color-picker',
647 + $color_picker_dep,
333 648 'visualizer-render',
334 649 ),
335 650 Visualizer_Plugin::VERSION,
336 651 true
337 652 );
338 - // added by Ash/Upwork
339 - if ( VISUALIZER_PRO ) {
653 + wp_register_script( 'visualizer-editor-simple', VISUALIZER_ABSURL . 'js/simple-editor.js', array( 'jquery' ), Visualizer_Plugin::VERSION, true );
654 +
655 + do_action( 'visualizer_add_scripts' );
656 +
657 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
340 658 global $Visualizer_Pro;
341 659 $Visualizer_Pro->_addScriptsAndStyles();
342 660 }
661 +
343 662 // dispatch pages
344 663 $this->_chart = get_post( $chart_id );
345 664 $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
346 665
@@ -352,8 +671,9 @@
352 671 if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
353 672 // if the cancel button is clicked.
354 673 $this->undoRevisions( $chart_id, true );
355 674 } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
675 + $this->handlePermissions();
356 676 // if the save button is clicked.
357 677 $this->undoRevisions( $chart_id, false );
358 678 } else {
359 679 // if the edit button is clicked.
@@ -359,8 +679,16 @@
359 679 // if the edit button is clicked.
360 680 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
361 681 }
362 682
683 + // Clear existing chart cache.
684 + if ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
685 + $cache_key = Visualizer_Plugin::CF_CHART_CACHE . '_' . $chart_id;
686 + if ( get_transient( $cache_key ) ) {
687 + delete_transient( $cache_key );
688 + }
689 + }
690 +
363 691 switch ( $tab ) {
364 692 case 'settings':
365 693 $this->_handleDataAndSettingsPage();
366 694 break;
@@ -368,9 +696,9 @@
368 696 case 'visualizer': // fall through.
369 697 $this->_handleTypesPage();
370 698 break;
371 699 default:
372 - do_action( 'visualizer_pro_handle_tab', $tab, $this->_chart );
700 + // this should never happen.
373 701 break;
374 702 }
375 703 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
376 704 }
@@ -377,25 +705,26 @@
377 705
378 706 /**
379 707 * Load code editor assets.
380 708 */
381 - private function loadCodeEditorAssets() {
709 + private function loadCodeEditorAssets( $chart_id ) {
382 710 global $wp_version;
383 711
384 - if ( ! VISUALIZER_PRO ) {
712 + $wp_scripts = wp_scripts();
713 +
714 + // data tables assets.
715 + wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
716 + wp_register_style( 'visualizer-datatables', VISUALIZER_ABSURL . 'css/lib/datatables.min.css', array(), Visualizer_Plugin::VERSION );
717 + 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 );
718 + wp_enqueue_script( 'visualizer-datatables' );
719 + wp_enqueue_style( 'visualizer-jquery-ui' );
720 +
721 + if ( ! Visualizer_Module::is_pro() ) {
385 722 return;
386 723 }
387 724
388 - $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping();
725 + $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping( $chart_id );
389 726
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 727 if ( version_compare( $wp_version, '4.9.0', '<' ) ) {
399 728 // code mirror assets.
400 729 wp_register_script( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
401 730 wp_register_script( 'visualizer-codemirror-placeholder', '//codemirror.net/addon/display/placeholder.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
@@ -429,8 +758,25 @@
429 758 return $table_col_mapping;
430 759 }
431 760
432 761 /**
762 + * Handle permissions from the new conslidated settings sidebar.
763 + */
764 + private function handlePermissions() {
765 + if ( ! Visualizer_Module::is_pro() ) {
766 + return;
767 + }
768 +
769 + // we will not support old free and new pro.
770 + // handling new free and old pro.
771 + if ( has_action( 'visualizer_pro_handle_tab' ) ) {
772 + do_action( 'visualizer_pro_handle_tab', 'permissions', $this->_chart );
773 + } else {
774 + do_action( 'visualizer_handle_permissions', $this->_chart );
775 + }
776 + }
777 +
778 + /**
433 779 * Handle data and settings page
434 780 */
435 781 private function _handleDataAndSettingsPage() {
436 782 if ( isset( $_POST['map_api_key'] ) ) {
@@ -435,30 +781,53 @@
435 781 private function _handleDataAndSettingsPage() {
436 782 if ( isset( $_POST['map_api_key'] ) ) {
437 783 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
438 784 }
439 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
440 - if ( $this->_chart->post_status == 'auto-draft' ) {
785 +
786 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
787 + if ( $this->_chart->post_status === 'auto-draft' ) {
441 788 $this->_chart->post_status = 'publish';
442 789
443 790 // 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 791 // we do not want any difference in data so disable revisions temporarily.
445 - add_filter( 'wp_revisions_to_keep', '__return_false' );
792 + $this->disableRevisionsTemporarily();
793 +
446 794 wp_update_post( $this->_chart->to_array() );
447 795 }
448 796 // save meta data only when it is NOT being canceled.
449 797 if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
450 798 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
799 +
800 + // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
801 + // this will help in searching with the chart id.
802 + $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
803 + $title = null;
804 + if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
805 + $title = $settings['title'];
806 + if ( is_array( $title ) ) {
807 + $title = $settings['title']['text'];
808 + }
809 + }
810 + if ( empty( $title ) ) {
811 + $title = $this->_chart->ID;
812 + }
813 + $settings['internal_title'] = $title;
814 + $settings_label = isset( $settings['pieResidueSliceLabel'] ) ? $settings['pieResidueSliceLabel'] : '';
815 + if ( empty( $settings_label ) ) {
816 + $settings['pieResidueSliceLabel'] = esc_html__( 'Other', 'visualizer' );
817 + } else {
818 + $settings['pieResidueSliceLabel'] = $settings_label;
819 + }
820 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
451 821 }
452 822 $render = new Visualizer_Render_Page_Send();
453 823 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
454 824 wp_iframe( array( $render, 'render' ) );
455 -
456 825 return;
457 826 }
458 827 $data = $this->_getChartArray();
459 828 $sidebar = '';
460 - $sidebar_class = 'Visualizer_Render_Sidebar_Type_' . ucfirst( $data['type'] );
829 + $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
461 830 if ( class_exists( $sidebar_class, true ) ) {
462 831 $sidebar = new $sidebar_class( $data['settings'] );
463 832 $sidebar->__series = $data['series'];
464 833 $sidebar->__data = $data['data'];
@@ -463,14 +832,14 @@
463 832 $sidebar->__series = $data['series'];
464 833 $sidebar->__data = $data['data'];
465 834 } else {
466 835 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
467 - if ( $sidebar != '' ) {
836 + if ( $sidebar !== '' ) {
468 837 $sidebar->__series = $data['series'];
469 838 $sidebar->__data = $data['data'];
470 839 }
471 840 }
472 - unset( $data['settings']['width'], $data['settings']['height'] );
841 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
473 842 wp_enqueue_style( 'wp-color-picker' );
474 843 wp_enqueue_style( 'visualizer-frame' );
475 844 wp_enqueue_script( 'visualizer-preview' );
476 845 wp_enqueue_script( 'visualizer-chosen' );
@@ -475,17 +844,37 @@
475 844 wp_enqueue_script( 'visualizer-preview' );
476 845 wp_enqueue_script( 'visualizer-chosen' );
477 846 wp_enqueue_script( 'visualizer-render' );
478 847
479 - $table_col_mapping = $this->loadCodeEditorAssets();
848 + if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
849 + wp_enqueue_script( 'visualizer-editor-simple' );
850 + wp_localize_script(
851 + 'visualizer-editor-simple',
852 + 'visualizer1',
853 + array(
854 + 'ajax' => array(
855 + 'url' => admin_url( 'admin-ajax.php' ),
856 + 'nonces' => array(
857 + ),
858 + 'actions' => array(
859 + ),
860 + ),
861 + )
862 + );
863 + }
480 864
865 + $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
866 +
481 867 wp_localize_script(
482 868 'visualizer-render',
483 869 'visualizer',
484 870 array(
485 871 'l10n' => array(
486 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
872 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
487 873 'loading' => esc_html__( 'Loading...', 'visualizer' ),
874 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
875 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
876 + '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 877 ),
489 878 'charts' => array(
490 879 'canvas' => $data,
491 880 'id' => $this->_chart->ID,
@@ -496,19 +885,29 @@
496 885 'url' => admin_url( 'admin-ajax.php' ),
497 886 'nonces' => array(
498 887 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
499 888 'db_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ),
889 + 'json_get_roots' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION ),
890 + 'json_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION ),
891 + 'json_set_schedule' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION ),
500 892 ),
501 893 'actions' => array(
502 894 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
503 895 'db_get_data' => Visualizer_Plugin::ACTION_FETCH_DB_DATA,
896 + 'json_get_roots' => Visualizer_Plugin::ACTION_JSON_GET_ROOTS,
897 + 'json_get_data' => Visualizer_Plugin::ACTION_JSON_GET_DATA,
898 + 'json_set_schedule' => Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE,
504 899 ),
505 900 ),
506 901 'db_query' => array(
507 902 'tables' => $table_col_mapping,
508 903 ),
509 - 'is_pro' => VISUALIZER_PRO,
904 + 'is_pro' => Visualizer_Module::is_pro(),
510 905 'page_type' => 'chart',
906 + 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
907 + 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
908 + 'is_front' => false,
909 + 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
511 910 )
512 911 );
513 912
514 913 $render = new Visualizer_Render_Page_Data();
@@ -516,21 +915,25 @@
516 915 $render->type = $data['type'];
517 916 $render->custom_css = $data['css'];
518 917 $render->sidebar = $sidebar;
519 918 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
520 - $render->button = filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART
919 + $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
521 920 ? esc_html__( 'Save Chart', 'visualizer' )
522 921 : esc_html__( 'Create Chart', 'visualizer' );
523 - if ( filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART ) {
922 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
524 923 $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
525 924 }
526 925 } else {
527 926 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
528 927 }
529 - if ( VISUALIZER_PRO ) {
928 +
929 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
930 +
931 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
530 932 global $Visualizer_Pro;
531 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
933 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
532 934 }
935 +
533 936 $this->_addAction( 'admin_head', 'renderFlattrScript' );
534 937 wp_iframe( array( $render, 'render' ) );
535 938 }
536 939
@@ -542,24 +945,35 @@
542 945 * @access private
543 946 */
544 947 private function _handleTypesPage() {
545 948 // process post request
546 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
949 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
547 950 $type = filter_input( INPUT_POST, 'type' );
548 - if ( in_array( $type, Visualizer_Plugin::getChartTypes() ) ) {
951 + $library = filter_input( INPUT_POST, 'chart-library' );
952 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
953 + if ( empty( $library ) ) {
954 + // library cannot be empty.
955 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
956 + return;
957 + }
958 +
549 959 // save new chart type
550 960 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, $type );
961 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_LIBRARY, $library );
551 962 // if the chart has default data, update it with appropriate default data for new type
552 963 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
553 964 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
554 965 $source->fetch();
555 - $this->_chart->post_content = $source->getData();
966 + $this->_chart->post_content = $source->getData( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
556 967 wp_update_post( $this->_chart->to_array() );
557 968 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
558 969 }
970 +
971 + Visualizer_Module_Utility::set_defaults( $this->_chart );
972 +
559 973 // redirect to next tab
560 974 // changed by Ash/Upwork
561 - wp_redirect( add_query_arg( 'tab', 'settings' ) );
975 + wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
562 976
563 977 return;
564 978 }
565 979 }
@@ -564,9 +978,9 @@
564 978 }
565 979 }
566 980 $render = new Visualizer_Render_Page_Types();
567 981 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
568 - $render->types = Visualizer_Module_Admin::_getChartTypesLocalized();
982 + $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
569 983 $render->chart = $this->_chart;
570 984 wp_enqueue_style( 'visualizer-frame' );
571 985 wp_enqueue_script( 'visualizer-frame' );
572 986 wp_iframe( array( $render, 'render' ) );
@@ -582,10 +996,131 @@
582 996 */
583 997 public function renderFlattrScript() {
584 998 echo '';
585 999 }
586 - // changed by Ash/Upwork
1000 +
587 1001 /**
1002 + * Processes the CSV that is sent in the request as a string.
1003 + *
1004 + * @since 3.2.0
1005 + */
1006 + private function handleCSVasString( $data, $editor_type ) {
1007 + $source = null;
1008 +
1009 + switch ( $editor_type ) {
1010 + case 'text':
1011 + // data coming in from the text editor.
1012 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1013 + $handle = fopen( $tmpfile, 'w' );
1014 + $values = preg_split( '/[\n\r]+/', stripslashes( trim( $data ) ) );
1015 + if ( $values ) {
1016 + foreach ( $values as $row ) {
1017 + if ( empty( $row ) ) {
1018 + continue;
1019 + }
1020 + $row = explode( ',', $row );
1021 + $row = array_map(
1022 + function( $r ) {
1023 + return '' === $r ? ' ' : $r;
1024 + },
1025 + $row
1026 + );
1027 + $row = implode( ',', $row );
1028 + // don't use fpucsv here because we need to just dump the data
1029 + // minus the empty rows
1030 + // because fputcsv needs to tokenize
1031 + // we can standardize the CSV enclosure here and replace all ' with "
1032 + // we can assume that if there are an even number of ' they should be changed to "
1033 + // but that will screw up words like fo'c'sle
1034 + // so here let's just assume ' will NOT be used for enclosures
1035 + fwrite( $handle, $row );
1036 + fwrite( $handle, PHP_EOL );
1037 + }
1038 + }
1039 + $source = new Visualizer_Source_Csv( $tmpfile );
1040 + fclose( $handle );
1041 + break;
1042 + case 'table':
1043 + // Import from Chart
1044 + // fall-through.
1045 + case 'excel':
1046 + // data coming in from the excel editor.
1047 + $source = apply_filters( 'visualizer_pro_handle_chart_data', $data, '' );
1048 + break;
1049 + }
1050 + return $source;
1051 + }
1052 +
1053 + /**
1054 + * Parses the data uploaded as an HTML table.
1055 + *
1056 + * @since 3.2.0
1057 + *
1058 + * @access private
1059 + */
1060 + private function handleTabularData() {
1061 + $csv = array();
1062 + // the datatable mentions the headers twice, so lets remove the duplicates.
1063 + $headers = array_unique( array_filter( $_POST['header'] ) );
1064 + $types = $_POST['type'];
1065 +
1066 + // capture all the indexes that correspond to excluded columns.
1067 + $exclude = array();
1068 + $index = 0;
1069 + foreach ( $types as $type ) {
1070 + if ( empty( $type ) ) {
1071 + $exclude[] = $index;
1072 + }
1073 + $index++;
1074 + }
1075 +
1076 + // when N headers are being renamed, the number of headers increases by N
1077 + // because of the way datatable duplicates header information
1078 + // so unset the headers that have been renamed.
1079 + if ( count( $headers ) !== count( $types ) ) {
1080 + $to = count( $headers );
1081 + for ( $i = count( $types ); $i < $to; $i++ ) {
1082 + unset( $headers[ $i + 1 ] );
1083 + }
1084 + }
1085 +
1086 + $columns = array();
1087 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1088 + if ( ! isset( $_POST[ 'data' . $i ] ) ) {
1089 + continue;
1090 + }
1091 + $columns[ $i ] = $_POST[ 'data' . $i ];
1092 + }
1093 +
1094 + $csv[] = $headers;
1095 + $csv[] = $types;
1096 + for ( $j = 0; $j < count( $columns[0] ); $j++ ) {
1097 + $row = array();
1098 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1099 + $row[] = sanitize_text_field( $columns[ $i ][ $j ] );
1100 + }
1101 + $csv[] = $row;
1102 + }
1103 +
1104 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1105 + $handle = fopen( $tmpfile, 'w' );
1106 +
1107 + if ( $csv ) {
1108 + $index = 0;
1109 + foreach ( $csv as $row ) {
1110 + // remove all the cells corresponding to the excluded headers.
1111 + foreach ( $exclude as $j ) {
1112 + unset( $row[ $j ] );
1113 + }
1114 + fputcsv( $handle, $row );
1115 + }
1116 + }
1117 + $source = new Visualizer_Source_Csv( $tmpfile );
1118 + fclose( $handle );
1119 + return $source;
1120 + }
1121 +
1122 + /**
588 1123 * Parses uploaded CSV file and saves new data for the chart.
589 1124 *
590 1125 * @since 1.0.0
591 1126 *
@@ -607,9 +1142,9 @@
607 1142
608 1143 // check chart, if chart exists
609 1144 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
610 1145 $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 ) {
1146 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
612 1147 if ( ! $can_die ) {
613 1148 return;
614 1149 }
615 1150 status_header( 400 );
@@ -615,8 +1150,10 @@
615 1150 status_header( 400 );
616 1151 exit;
617 1152 }
618 1153
1154 + 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__ );
1155 +
619 1156 if ( ! isset( $_POST['vz-import-time'] ) ) {
620 1157 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
621 1158 }
622 1159
@@ -628,27 +1165,58 @@
628 1165
629 1166 // delete "import from db" specific parameters.
630 1167 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
631 1168 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1169 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
632 1170 }
633 1171
1172 + // delete json related data.
1173 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_URL );
1174 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_ROOT );
1175 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_PAGING );
1176 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_HEADERS );
1177 +
1178 + // delete last error
1179 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR );
1180 +
1181 + // delete editor related data.
1182 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1183 +
1184 + // delete this so that a JSON import can be later edited manually without a problem.
1185 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1186 +
634 1187 $source = null;
635 1188 $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'] );
1189 +
1190 + $remote_data = false;
1191 + if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1192 + $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1193 + }
1194 + if ( false !== $remote_data ) {
1195 + $source = new Visualizer_Source_Csv_Remote( $remote_data );
638 1196 if ( isset( $_POST['vz-import-time'] ) ) {
639 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1197 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
640 1198 }
1199 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
641 1200 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
642 1201 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
643 1202 } 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 );
1203 + $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1204 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1205 + } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1206 + $source = $this->handleTabularData();
1207 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
645 1208 } else {
646 - $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please, try again.', 'visualizer' );
1209 + 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__ );
1210 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1211 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
647 1212 }
1213 +
1214 + 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__ );
1215 +
648 1216 if ( $source ) {
649 1217 if ( $source->fetch() ) {
650 - $content = $source->getData();
1218 + $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
651 1219 $populate = true;
652 1220 if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
653 1221 $json = unserialize( $content );
654 1222 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
@@ -654,8 +1222,9 @@
654 1222 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
655 1223 // if we populate the data even if it is empty, the chart will show "Table has no columns".
656 1224 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
657 1225 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__ );
1226 + 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 1227 $populate = false;
659 1228 }
660 1229 }
661 1230 if ( $populate ) {
@@ -661,17 +1230,43 @@
661 1230 if ( $populate ) {
662 1231 $chart->post_content = $content;
663 1232 }
664 1233 wp_update_post( $chart->to_array() );
1234 +
665 1235 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
666 1236 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
667 1237 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1238 +
1239 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1240 +
1241 + Visualizer_Module_Utility::set_defaults( $chart, null );
1242 +
1243 + $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1244 + if ( isset( $settings['series'] ) && ! ( count( $settings['series'] ) - count( $source->getSeries() ) > 1 ) ) {
1245 + $diff_total_series = abs( count( $settings['series'] ) - count( $source->getSeries() ) );
1246 + if ( $diff_total_series ) {
1247 + foreach ( range( 1, $diff_total_series ) as $k => $diff_series ) {
1248 + $settings['series'][] = end( $settings['series'] );
1249 + }
1250 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
1251 + }
1252 + }
1253 +
668 1254 $render->id = $chart->ID;
669 - $render->data = json_encode( $source->getRawData() );
1255 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
670 1256 $render->series = json_encode( $source->getSeries() );
1257 + $render->settings = json_encode( $settings );
671 1258 } else {
672 - $render->message = esc_html__( 'CSV file is broken or invalid. Please, try again.', 'visualizer' );
1259 + $error = $source->get_error();
1260 + if ( empty( $error ) ) {
1261 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1262 + }
1263 + $render->message = $error;
1264 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1265 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
673 1266 }
1267 + } else {
1268 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
674 1269 }
675 1270 $render->render();
676 1271 if ( ! $can_die ) {
677 1272 return;
@@ -687,27 +1282,18 @@
687 1282 * @access public
688 1283 */
689 1284 public function cloneChart() {
690 1285 $chart_id = $success = false;
691 - $nonce = wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), Visualizer_Plugin::ACTION_CLONE_CHART );
1286 + $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
692 1287 $capable = current_user_can( 'edit_posts' );
693 1288 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 - );
1289 + $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
704 1290 if ( $chart_id ) {
705 1291 $chart = get_post( $chart_id );
706 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
1292 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
707 1293 }
708 1294 }
709 - $redirect = wp_get_referer();
1295 + $redirect = remove_query_arg( 'vaction', wp_get_referer() );
710 1296 if ( $success ) {
711 1297 $new_chart_id = wp_insert_post(
712 1298 array(
713 1299 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
@@ -716,23 +1302,35 @@
716 1302 'post_status' => $chart->post_status,
717 1303 'post_content' => $chart->post_content,
718 1304 )
719 1305 );
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' ),
1306 + if ( is_wp_error( $new_chart_id ) ) {
1307 + 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__ );
1308 + } else {
1309 + $post_meta = get_post_meta( $chart_id );
1310 + foreach ( $post_meta as $key => $value ) {
1311 + if ( strpos( $key, 'visualizer-' ) !== false ) {
1312 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1313 + }
1314 + }
1315 + $redirect = esc_url(
1316 + add_query_arg(
1317 + array(
1318 + 'page' => 'visualizer',
1319 + 'type' => filter_input( INPUT_GET, 'type' ),
1320 + 'vaction' => false,
1321 + ),
1322 + admin_url( 'admin.php' )
730 1323 ),
731 - admin_url( 'upload.php' )
1324 + null,
1325 + 'db'
732 1326 );
733 1327 }
734 1328 }
1329 +
1330 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1331 + wp_die();
1332 + }
735 1333 wp_redirect( $redirect );
736 1334 exit;
737 1335 }
738 1336
@@ -778,9 +1376,12 @@
778 1376 $data = $this->_getChartArray();
779 1377 $render = new Visualizer_Render_Page_Data();
780 1378 $render->chart = $this->_chart;
781 1379 $render->type = $data['type'];
782 - unset( $data['settings']['width'], $data['settings']['height'] );
1380 +
1381 + if ( $data && $data['settings'] ) {
1382 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1383 + }
783 1384 wp_enqueue_style( 'visualizer-frame' );
784 1385 wp_enqueue_script( 'visualizer-render' );
785 1386 wp_localize_script(
786 1387 'visualizer-render',
@@ -786,9 +1387,9 @@
786 1387 'visualizer-render',
787 1388 'visualizer',
788 1389 array(
789 1390 'l10n' => array(
790 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
1391 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
791 1392 'loading' => esc_html__( 'Loading...', 'visualizer' ),
792 1393 ),
793 1394 'charts' => array(
794 1395 'canvas' => $data,
@@ -794,13 +1395,16 @@
794 1395 'canvas' => $data,
795 1396 ),
796 1397 )
797 1398 );
798 - // Added by Ash/Upwork
799 - if ( VISUALIZER_PRO ) {
1399 +
1400 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1401 +
1402 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
800 1403 global $Visualizer_Pro;
801 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
1404 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
802 1405 }
1406 +
803 1407 // Added by Ash/Upwork
804 1408 $this->_addAction( 'admin_head', 'renderFlattrScript' );
805 1409 wp_iframe( array( $render, 'render' ) );
806 1410 }
@@ -813,13 +1417,14 @@
813 1417 public function getQueryData() {
814 1418 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
815 1419
816 1420 $params = wp_parse_args( $_POST['params'] );
817 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1421 + $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1422 +
1423 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
818 1424 $html = $source->fetch( true );
819 - $error = '';
820 - if ( empty( $html ) ) {
821 - $error = $source->get_error();
1425 + $error = $source->get_error();
1426 + if ( ! empty( $error ) ) {
822 1427 wp_send_json_error( array( 'msg' => $error ) );
823 1428 }
824 1429 wp_send_json_success( array( 'table' => $html ) );
825 1430 }
@@ -842,21 +1447,42 @@
842 1447 ),
843 1448 )
844 1449 );
845 1450
1451 + $hours = filter_input(
1452 + INPUT_POST,
1453 + 'refresh',
1454 + FILTER_VALIDATE_FLOAT,
1455 + array(
1456 + 'options' => array(
1457 + 'min_range' => -1,
1458 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1459 + ),
1460 + )
1461 + );
1462 +
1463 + if ( ! is_numeric( $hours ) ) {
1464 + $hours = -1;
1465 + }
1466 +
846 1467 $render = new Visualizer_Render_Page_Update();
847 1468 if ( $chart_id ) {
848 1469 $params = wp_parse_args( $_POST['params'] );
849 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1470 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
850 1471 $source->fetch( false );
851 1472 $error = $source->get_error();
852 1473 if ( empty( $error ) ) {
853 - $hours = $_POST['refresh'];
854 1474 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
855 1475 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
856 1476 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
857 1477 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
858 1478 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1479 + if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1480 + $remote_db_params = $params;
1481 + unset( $remote_db_params['query'] );
1482 + unset( $remote_db_params['chart_id'] );
1483 + update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1484 + }
859 1485
860 1486 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
861 1487 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
862 1488 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
@@ -863,13 +1489,14 @@
863 1489
864 1490 wp_update_post(
865 1491 array(
866 1492 'ID' => $chart_id,
867 - 'post_content' => $source->getData(),
1493 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
868 1494 )
869 1495 );
870 - $render->data = json_encode( $source->getRawData() );
1496 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
871 1497 $render->series = json_encode( $source->getSeries() );
1498 + $render->id = $chart_id;
872 1499 } else {
873 1500 $render->message = $error;
874 1501 }
875 1502 }
@@ -898,13 +1525,76 @@
898 1525 ),
899 1526 )
900 1527 );
901 1528
902 - $hours = $_POST['refresh'];
1529 + $hours = filter_input(
1530 + INPUT_POST,
1531 + 'refresh',
1532 + FILTER_VALIDATE_INT,
1533 + array(
1534 + 'options' => array(
1535 + 'min_range' => -1,
1536 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1537 + ),
1538 + )
1539 + );
903 1540
1541 + if ( 0 !== $hours && empty( $hours ) ) {
1542 + $hours = -1;
1543 + }
1544 +
904 1545 do_action( 'visualizer_save_filter', $chart_id, $hours );
905 1546
906 1547 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
907 1548 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
908 1549 }
1550 + }
1551 +
1552 + /**
1553 + * Save chart image.
1554 + *
1555 + * @param string $base64_img Chart image.
1556 + * @param int $chart_id Chart ID.
1557 + * @param bool $save_attachment Save attachment.
1558 + * @return attachment ID
1559 + */
1560 + public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1561 + // Delete old chart image.
1562 + $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
1563 + if ( $old_attachment_id ) {
1564 + wp_delete_attachment( $old_attachment_id, true );
1565 + }
1566 +
1567 + if ( ! $save_attachment ) {
1568 + return 0;
1569 + }
1570 +
1571 + // Upload dir.
1572 + $upload_dir = wp_upload_dir();
1573 + $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1574 +
1575 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1576 + $img = str_replace( ' ', '+', $img );
1577 + $decoded = base64_decode( $img );
1578 + $filename = 'visualization-' . $chart_id . '.png';
1579 + $file_type = 'image/png';
1580 + $hashed_filename = $filename;
1581 +
1582 + // Save the image in the uploads directory.
1583 + require_once ABSPATH . '/wp-admin/includes/file.php';
1584 + \WP_Filesystem();
1585 + global $wp_filesystem;
1586 + $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1587 +
1588 + // Insert new chart image.
1589 + $attachment = array(
1590 + 'post_mime_type' => $file_type,
1591 + 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1592 + 'post_content' => '',
1593 + 'post_status' => 'inherit',
1594 + 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1595 + );
1596 +
1597 + $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1598 + return $attach_id;
909 1599 }
910 1600 }