PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.6.1
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.6.1
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 3.10.4 All 148 releases
← All changes | classes/Visualizer/Module/Chart.php +928 -76 3.0.83.6.1 View file →
@@ -57,13 +57,234 @@
57 57 $this->_addAjaxAction( Visualizer_Plugin::ACTION_EDIT_CHART, 'renderChartPages' );
58 58 $this->_addAjaxAction( Visualizer_Plugin::ACTION_UPLOAD_DATA, 'uploadData' );
59 59 $this->_addAjaxAction( Visualizer_Plugin::ACTION_CLONE_CHART, 'cloneChart' );
60 60 $this->_addAjaxAction( Visualizer_Plugin::ACTION_EXPORT_DATA, 'exportData' );
61 +
62 + $this->_addAjaxAction( Visualizer_Plugin::ACTION_FETCH_DB_DATA, 'getQueryData' );
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 +
70 + $this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY, 'saveFilter' );
71 +
72 + $this->_addFilter( 'visualizer_get_sidebar', 'getSidebar', 10, 2 );
73 +
61 74 }
62 75
63 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 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE );
139 +
140 + if ( -1 < $time ) {
141 + add_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE, $time );
142 + // Update schedules.
143 + $schedules = get_option( Visualizer_Plugin::CF_JSON_SCHEDULE, array() );
144 + $schedules[ $chart_id ] = time() + $time * HOUR_IN_SECONDS;
145 + update_option( Visualizer_Plugin::CF_JSON_SCHEDULE, $schedules );
146 + }
147 + wp_send_json_success();
148 + }
149 +
150 + /**
151 + * Get the root elements for JSON-endpoint.
152 + *
153 + * @since ?
154 + *
155 + * @access public
156 + */
157 + public function getJsonRoots() {
158 + check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION, 'security' );
159 +
160 + $params = wp_parse_args( $_POST['params'] );
161 +
162 + $source = new Visualizer_Source_Json( $params );
163 +
164 + $roots = $source->fetchRoots();
165 + if ( empty( $roots ) ) {
166 + wp_send_json_error( array( 'msg' => $source->get_error() ) );
167 + }
168 +
169 + wp_send_json_success( array( 'url' => $params['url'], 'roots' => $roots ) );
170 + }
171 +
172 + /**
173 + * Get the data for the JSON-endpoint corresponding to the chosen root.
174 + *
175 + * @since ?
176 + *
177 + * @access public
178 + */
179 + public function getJsonData() {
180 + check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION, 'security' );
181 +
182 + $params = wp_parse_args( $_POST['params'] );
183 +
184 + $chart_id = $params['chart'];
185 +
186 + if ( empty( $chart_id ) ) {
187 + wp_die();
188 + }
189 +
190 + $source = new Visualizer_Source_Json( $params );
191 + $source->fetch();
192 + $data = $source->getRawData();
193 +
194 + if ( empty( $data ) ) {
195 + wp_send_json_error( array( 'msg' => esc_html__( 'Unable to fetch data from the endpoint. Please try again.', 'visualizer' ) ) );
196 + }
197 +
198 + $data = Visualizer_Render_Layout::show( 'editor-table', $data, $chart_id, 'viz-json-table', false, false );
199 + wp_send_json_success( array( 'table' => $data, 'root' => $params['root'], 'url' => $params['url'], 'paging' => $source->getPaginationElements() ) );
200 + }
201 +
202 + /**
203 + * Updates the database with the correct post parameters for JSON-endpoint charts.
204 + *
205 + * @since ?
206 + *
207 + * @access public
208 + */
209 + public function setJsonData() {
210 + check_ajax_referer( Visualizer_Plugin::ACTION_JSON_SET_DATA . Visualizer_Plugin::VERSION, 'security' );
211 +
212 + $params = $_POST;
213 + $chart_id = $_GET['chart'];
214 +
215 + if ( empty( $chart_id ) ) {
216 + wp_die();
217 + }
218 +
219 + $chart = get_post( $chart_id );
220 +
221 + $source = new Visualizer_Source_Json( $params );
222 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true );
223 + $source->fetchFromEditableTable();
224 +
225 + $content = $source->getData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
226 + $chart->post_content = $content;
227 + wp_update_post( $chart->to_array() );
228 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
229 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
230 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
231 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_URL, $params['url'] );
232 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_ROOT, $params['root'] );
233 +
234 + delete_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_HEADERS );
235 + $headers = array( 'method' => $params['method'] );
236 + if ( ! empty( $params['auth'] ) ) {
237 + $headers['auth'] = $params['auth'];
238 + } elseif ( ! empty( $params['username'] ) && ! empty( $params['password'] ) ) {
239 + $headers['auth'] = array( 'username' => $params['username'], 'password' => $params['password'] );
240 + }
241 +
242 + add_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_HEADERS, $headers );
243 +
244 + delete_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_PAGING );
245 + if ( ! empty( $params['paging'] ) ) {
246 + add_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_PAGING, $params['paging'] );
247 + }
248 +
249 + $time = filter_input(
250 + INPUT_POST,
251 + 'time',
252 + FILTER_VALIDATE_INT,
253 + array(
254 + 'options' => array(
255 + 'min_range' => -1,
256 + ),
257 + )
258 + );
259 +
260 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE );
261 +
262 + if ( -1 < $time ) {
263 + add_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE, $time );
264 + }
265 +
266 + // delete other source specific parameters.
267 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
268 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
269 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_URL );
270 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_SCHEDULE );
271 +
272 + $render = new Visualizer_Render_Page_Update();
273 + $render->id = $chart->ID;
274 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
275 + $render->series = json_encode( $source->getSeries() );
276 + $render->render();
277 +
278 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
279 + }
280 +
281 +
282 + /**
64 283 * Fetches charts from database.
65 284 *
285 + * This method is also called from the media pop-up (classic editor: create a post and add chart from insert content).
286 + *
66 287 * @since 1.0.0
67 288 *
68 289 * @access public
69 290 */
@@ -71,9 +292,12 @@
71 292 $query_args = array(
72 293 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
73 294 'posts_per_page' => 9,
74 295 'paged' => filter_input(
75 - INPUT_GET, 'page', FILTER_VALIDATE_INT, array(
296 + INPUT_GET,
297 + 'page',
298 + FILTER_VALIDATE_INT,
299 + array(
76 300 'options' => array(
77 301 'min_range' => 1,
78 302 'default' => 1,
79 303 ),
@@ -85,9 +309,9 @@
85 309 // 'filter' is from the modal from the add media button.
86 310 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
87 311 }
88 312
89 - if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes() ) ) {
313 + if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes(), true ) ) {
90 314 $query_args['meta_query'] = array(
91 315 array(
92 316 'key' => Visualizer_Plugin::CF_CHART_TYPE,
93 317 'value' => $filter,
@@ -100,8 +324,18 @@
100 324 while ( $query->have_posts() ) {
101 325 $chart = $query->next_post();
102 326 $chart_data = $this->_getChartArray( $chart );
103 327 $chart_data['id'] = $chart->ID;
328 + $chart_data['library'] = $this->load_chart_type( $chart->ID );
329 + $css = '';
330 + $settings = $chart_data['settings'];
331 + $arguments = $this->get_inline_custom_css( 'visualizer-chart-' . $chart->ID, $settings );
332 + if ( ! empty( $arguments ) ) {
333 + $css = $arguments[0];
334 + $settings = $arguments[1];
335 + }
336 + $chart_data['settings'] = $settings;
337 + $chart_data['css'] = $css;
104 338 $charts[] = $chart_data;
105 339 }
106 340 self::_sendResponse(
107 341 array(
@@ -128,15 +362,34 @@
128 362 $chart = $this->_chart;
129 363 }
130 364 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
131 365 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
132 - $data = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA, unserialize( $chart->post_content ), $chart->ID, $type );
366 + $data = self::get_chart_data( $chart, $type );
367 + $library = $this->load_chart_type( $chart->ID );
133 368
369 + $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
370 + $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
371 + if ( ! empty( $atts['settings'] ) ) {
372 + $settings = apply_filters( $atts['settings'], $settings, $chart->ID, $type );
373 + }
374 +
375 + $css = '';
376 + $arguments = $this->get_inline_custom_css( 'visualizer-' . $chart->ID, $settings );
377 + if ( ! empty( $arguments ) ) {
378 + $css = $arguments[0];
379 + $settings = $arguments[1];
380 + }
381 +
382 + $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
383 +
134 384 return array(
135 385 'type' => $type,
136 386 'series' => $series,
137 - 'settings' => get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true ),
387 + 'settings' => $settings,
138 388 'data' => $data,
389 + 'library' => $library,
390 + 'css' => $css,
391 + 'date_formats' => $date_formats,
139 392 );
140 393 }
141 394
142 395 /**
@@ -163,9 +416,9 @@
163 416 *
164 417 * @access public
165 418 */
166 419 public function deleteChart() {
167 - $is_post = $_SERVER['REQUEST_METHOD'] == 'POST';
420 + $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
168 421 $input_method = $is_post ? INPUT_POST : INPUT_GET;
169 422 $chart_id = $success = false;
170 423 $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
171 424 $capable = current_user_can( 'delete_posts' );
@@ -170,9 +423,12 @@
170 423 $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
171 424 $capable = current_user_can( 'delete_posts' );
172 425 if ( $nonce && $capable ) {
173 426 $chart_id = filter_input(
174 - $input_method, 'chart', FILTER_VALIDATE_INT, array(
427 + $input_method,
428 + 'chart',
429 + FILTER_VALIDATE_INT,
430 + array(
175 431 'options' => array(
176 432 'min_range' => 1,
177 433 ),
178 434 )
@@ -178,9 +434,9 @@
178 434 )
179 435 );
180 436 if ( $chart_id ) {
181 437 $chart = get_post( $chart_id );
182 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
438 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
183 439 }
184 440 }
185 441 if ( $success ) {
186 442 wp_delete_post( $chart_id, true );
@@ -191,13 +447,41 @@
191 447 'success' => $success,
192 448 )
193 449 );
194 450 }
195 - wp_redirect( wp_get_referer() );
451 + wp_redirect( remove_query_arg( 'vaction', wp_get_referer() ) );
196 452 exit;
197 453 }
198 454
199 455 /**
456 + * Delete charts that are still in auto-draft mode.
457 + */
458 + private function deleteOldCharts() {
459 + $query = new WP_Query(
460 + array(
461 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
462 + 'post_status' => 'auto-draft',
463 + 'fields' => 'ids',
464 + 'update_post_meta_cache' => false,
465 + 'update_post_term_cache' => false,
466 + 'posts_per_page' => 50,
467 + 'date_query' => array(
468 + array(
469 + 'before' => 'today',
470 + ),
471 + ),
472 + )
473 + );
474 +
475 + if ( $query->have_posts() ) {
476 + $ids = array();
477 + while ( $query->have_posts() ) {
478 + wp_delete_post( $query->next_post(), true );
479 + }
480 + }
481 + }
482 +
483 + /**
200 484 * Renders appropriate page for chart builder. Creates new auto draft chart
201 485 * if no chart has been specified.
202 486 *
203 487 * @since 1.0.0
@@ -205,11 +489,14 @@
205 489 * @access public
206 490 */
207 491 public function renderChartPages() {
208 492 defined( 'IFRAME_REQUEST' ) || define( 'IFRAME_REQUEST', 1 );
493 + // Set current screen for the render chart.
494 + set_current_screen( 'visualizer_render_chart' );
209 495 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
210 496 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
211 - if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
497 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
498 + $this->deleteOldCharts();
212 499 $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
213 500 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
214 501 $source->fetch();
215 502 $chart_id = wp_insert_post(
@@ -217,9 +504,9 @@
217 504 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
218 505 'post_title' => 'Visualization',
219 506 'post_author' => get_current_user_id(),
220 507 'post_status' => 'auto-draft',
221 - 'post_content' => $source->getData(),
508 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
222 509 )
223 510 );
224 511 if ( $chart_id && ! is_wp_error( $chart_id ) ) {
225 512 add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
@@ -225,10 +512,13 @@
225 512 add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
226 513 add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
227 514 add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
228 515 add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
516 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
229 517 add_post_meta(
230 - $chart_id, Visualizer_Plugin::CF_SETTINGS, array(
518 + $chart_id,
519 + Visualizer_Plugin::CF_SETTINGS,
520 + array(
231 521 'focusTarget' => 'datum',
232 522 )
233 523 );
234 524 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
@@ -235,34 +525,57 @@
235 525 }
236 526 wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
237 527 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
238 528 }
529 +
530 + if ( isset( $_POST['chart-img'] ) && ! empty( $_POST['chart-img'] ) ) {
531 + $attachment_id = $this->save_chart_image( $_POST['chart-img'], $chart_id );
532 + if ( $attachment_id ) {
533 + update_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, $attachment_id );
534 + }
535 + }
536 + $lib = $this->load_chart_type( $chart_id );
537 +
538 + // the alpha color picker (RGBA) is not supported by google.
539 + $color_picker_dep = 'wp-color-picker';
540 + if ( in_array( $lib, array( 'chartjs', 'datatables' ), true ) && ! wp_script_is( 'wp-color-picker-alpha', 'registered' ) ) {
541 + wp_register_script( 'wp-color-picker-alpha', VISUALIZER_ABSURL . 'js/lib/wp-color-picker-alpha.min.js', array( 'wp-color-picker' ), Visualizer_Plugin::VERSION );
542 + $color_picker_dep = 'wp-color-picker-alpha';
543 + }
544 +
239 545 // enqueue and register scripts and styles
240 546 wp_register_script( 'visualizer-chosen', VISUALIZER_ABSURL . 'js/lib/chosen.jquery.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
241 547 wp_register_style( 'visualizer-chosen', VISUALIZER_ABSURL . 'css/lib/chosen.min.css', array(), Visualizer_Plugin::VERSION );
242 548
243 549 wp_register_style( 'visualizer-frame', VISUALIZER_ABSURL . 'css/frame.css', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION );
244 - wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION, true );
245 - wp_register_script( 'google-jsapi-new', '//www.gstatic.com/charts/loader.js', array(), null, true );
246 - wp_register_script( 'google-jsapi-old', '//www.google.com/jsapi', array( 'google-jsapi-new' ), null, true );
550 + wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen', 'jquery-ui-accordion', 'jquery-ui-tabs' ), Visualizer_Plugin::VERSION, true );
551 + wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
247 552 wp_register_script(
248 - 'visualizer-render', VISUALIZER_ABSURL . 'js/render.js', array(
249 - 'google-jsapi-old',
250 - 'google-jsapi-new',
251 - 'visualizer-frame',
252 - ), Visualizer_Plugin::VERSION, true
553 + 'visualizer-render',
554 + VISUALIZER_ABSURL . 'js/render-facade.js',
555 + apply_filters( 'visualizer_assets_render', array( 'visualizer-frame', 'visualizer-customization' ), false ),
556 + Visualizer_Plugin::VERSION,
557 + true
253 558 );
254 559 wp_register_script(
255 - 'visualizer-preview', VISUALIZER_ABSURL . 'js/preview.js', array(
256 - 'wp-color-picker',
560 + 'visualizer-preview',
561 + VISUALIZER_ABSURL . 'js/preview.js',
562 + array(
563 + $color_picker_dep,
257 564 'visualizer-render',
258 - ), Visualizer_Plugin::VERSION, true
565 + ),
566 + Visualizer_Plugin::VERSION,
567 + true
259 568 );
260 - // added by Ash/Upwork
261 - if ( VISUALIZER_PRO ) {
569 + wp_register_script( 'visualizer-editor-simple', VISUALIZER_ABSURL . 'js/simple-editor.js', array( 'jquery' ), Visualizer_Plugin::VERSION, true );
570 +
571 + do_action( 'visualizer_add_scripts' );
572 +
573 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
262 574 global $Visualizer_Pro;
263 575 $Visualizer_Pro->_addScriptsAndStyles();
264 576 }
577 +
265 578 // dispatch pages
266 579 $this->_chart = get_post( $chart_id );
267 580 $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
268 581
@@ -274,8 +587,9 @@
274 587 if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
275 588 // if the cancel button is clicked.
276 589 $this->undoRevisions( $chart_id, true );
277 590 } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
591 + $this->handlePermissions();
278 592 // if the save button is clicked.
279 593 $this->undoRevisions( $chart_id, false );
280 594 } else {
281 595 // if the edit button is clicked.
@@ -290,9 +604,9 @@
290 604 case 'visualizer': // fall through.
291 605 $this->_handleTypesPage();
292 606 break;
293 607 default:
294 - do_action( 'visualizer_pro_handle_tab', $tab, $this->_chart );
608 + // this should never happen.
295 609 break;
296 610 }
297 611 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
298 612 }
@@ -297,8 +611,80 @@
297 611 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
298 612 }
299 613
300 614 /**
615 + * Load code editor assets.
616 + */
617 + private function loadCodeEditorAssets( $chart_id ) {
618 + global $wp_version;
619 +
620 + $wp_scripts = wp_scripts();
621 +
622 + // data tables assets.
623 + wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
624 + wp_register_style( 'visualizer-datatables', VISUALIZER_ABSURL . 'css/lib/datatables.min.css', array(), Visualizer_Plugin::VERSION );
625 + wp_register_style( 'visualizer-jquery-ui', sprintf( '//ajax.googleapis.com/ajax/libs/jqueryui/%s/themes/smoothness/jquery-ui.css', $wp_scripts->registered['jquery-ui-core']->ver ), array( 'visualizer-datatables' ), Visualizer_Plugin::VERSION );
626 + wp_enqueue_script( 'visualizer-datatables' );
627 + wp_enqueue_style( 'visualizer-jquery-ui' );
628 +
629 + if ( ! Visualizer_Module::is_pro() ) {
630 + return;
631 + }
632 +
633 + $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping( $chart_id );
634 +
635 + if ( version_compare( $wp_version, '4.9.0', '<' ) ) {
636 + // code mirror assets.
637 + wp_register_script( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
638 + wp_register_script( 'visualizer-codemirror-placeholder', '//codemirror.net/addon/display/placeholder.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
639 + wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
640 + wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
641 + wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
642 + wp_register_script( 'visualizer-codemirror-sql-hint', '//codemirror.net/addon/hint/sql-hint.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
643 + wp_register_script( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.js', array( 'visualizer-codemirror-sql', 'visualizer-codemirror-sql-hint', 'visualizer-codemirror-placeholder', 'visualizer-codemirror-matchbrackets', 'visualizer-codemirror-closebrackets' ), Visualizer_Plugin::VERSION );
644 + wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
645 + wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
646 +
647 + wp_enqueue_script( 'visualizer-codemirror-hint' );
648 + wp_enqueue_style( 'visualizer-codemirror-hint' );
649 + } else {
650 + wp_enqueue_code_editor(
651 + array(
652 + 'type' => 'sql',
653 + 'codemirror' => array(
654 + 'autofocus' => true,
655 + 'lineWrapping' => true,
656 + 'dragDrop' => false,
657 + 'matchBrackets' => true,
658 + 'autoCloseBrackets' => true,
659 + 'extraKeys' => array( 'Ctrl-Space' => 'autocomplete' ),
660 + 'hintOptions' => array( 'tables' => $table_col_mapping ),
661 + ),
662 + )
663 + );
664 + }
665 +
666 + return $table_col_mapping;
667 + }
668 +
669 + /**
670 + * Handle permissions from the new conslidated settings sidebar.
671 + */
672 + private function handlePermissions() {
673 + if ( ! Visualizer_Module::is_pro() ) {
674 + return;
675 + }
676 +
677 + // we will not support old free and new pro.
678 + // handling new free and old pro.
679 + if ( has_action( 'visualizer_pro_handle_tab' ) ) {
680 + do_action( 'visualizer_pro_handle_tab', 'permissions', $this->_chart );
681 + } else {
682 + do_action( 'visualizer_handle_permissions', $this->_chart );
683 + }
684 + }
685 +
686 + /**
301 687 * Handle data and settings page
302 688 */
303 689 private function _handleDataAndSettingsPage() {
304 690 if ( isset( $_POST['map_api_key'] ) ) {
@@ -303,23 +689,47 @@
303 689 private function _handleDataAndSettingsPage() {
304 690 if ( isset( $_POST['map_api_key'] ) ) {
305 691 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
306 692 }
307 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
308 - if ( $this->_chart->post_status == 'auto-draft' ) {
693 +
694 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
695 + if ( $this->_chart->post_status === 'auto-draft' ) {
309 696 $this->_chart->post_status = 'publish';
697 +
698 + // 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.
699 + // we do not want any difference in data so disable revisions temporarily.
700 + $this->disableRevisionsTemporarily();
701 +
310 702 wp_update_post( $this->_chart->to_array() );
311 703 }
312 - update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
704 + // save meta data only when it is NOT being canceled.
705 + if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
706 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
707 +
708 + // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
709 + // this will help in searching with the chart id.
710 + $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
711 + $title = null;
712 + if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
713 + $title = $settings['title'];
714 + if ( is_array( $title ) ) {
715 + $title = $settings['title']['text'];
716 + }
717 + }
718 + if ( empty( $title ) ) {
719 + $title = $this->_chart->ID;
720 + }
721 + $settings['internal_title'] = $title;
722 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
723 + }
313 724 $render = new Visualizer_Render_Page_Send();
314 725 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
315 726 wp_iframe( array( $render, 'render' ) );
316 -
317 727 return;
318 728 }
319 729 $data = $this->_getChartArray();
320 730 $sidebar = '';
321 - $sidebar_class = 'Visualizer_Render_Sidebar_Type_' . ucfirst( $data['type'] );
731 + $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
322 732 if ( class_exists( $sidebar_class, true ) ) {
323 733 $sidebar = new $sidebar_class( $data['settings'] );
324 734 $sidebar->__series = $data['series'];
325 735 $sidebar->__data = $data['data'];
@@ -324,59 +734,107 @@
324 734 $sidebar->__series = $data['series'];
325 735 $sidebar->__data = $data['data'];
326 736 } else {
327 737 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
328 - if ( $sidebar != '' ) {
738 + if ( $sidebar !== '' ) {
329 739 $sidebar->__series = $data['series'];
330 740 $sidebar->__data = $data['data'];
331 741 }
332 742 }
333 - unset( $data['settings']['width'], $data['settings']['height'] );
334 - wp_enqueue_style( 'visualizer-frame' );
743 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
335 744 wp_enqueue_style( 'wp-color-picker' );
336 745 wp_enqueue_style( 'visualizer-frame' );
337 746 wp_enqueue_script( 'visualizer-preview' );
747 + wp_enqueue_script( 'visualizer-chosen' );
338 748 wp_enqueue_script( 'visualizer-render' );
749 +
750 + if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
751 + wp_enqueue_script( 'visualizer-editor-simple' );
752 + wp_localize_script(
753 + 'visualizer-editor-simple',
754 + 'visualizer1',
755 + array(
756 + 'ajax' => array(
757 + 'url' => admin_url( 'admin-ajax.php' ),
758 + 'nonces' => array(
759 + ),
760 + 'actions' => array(
761 + ),
762 + ),
763 + )
764 + );
765 + }
766 +
767 + $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
768 +
339 769 wp_localize_script(
340 - 'visualizer-render', 'visualizer', array(
770 + 'visualizer-render',
771 + 'visualizer',
772 + array(
341 773 'l10n' => array(
342 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
774 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
343 775 'loading' => esc_html__( 'Loading...', 'visualizer' ),
776 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
777 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
778 + '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' ),
344 779 ),
345 780 'charts' => array(
346 781 'canvas' => $data,
782 + 'id' => $this->_chart->ID,
347 783 ),
348 784 'language' => $this->get_language(),
349 785 'map_api_key' => get_option( 'visualizer-map-api-key' ),
350 - 'ajax' => array(
786 + 'ajax' => array(
351 787 'url' => admin_url( 'admin-ajax.php' ),
352 788 'nonces' => array(
353 789 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
790 + 'db_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ),
791 + 'json_get_roots' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION ),
792 + 'json_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION ),
793 + 'json_set_schedule' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION ),
354 794 ),
355 795 'actions' => array(
356 796 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
797 + 'db_get_data' => Visualizer_Plugin::ACTION_FETCH_DB_DATA,
798 + 'json_get_roots' => Visualizer_Plugin::ACTION_JSON_GET_ROOTS,
799 + 'json_get_data' => Visualizer_Plugin::ACTION_JSON_GET_DATA,
800 + 'json_set_schedule' => Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE,
357 801 ),
358 802 ),
803 + 'db_query' => array(
804 + 'tables' => $table_col_mapping,
805 + ),
806 + 'is_pro' => Visualizer_Module::is_pro(),
807 + 'page_type' => 'chart',
808 + 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
809 + 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
810 + 'is_front' => false,
359 811 )
360 812 );
813 +
361 814 $render = new Visualizer_Render_Page_Data();
362 815 $render->chart = $this->_chart;
363 816 $render->type = $data['type'];
817 + $render->custom_css = $data['css'];
364 818 $render->sidebar = $sidebar;
365 819 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
366 - $render->button = filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART
820 + $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
367 821 ? esc_html__( 'Save Chart', 'visualizer' )
368 822 : esc_html__( 'Create Chart', 'visualizer' );
369 - if ( filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART ) {
823 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
370 824 $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
371 825 }
372 826 } else {
373 827 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
374 828 }
375 - if ( VISUALIZER_PRO ) {
829 +
830 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
831 +
832 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
376 833 global $Visualizer_Pro;
377 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
834 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
378 835 }
836 +
379 837 $this->_addAction( 'admin_head', 'renderFlattrScript' );
380 838 wp_iframe( array( $render, 'render' ) );
381 839 }
382 840
@@ -388,21 +846,32 @@
388 846 * @access private
389 847 */
390 848 private function _handleTypesPage() {
391 849 // process post request
392 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
850 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
393 851 $type = filter_input( INPUT_POST, 'type' );
394 - if ( in_array( $type, Visualizer_Plugin::getChartTypes() ) ) {
852 + $library = filter_input( INPUT_POST, 'chart-library' );
853 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
854 + if ( empty( $library ) ) {
855 + // library cannot be empty.
856 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
857 + return;
858 + }
859 +
395 860 // save new chart type
396 861 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, $type );
862 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_LIBRARY, $library );
397 863 // if the chart has default data, update it with appropriate default data for new type
398 864 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
399 865 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
400 866 $source->fetch();
401 - $this->_chart->post_content = $source->getData();
867 + $this->_chart->post_content = $source->getData( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
402 868 wp_update_post( $this->_chart->to_array() );
403 869 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
404 870 }
871 +
872 + Visualizer_Module_Utility::set_defaults( $this->_chart );
873 +
405 874 // redirect to next tab
406 875 // changed by Ash/Upwork
407 876 wp_redirect( add_query_arg( 'tab', 'settings' ) );
408 877
@@ -410,9 +879,9 @@
410 879 }
411 880 }
412 881 $render = new Visualizer_Render_Page_Types();
413 882 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
414 - $render->types = Visualizer_Module_Admin::_getChartTypesLocalized();
883 + $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
415 884 $render->chart = $this->_chart;
416 885 wp_enqueue_style( 'visualizer-frame' );
417 886 wp_enqueue_script( 'visualizer-frame' );
418 887 wp_iframe( array( $render, 'render' ) );
@@ -428,10 +897,131 @@
428 897 */
429 898 public function renderFlattrScript() {
430 899 echo '';
431 900 }
432 - // changed by Ash/Upwork
901 +
433 902 /**
903 + * Processes the CSV that is sent in the request as a string.
904 + *
905 + * @since 3.2.0
906 + */
907 + private function handleCSVasString( $data, $editor_type ) {
908 + $source = null;
909 +
910 + switch ( $editor_type ) {
911 + case 'text':
912 + // data coming in from the text editor.
913 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
914 + $handle = fopen( $tmpfile, 'w' );
915 + $values = preg_split( '/[\n\r]+/', stripslashes( trim( $data ) ) );
916 + if ( $values ) {
917 + foreach ( $values as $row ) {
918 + if ( empty( $row ) ) {
919 + continue;
920 + }
921 + $row = explode( ',', $row );
922 + $row = array_map(
923 + function( $r ) {
924 + return '' === $r ? ' ' : $r;
925 + },
926 + $row
927 + );
928 + $row = implode( ',', $row );
929 + // don't use fpucsv here because we need to just dump the data
930 + // minus the empty rows
931 + // because fputcsv needs to tokenize
932 + // we can standardize the CSV enclosure here and replace all ' with "
933 + // we can assume that if there are an even number of ' they should be changed to "
934 + // but that will screw up words like fo'c'sle
935 + // so here let's just assume ' will NOT be used for enclosures
936 + fwrite( $handle, $row );
937 + fwrite( $handle, PHP_EOL );
938 + }
939 + }
940 + $source = new Visualizer_Source_Csv( $tmpfile );
941 + fclose( $handle );
942 + break;
943 + case 'table':
944 + // Import from Chart
945 + // fall-through.
946 + case 'excel':
947 + // data coming in from the excel editor.
948 + $source = apply_filters( 'visualizer_pro_handle_chart_data', $data, '' );
949 + break;
950 + }
951 + return $source;
952 + }
953 +
954 + /**
955 + * Parses the data uploaded as an HTML table.
956 + *
957 + * @since 3.2.0
958 + *
959 + * @access private
960 + */
961 + private function handleTabularData() {
962 + $csv = array();
963 + // the datatable mentions the headers twice, so lets remove the duplicates.
964 + $headers = array_unique( array_filter( $_POST['header'] ) );
965 + $types = $_POST['type'];
966 +
967 + // capture all the indexes that correspond to excluded columns.
968 + $exclude = array();
969 + $index = 0;
970 + foreach ( $types as $type ) {
971 + if ( empty( $type ) ) {
972 + $exclude[] = $index;
973 + }
974 + $index++;
975 + }
976 +
977 + // when N headers are being renamed, the number of headers increases by N
978 + // because of the way datatable duplicates header information
979 + // so unset the headers that have been renamed.
980 + if ( count( $headers ) !== count( $types ) ) {
981 + $to = count( $headers );
982 + for ( $i = count( $types ); $i < $to; $i++ ) {
983 + unset( $headers[ $i + 1 ] );
984 + }
985 + }
986 +
987 + $columns = array();
988 + for ( $i = 0; $i < count( $headers ); $i++ ) {
989 + if ( ! isset( $_POST[ 'data' . $i ] ) ) {
990 + continue;
991 + }
992 + $columns[ $i ] = $_POST[ 'data' . $i ];
993 + }
994 +
995 + $csv[] = $headers;
996 + $csv[] = $types;
997 + for ( $j = 0; $j < count( $columns[0] ); $j++ ) {
998 + $row = array();
999 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1000 + $row[] = sanitize_text_field( $columns[ $i ][ $j ] );
1001 + }
1002 + $csv[] = $row;
1003 + }
1004 +
1005 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1006 + $handle = fopen( $tmpfile, 'w' );
1007 +
1008 + if ( $csv ) {
1009 + $index = 0;
1010 + foreach ( $csv as $row ) {
1011 + // remove all the cells corresponding to the excluded headers.
1012 + foreach ( $exclude as $j ) {
1013 + unset( $row[ $j ] );
1014 + }
1015 + fputcsv( $handle, $row );
1016 + }
1017 + }
1018 + $source = new Visualizer_Source_Csv( $tmpfile );
1019 + fclose( $handle );
1020 + return $source;
1021 + }
1022 +
1023 + /**
434 1024 * Parses uploaded CSV file and saves new data for the chart.
435 1025 *
436 1026 * @since 1.0.0
437 1027 *
@@ -453,9 +1043,9 @@
453 1043
454 1044 // check chart, if chart exists
455 1045 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
456 1046 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
457 - if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
1047 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
458 1048 if ( ! $can_die ) {
459 1049 return;
460 1050 }
461 1051 status_header( 400 );
@@ -461,8 +1051,10 @@
461 1051 status_header( 400 );
462 1052 exit;
463 1053 }
464 1054
1055 + 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__ );
1056 +
465 1057 if ( ! isset( $_POST['vz-import-time'] ) ) {
466 1058 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
467 1059 }
468 1060
@@ -467,11 +1059,33 @@
467 1059 }
468 1060
469 1061 if ( ! isset( $_POST['chart_data_src'] ) || Visualizer_Plugin::CF_SOURCE_FILTER !== $_POST['chart_data_src'] ) {
470 1062 // delete the filters in case this chart is being uploaded from other data sources
471 - delete_post_meta( $chart_id, 'visualizer-filter-config' );
1063 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_FILTER_CONFIG );
1064 + delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_FILTER_CONFIG );
1065 + delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_DB_QUERY );
1066 +
1067 + // delete "import from db" specific parameters.
1068 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
1069 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1070 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
472 1071 }
473 1072
1073 + // delete json related data.
1074 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_URL );
1075 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_ROOT );
1076 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_PAGING );
1077 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_HEADERS );
1078 +
1079 + // delete last error
1080 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR );
1081 +
1082 + // delete editor related data.
1083 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1084 +
1085 + // delete this so that a JSON import can be later edited manually without a problem.
1086 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1087 +
474 1088 $source = null;
475 1089 $render = new Visualizer_Render_Page_Update();
476 1090 if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
477 1091 $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
@@ -477,27 +1091,68 @@
477 1091 $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
478 1092 if ( isset( $_POST['vz-import-time'] ) ) {
479 1093 apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
480 1094 }
1095 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
481 1096 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
482 1097 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
483 1098 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
484 - $source = apply_filters( 'visualizer_pro_handle_chart_data', $_POST['chart_data'], '' );
1099 + $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1100 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1101 + } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1102 + $source = $this->handleTabularData();
1103 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
485 1104 } else {
486 - $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please, try again.', 'visualizer' );
1105 + 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__ );
1106 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1107 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
487 1108 }
1109 +
1110 + 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__ );
1111 +
488 1112 if ( $source ) {
489 1113 if ( $source->fetch() ) {
490 - $chart->post_content = $source->getData();
1114 + $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
1115 + $populate = true;
1116 + if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
1117 + $json = unserialize( $content );
1118 + // if source exists, so should data. if source exists but data is blank, do not populate the chart.
1119 + // if we populate the data even if it is empty, the chart will show "Table has no columns".
1120 + if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
1121 + 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__ );
1122 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, sprintf( 'Not populating chart data as source exists (%s) but data is empty!', $json['source'] ) );
1123 + $populate = false;
1124 + }
1125 + }
1126 + if ( $populate ) {
1127 + $chart->post_content = $content;
1128 + }
491 1129 wp_update_post( $chart->to_array() );
492 1130 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
493 1131 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
494 1132 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
495 - $render->data = json_encode( $source->getRawData() );
1133 +
1134 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1135 +
1136 + Visualizer_Module_Utility::set_defaults( $chart, null );
1137 +
1138 + $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1139 +
1140 + $render->id = $chart->ID;
1141 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
496 1142 $render->series = json_encode( $source->getSeries() );
1143 + $render->settings = json_encode( $settings );
497 1144 } else {
498 - $render->message = esc_html__( 'CSV file is broken or invalid. Please, try again.', 'visualizer' );
1145 + $error = $source->get_error();
1146 + if ( empty( $error ) ) {
1147 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1148 + }
1149 + $render->message = $error;
1150 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1151 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
499 1152 }
1153 + } else {
1154 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
500 1155 }
501 1156 $render->render();
502 1157 if ( ! $can_die ) {
503 1158 return;
@@ -513,24 +1168,18 @@
513 1168 * @access public
514 1169 */
515 1170 public function cloneChart() {
516 1171 $chart_id = $success = false;
517 - $nonce = wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), Visualizer_Plugin::ACTION_CLONE_CHART );
1172 + $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
518 1173 $capable = current_user_can( 'edit_posts' );
519 1174 if ( $nonce && $capable ) {
520 - $chart_id = filter_input(
521 - INPUT_GET, 'chart', FILTER_VALIDATE_INT, array(
522 - 'options' => array(
523 - 'min_range' => 1,
524 - ),
525 - )
526 - );
1175 + $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
527 1176 if ( $chart_id ) {
528 1177 $chart = get_post( $chart_id );
529 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
1178 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
530 1179 }
531 1180 }
532 - $redirect = wp_get_referer();
1181 + $redirect = remove_query_arg( 'vaction', wp_get_referer() );
533 1182 if ( $success ) {
534 1183 $new_chart_id = wp_insert_post(
535 1184 array(
536 1185 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
@@ -539,22 +1188,32 @@
539 1188 'post_status' => $chart->post_status,
540 1189 'post_content' => $chart->post_content,
541 1190 )
542 1191 );
543 - if ( $new_chart_id && ! is_wp_error( $new_chart_id ) ) {
544 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_CHART_TYPE, get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true ) );
545 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, get_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, true ) );
546 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SOURCE, get_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, true ) );
547 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SERIES, get_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, true ) );
548 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SETTINGS, get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true ) );
1192 + if ( is_wp_error( $new_chart_id ) ) {
1193 + 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__ );
1194 + } else {
1195 + $post_meta = get_post_meta( $chart_id );
1196 + foreach ( $post_meta as $key => $value ) {
1197 + if ( strpos( $key, 'visualizer-' ) !== false ) {
1198 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1199 + }
1200 + }
549 1201 $redirect = add_query_arg(
550 1202 array(
551 1203 'page' => 'visualizer',
552 1204 'type' => filter_input( INPUT_GET, 'type' ),
553 - ), admin_url( 'upload.php' )
1205 + 'vaction' => false,
1206 + ),
1207 + admin_url( 'admin.php' )
554 1208 );
555 1209 }
556 1210 }
1211 +
1212 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1213 + wp_die();
1214 + }
1215 +
557 1216 wp_redirect( $redirect );
558 1217 exit;
559 1218 }
560 1219
@@ -569,9 +1228,11 @@
569 1228 check_ajax_referer( Visualizer_Plugin::ACTION_EXPORT_DATA . Visualizer_Plugin::VERSION, 'security' );
570 1229 $capable = current_user_can( 'edit_posts' );
571 1230 if ( $capable ) {
572 1231 $chart_id = isset( $_GET['chart'] ) ? filter_var(
573 - $_GET['chart'], FILTER_VALIDATE_INT, array(
1232 + $_GET['chart'],
1233 + FILTER_VALIDATE_INT,
1234 + array(
574 1235 'options' => array(
575 1236 'min_range' => 1,
576 1237 ),
577 1238 )
@@ -598,15 +1259,20 @@
598 1259 $data = $this->_getChartArray();
599 1260 $render = new Visualizer_Render_Page_Data();
600 1261 $render->chart = $this->_chart;
601 1262 $render->type = $data['type'];
602 - unset( $data['settings']['width'], $data['settings']['height'] );
1263 +
1264 + if ( $data && $data['settings'] ) {
1265 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1266 + }
603 1267 wp_enqueue_style( 'visualizer-frame' );
604 1268 wp_enqueue_script( 'visualizer-render' );
605 1269 wp_localize_script(
606 - 'visualizer-render', 'visualizer', array(
1270 + 'visualizer-render',
1271 + 'visualizer',
1272 + array(
607 1273 'l10n' => array(
608 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
1274 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
609 1275 'loading' => esc_html__( 'Loading...', 'visualizer' ),
610 1276 ),
611 1277 'charts' => array(
612 1278 'canvas' => $data,
@@ -612,15 +1278,201 @@
612 1278 'canvas' => $data,
613 1279 ),
614 1280 )
615 1281 );
616 - // Added by Ash/Upwork
617 - if ( VISUALIZER_PRO ) {
1282 +
1283 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1284 +
1285 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
618 1286 global $Visualizer_Pro;
619 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
1287 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
620 1288 }
1289 +
621 1290 // Added by Ash/Upwork
622 1291 $this->_addAction( 'admin_head', 'renderFlattrScript' );
623 1292 wp_iframe( array( $render, 'render' ) );
624 1293 }
625 1294
1295 + /**
1296 + * Returns the data for the query.
1297 + *
1298 + * @access public
1299 + */
1300 + public function getQueryData() {
1301 + check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1302 +
1303 + $params = wp_parse_args( $_POST['params'] );
1304 + $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1305 +
1306 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1307 + $html = $source->fetch( true );
1308 + $error = $source->get_error();
1309 + if ( ! empty( $error ) ) {
1310 + wp_send_json_error( array( 'msg' => $error ) );
1311 + }
1312 + wp_send_json_success( array( 'table' => $html ) );
1313 + }
1314 +
1315 + /**
1316 + * Saves the query and the schedule.
1317 + *
1318 + * @access public
1319 + */
1320 + public function saveQuery() {
1321 + check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1322 +
1323 + $chart_id = filter_input(
1324 + INPUT_GET,
1325 + 'chart',
1326 + FILTER_VALIDATE_INT,
1327 + array(
1328 + 'options' => array(
1329 + 'min_range' => 1,
1330 + ),
1331 + )
1332 + );
1333 +
1334 + $hours = filter_input(
1335 + INPUT_POST,
1336 + 'refresh',
1337 + FILTER_VALIDATE_INT,
1338 + array(
1339 + 'options' => array(
1340 + 'min_range' => -1,
1341 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1342 + ),
1343 + )
1344 + );
1345 +
1346 + if ( ! is_int( $hours ) ) {
1347 + $hours = -1;
1348 + }
1349 +
1350 + $render = new Visualizer_Render_Page_Update();
1351 + if ( $chart_id ) {
1352 + $params = wp_parse_args( $_POST['params'] );
1353 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
1354 + $source->fetch( false );
1355 + $error = $source->get_error();
1356 + if ( empty( $error ) ) {
1357 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
1358 + update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1359 + update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1360 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1361 + update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1362 + if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1363 + $remote_db_params = $params;
1364 + unset( $remote_db_params['query'] );
1365 + unset( $remote_db_params['chart_id'] );
1366 + update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1367 + }
1368 +
1369 + $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
1370 + $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
1371 + update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
1372 +
1373 + wp_update_post(
1374 + array(
1375 + 'ID' => $chart_id,
1376 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
1377 + )
1378 + );
1379 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1380 + $render->series = json_encode( $source->getSeries() );
1381 + $render->id = $chart_id;
1382 + } else {
1383 + $render->message = $error;
1384 + }
1385 + }
1386 + $render->render();
1387 + if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1388 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1389 + }
1390 + }
1391 +
1392 +
1393 + /**
1394 + * Saves the filter query and the schedule.
1395 + *
1396 + * @access public
1397 + */
1398 + public function saveFilter() {
1399 + check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1400 +
1401 + $chart_id = filter_input(
1402 + INPUT_GET,
1403 + 'chart',
1404 + FILTER_VALIDATE_INT,
1405 + array(
1406 + 'options' => array(
1407 + 'min_range' => 1,
1408 + ),
1409 + )
1410 + );
1411 +
1412 + $hours = filter_input(
1413 + INPUT_POST,
1414 + 'refresh',
1415 + FILTER_VALIDATE_INT,
1416 + array(
1417 + 'options' => array(
1418 + 'min_range' => -1,
1419 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1420 + ),
1421 + )
1422 + );
1423 +
1424 + if ( 0 !== $hours && empty( $hours ) ) {
1425 + $hours = -1;
1426 + }
1427 +
1428 + do_action( 'visualizer_save_filter', $chart_id, $hours );
1429 +
1430 + if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1431 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1432 + }
1433 + }
1434 +
1435 + /**
1436 + * Save chart image.
1437 + *
1438 + * @param string $base64_img Chart image.
1439 + * @param int $chart_id Chart ID.
1440 + * @return attachment ID
1441 + */
1442 + public function save_chart_image( $base64_img, $chart_id ) {
1443 + // Upload dir.
1444 + $upload_dir = wp_upload_dir();
1445 + $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1446 +
1447 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1448 + $img = str_replace( ' ', '+', $img );
1449 + $decoded = base64_decode( $img );
1450 + $filename = 'visualization-' . $chart_id . '.png';
1451 + $file_type = 'image/png';
1452 + $hashed_filename = $filename;
1453 +
1454 + // Delete old chart image.
1455 + $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
1456 + if ( $old_attachment_id ) {
1457 + wp_delete_attachment( $old_attachment_id, true );
1458 + }
1459 +
1460 + // Save the image in the uploads directory.
1461 + require_once ABSPATH . '/wp-admin/includes/file.php';
1462 + \WP_Filesystem();
1463 + global $wp_filesystem;
1464 + $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1465 +
1466 + // Insert new chart image.
1467 + $attachment = array(
1468 + 'post_mime_type' => $file_type,
1469 + 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1470 + 'post_content' => '',
1471 + 'post_status' => 'inherit',
1472 + 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1473 + );
1474 +
1475 + $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1476 + return $attach_id;
1477 + }
626 1478 }