PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.11.15
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.11.15
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 3.10.4 All 148 releases
visualizer / classes / Visualizer / Module / Chart.php

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

1,647 lines 57.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // +----------------------------------------------------------------------+
3 // | Copyright 2013 Madpixels (email : visualizer@madpixels.net) |
4 // +----------------------------------------------------------------------+
5 // | This program is free software; you can redistribute it and/or modify |
6 // | it under the terms of the GNU General Public License, version 2, as |
7 // | published by the Free Software Foundation. |
8 // | |
9 // | This program is distributed in the hope that it will be useful, |
10 // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 // | GNU General Public License for more details. |
13 // | |
14 // | You should have received a copy of the GNU General Public License |
15 // | along with this program; if not, write to the Free Software |
16 // | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
17 // | MA 02110-1301 USA |
18 // +----------------------------------------------------------------------+
19 // | Author: Eugene Manuilov <eugene@manuilov.org> |
20 // +----------------------------------------------------------------------+
21 /**
22 * The module for all stuff related to getting, editing, creating and deleting charts.
23 *
24 * @category Visualizer
25 * @package Module
26 *
27 * @since 1.0.0
28 */
29 class Visualizer_Module_Chart extends Visualizer_Module {
30
31 const NAME = __CLASS__;
32
33 /**
34 * The chart object.
35 *
36 * @since 1.0.0
37 *
38 * @access private
39 * @var WP_Post
40 */
41 private $_chart;
42
43 /**
44 * Constructor.
45 *
46 * @since 1.0.0
47 *
48 * @access public
49 *
50 * @param Visualizer_Plugin $plugin The instance of the plugin.
51 */
52 public function __construct( Visualizer_Plugin $plugin ) {
53 parent::__construct( $plugin );
54 $this->_addAjaxAction( Visualizer_Plugin::ACTION_GET_CHARTS, 'getCharts' );
55 $this->_addAjaxAction( Visualizer_Plugin::ACTION_DELETE_CHART, 'deleteChart' );
56 $this->_addAjaxAction( Visualizer_Plugin::ACTION_CREATE_CHART, 'renderChartPages' );
57 $this->_addAjaxAction( Visualizer_Plugin::ACTION_EDIT_CHART, 'renderChartPages' );
58 $this->_addAjaxAction( Visualizer_Plugin::ACTION_UPLOAD_DATA, 'uploadData' );
59 $this->_addAjaxAction( Visualizer_Plugin::ACTION_CLONE_CHART, 'cloneChart' );
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
74 }
75
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 /**
305 * Fetches charts from database.
306 *
307 * This method is also called from the media pop-up (classic editor: create a post and add chart from insert content).
308 *
309 * @since 1.0.0
310 *
311 * @access public
312 */
313 public function getCharts() {
314 $query_args = array(
315 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
316 'posts_per_page' => 9,
317 'paged' => filter_input(
318 INPUT_GET,
319 'page',
320 FILTER_VALIDATE_INT,
321 array(
322 'options' => array(
323 'min_range' => 1,
324 'default' => 1,
325 ),
326 )
327 ),
328 );
329 $filter = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );
330 if ( empty( $filter ) ) {
331 // 'filter' is from the modal from the add media button.
332 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
333 }
334
335 if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes(), true ) ) {
336 $query_args['meta_query'] = array(
337 array(
338 'key' => Visualizer_Plugin::CF_CHART_TYPE,
339 'value' => $filter,
340 'compare' => '=',
341 ),
342 );
343 }
344 $query = new WP_Query( $query_args );
345 $charts = array();
346 while ( $query->have_posts() ) {
347 $chart = $query->next_post();
348 $chart_data = $this->_getChartArray( $chart );
349 $chart_data['id'] = $chart->ID;
350 $chart_data['library'] = $this->load_chart_type( $chart->ID );
351 $css = '';
352 $settings = $chart_data['settings'];
353 $arguments = $this->get_inline_custom_css( 'visualizer-chart-' . $chart->ID, $settings );
354 if ( ! empty( $arguments ) ) {
355 $css = $arguments[0];
356 $settings = $arguments[1];
357 }
358 $chart_data['settings'] = $settings;
359 $chart_data['css'] = $css;
360 $charts[] = $chart_data;
361 }
362 self::_sendResponse(
363 array(
364 'success' => true,
365 'data' => $charts,
366 'total' => $query->max_num_pages,
367 )
368 );
369 }
370
371 /**
372 * Returns chart data required for rendering.
373 *
374 * @since 1.0.0
375 *
376 * @access private
377 *
378 * @param WP_Post|null $chart The chart object.
379 *
380 * @return array The array of chart data.
381 */
382 private function _getChartArray( $chart = null ) {
383 if ( is_null( $chart ) ) {
384 $chart = $this->_chart;
385 }
386 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
387 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
388 $data = self::get_chart_data( $chart, $type );
389 $library = $this->load_chart_type( $chart->ID );
390
391 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
392 $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
393 if ( ! empty( $atts['settings'] ) ) {
394 $settings = apply_filters( $atts['settings'], $settings, $chart->ID, $type );
395 }
396
397 $css = '';
398 $arguments = $this->get_inline_custom_css( 'visualizer-' . $chart->ID, $settings );
399 if ( ! empty( $arguments ) ) {
400 $css = $arguments[0];
401 $settings = $arguments[1];
402 }
403
404 $date_formats = Visualizer_Source::get_date_formats_if_exists( $series, $data );
405
406 return array(
407 'type' => $type,
408 'series' => $series,
409 'settings' => $settings,
410 'data' => $data,
411 'library' => $library,
412 'css' => $css,
413 'date_formats' => $date_formats,
414 );
415 }
416
417 /**
418 * Sends json response.
419 *
420 * @since 1.0.0
421 *
422 * @access private
423 *
424 * @param array $results The response array.
425 */
426 public static function _sendResponse( $results ) {
427 header( 'Content-type: application/json' );
428 nocache_headers();
429 echo json_encode( $results );
430 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
431 }
432
433 /**
434 * Deletes a chart from database.
435 *
436 * @since 1.0.0
437 * @uses wp_delete_post() To delete a chart.
438 *
439 * @access public
440 */
441 public function deleteChart() {
442 $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
443 $input_method = $is_post ? INPUT_POST : INPUT_GET;
444 $chart_id = $success = false;
445 $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
446 $capable = current_user_can( 'delete_posts' );
447 if ( $nonce && $capable ) {
448 $chart_id = filter_input(
449 $input_method,
450 'chart',
451 FILTER_VALIDATE_INT,
452 array(
453 'options' => array(
454 'min_range' => 1,
455 ),
456 )
457 );
458 if ( $chart_id ) {
459 $chart = get_post( $chart_id );
460 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
461 }
462 }
463 if ( $success ) {
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 }
478 }
479 if ( $is_post ) {
480 self::_sendResponse(
481 array(
482 'success' => $success,
483 )
484 );
485 }
486 wp_redirect( remove_query_arg( 'vaction', wp_get_referer() ) );
487 exit;
488 }
489
490 /**
491 * Delete charts that are still in auto-draft mode.
492 */
493 private function deleteOldCharts() {
494 $query = new WP_Query(
495 array(
496 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
497 'post_status' => 'auto-draft',
498 'fields' => 'ids',
499 'update_post_meta_cache' => false,
500 'update_post_term_cache' => false,
501 'posts_per_page' => 50,
502 'date_query' => array(
503 array(
504 'before' => 'today',
505 ),
506 ),
507 )
508 );
509
510 if ( $query->have_posts() ) {
511 $ids = array();
512 while ( $query->have_posts() ) {
513 wp_delete_post( $query->next_post(), true );
514 }
515 }
516 }
517
518 /**
519 * Renders appropriate page for chart builder. Creates new auto draft chart
520 * if no chart has been specified.
521 *
522 * @since 1.0.0
523 *
524 * @access public
525 */
526 public function renderChartPages() {
527 if ( ! current_user_can( 'edit_posts' ) ) {
528 wp_die( __( 'You do not have permission to access this page.', 'visualizer' ) );
529 }
530
531 defined( 'IFRAME_REQUEST' ) || define( 'IFRAME_REQUEST', 1 );
532 if ( ! defined( 'ET_BUILDER_PRODUCT_VERSION' ) && function_exists( 'et_get_theme_version' ) ) {
533 define( 'ET_BUILDER_PRODUCT_VERSION', et_get_theme_version() );
534 }
535 // Set current screen for the render chart.
536 set_current_screen( 'visualizer_render_chart' );
537 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
538 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
539 if ( ! empty( $_POST ) ) {
540 $_POST = map_deep( $_POST, 'wp_strip_all_tags' );
541 }
542 if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
543 if ( empty( $_GET['lang'] ) || empty( $_GET['parent_chart_id'] ) ) {
544 $this->deleteOldCharts();
545 $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
546 $chart_status = Visualizer_Module_Admin::checkChartStatus( $default_type );
547 if ( ! $chart_status ) {
548 $default_type = 'line';
549 }
550 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
551 $source->fetch();
552 $chart_id = wp_insert_post(
553 array(
554 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
555 'post_title' => 'Visualization',
556 'post_author' => get_current_user_id(),
557 'post_status' => 'auto-draft',
558 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
559 )
560 );
561 if ( $chart_id && ! is_wp_error( $chart_id ) ) {
562 add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
563 add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
564 add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
565 add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
566 add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
567 add_post_meta(
568 $chart_id,
569 Visualizer_Plugin::CF_SETTINGS,
570 array(
571 'focusTarget' => 'datum',
572 )
573 );
574
575 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
576 }
577 } else {
578 $parent_chart_id = isset( $_GET['parent_chart_id'] ) ? filter_var( $_GET['parent_chart_id'], FILTER_VALIDATE_INT ) : '';
579 $success = false;
580 if ( $parent_chart_id ) {
581 $parent_chart = get_post( $parent_chart_id );
582 $success = $parent_chart && $parent_chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
583 }
584 if ( $success ) {
585 $new_chart_id = wp_insert_post(
586 array(
587 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
588 'post_title' => 'Visualization',
589 'post_author' => get_current_user_id(),
590 'post_status' => $parent_chart->post_status,
591 'post_content' => $parent_chart->post_content,
592 )
593 );
594
595 if ( is_wp_error( $new_chart_id ) ) {
596 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__ );
597 } else {
598 $post_meta = get_post_meta( $parent_chart_id );
599 $chart_id = $new_chart_id;
600 foreach ( $post_meta as $key => $value ) {
601 if ( strpos( $key, 'visualizer-' ) !== false ) {
602 add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
603 }
604 }
605 }
606 }
607 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
608 }
609 wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
610
611 if ( defined( 'WP_TESTS_DOMAIN' ) ) {
612 wp_die();
613 }
614 exit();
615 }
616
617 $_POST['save_chart_image'] = isset( $_POST['save_chart_image'] ) && 'yes' === $_POST['save_chart_image'] ? true : false;
618 $_POST['lazy_load_chart'] = isset( $_POST['lazy_load_chart'] ) && 'yes' === $_POST['lazy_load_chart'] ? true : false;
619
620 if ( isset( $_POST['chart-img'] ) && ! empty( $_POST['chart-img'] ) ) {
621 $attachment_id = $this->save_chart_image( $_POST['chart-img'], $chart_id, $_POST['save_chart_image'] );
622 if ( $attachment_id ) {
623 update_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, $attachment_id );
624 }
625 }
626 $lib = $this->load_chart_type( $chart_id );
627
628 // the alpha color picker (RGBA) is not supported by google.
629 $color_picker_dep = 'wp-color-picker';
630 if ( in_array( $lib, array( 'chartjs', 'datatables' ), true ) && ! wp_script_is( 'wp-color-picker-alpha', 'registered' ) ) {
631 wp_register_script( 'wp-color-picker-alpha', VISUALIZER_ABSURL . 'js/lib/wp-color-picker-alpha.min.js', array( 'wp-color-picker' ), Visualizer_Plugin::VERSION );
632 $color_picker_dep = 'wp-color-picker-alpha';
633 }
634
635 // enqueue and register scripts and styles
636 wp_register_script( 'visualizer-chosen', VISUALIZER_ABSURL . 'js/lib/chosen.jquery.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
637 wp_register_style( 'visualizer-chosen', VISUALIZER_ABSURL . 'css/lib/chosen.min.css', array(), Visualizer_Plugin::VERSION );
638
639 wp_register_style( 'visualizer-frame', VISUALIZER_ABSURL . 'css/frame.css', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION );
640 wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen', 'jquery-ui-accordion', 'jquery-ui-tabs' ), Visualizer_Plugin::VERSION, true );
641 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
642 wp_register_script(
643 'visualizer-render',
644 VISUALIZER_ABSURL . 'js/render-facade.js',
645 apply_filters( 'visualizer_assets_render', array( 'visualizer-frame', 'visualizer-customization' ), false ),
646 Visualizer_Plugin::VERSION,
647 true
648 );
649 wp_register_script(
650 'visualizer-preview',
651 VISUALIZER_ABSURL . 'js/preview.js',
652 array(
653 $color_picker_dep,
654 'visualizer-render',
655 ),
656 Visualizer_Plugin::VERSION,
657 true
658 );
659 wp_register_script( 'visualizer-editor-simple', VISUALIZER_ABSURL . 'js/simple-editor.js', array( 'jquery' ), Visualizer_Plugin::VERSION, true );
660
661 do_action( 'visualizer_add_scripts' );
662
663 if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
664 global $Visualizer_Pro;
665 $Visualizer_Pro->_addScriptsAndStyles();
666 }
667
668 // dispatch pages
669 $this->_chart = get_post( $chart_id );
670 $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
671
672 // skip chart type pages only for existing charts.
673 if ( VISUALIZER_SKIP_CHART_TYPE_PAGE && 'auto-draft' !== $this->_chart->post_status && ( ! empty( $_GET['tab'] ) && 'visualizer' === $_GET['tab'] ) ) {
674 $tab = 'settings';
675 }
676
677 if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
678 // if the cancel button is clicked.
679 $this->undoRevisions( $chart_id, true );
680 } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
681 $this->handlePermissions();
682 // if the save button is clicked.
683 $this->undoRevisions( $chart_id, false );
684 } else {
685 // if the edit button is clicked.
686 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
687 }
688
689 // Clear existing chart cache.
690 if ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
691 $cache_key = Visualizer_Plugin::CF_CHART_CACHE . '_' . $chart_id;
692 if ( get_transient( $cache_key ) ) {
693 delete_transient( $cache_key );
694 }
695 }
696
697 switch ( $tab ) {
698 case 'settings':
699 $this->_handleDataAndSettingsPage();
700 break;
701 case 'type': // fall through.
702 case 'visualizer': // fall through.
703 $this->_handleTypesPage();
704 break;
705 default:
706 // this should never happen.
707 break;
708 }
709 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
710 }
711
712 /**
713 * Load code editor assets.
714 */
715 private function loadCodeEditorAssets( $chart_id ) {
716 global $wp_version;
717
718 $wp_scripts = wp_scripts();
719
720 // data tables assets.
721 wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
722 wp_register_style( 'visualizer-datatables', VISUALIZER_ABSURL . 'css/lib/datatables.min.css', array(), Visualizer_Plugin::VERSION );
723 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 );
724 wp_enqueue_script( 'visualizer-datatables' );
725 wp_enqueue_style( 'visualizer-jquery-ui' );
726
727 if ( ! Visualizer_Module::is_pro() ) {
728 return;
729 }
730
731 $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping( $chart_id );
732
733 if ( version_compare( $wp_version, '4.9.0', '<' ) ) {
734 // code mirror assets.
735 wp_register_script( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
736 wp_register_script( 'visualizer-codemirror-placeholder', '//codemirror.net/addon/display/placeholder.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
737 wp_register_script( 'visualizer-codemirror-matchbrackets', '//codemirror.net/addon/edit/matchbrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
738 wp_register_script( 'visualizer-codemirror-closebrackets', '//codemirror.net/addon/edit/closebrackets.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
739 wp_register_script( 'visualizer-codemirror-sql', '//codemirror.net/mode/sql/sql.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
740 wp_register_script( 'visualizer-codemirror-sql-hint', '//codemirror.net/addon/hint/sql-hint.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
741 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 );
742 wp_register_style( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.css', array(), Visualizer_Plugin::VERSION );
743 wp_register_style( 'visualizer-codemirror-hint', '//codemirror.net/addon/hint/show-hint.css', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
744
745 wp_enqueue_script( 'visualizer-codemirror-hint' );
746 wp_enqueue_style( 'visualizer-codemirror-hint' );
747 } else {
748 wp_enqueue_code_editor(
749 array(
750 'type' => 'sql',
751 'codemirror' => array(
752 'autofocus' => true,
753 'lineWrapping' => true,
754 'dragDrop' => false,
755 'matchBrackets' => true,
756 'autoCloseBrackets' => true,
757 'extraKeys' => array( 'Shift-Space' => 'autocomplete' ),
758 'hintOptions' => array( 'tables' => $table_col_mapping ),
759 ),
760 )
761 );
762 }
763
764 return $table_col_mapping;
765 }
766
767 /**
768 * Handle permissions from the new conslidated settings sidebar.
769 */
770 private function handlePermissions() {
771 if ( ! Visualizer_Module::is_pro() ) {
772 return;
773 }
774
775 // we will not support old free and new pro.
776 // handling new free and old pro.
777 if ( has_action( 'visualizer_pro_handle_tab' ) ) {
778 do_action( 'visualizer_pro_handle_tab', 'permissions', $this->_chart );
779 } else {
780 do_action( 'visualizer_handle_permissions', $this->_chart );
781 }
782 }
783
784 /**
785 * Handle data and settings page
786 */
787 private function _handleDataAndSettingsPage() {
788 if ( isset( $_POST['map_api_key'] ) ) {
789 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
790 }
791
792 if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
793 $is_canceled = isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] );
794 $is_newly_created = $this->_chart->post_status === 'auto-draft';
795
796 if ( $is_newly_created && ! $is_canceled ) {
797 $this->_chart->post_status = 'publish';
798
799 // 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.
800 // we do not want any difference in data so disable revisions temporarily.
801 $this->disableRevisionsTemporarily();
802
803 wp_update_post( $this->_chart->to_array() );
804 }
805 // save meta data only when it is NOT being canceled.
806 if ( ! $is_canceled ) {
807 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
808
809 // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
810 // this will help in searching with the chart id.
811 $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
812 $title = null;
813 if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
814 $title = $settings['title'];
815 if ( is_array( $title ) ) {
816 $title = $settings['title']['text'];
817 }
818 }
819 if ( empty( $title ) ) {
820 $title = $this->_chart->ID;
821 }
822 $settings['internal_title'] = $title;
823 $settings_label = isset( $settings['pieResidueSliceLabel'] ) ? $settings['pieResidueSliceLabel'] : '';
824 if ( empty( $settings_label ) ) {
825 $settings['pieResidueSliceLabel'] = esc_html__( 'Other', 'visualizer' );
826 } else {
827 $settings['pieResidueSliceLabel'] = $settings_label;
828 }
829 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
830 }
831 $render = new Visualizer_Render_Page_Send();
832 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
833 wp_iframe( array( $render, 'render' ) );
834 return;
835 }
836 $data = $this->_getChartArray();
837 $sidebar = '';
838 $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
839 if ( class_exists( $sidebar_class, true ) ) {
840 $sidebar = new $sidebar_class( $data['settings'] );
841 $sidebar->__series = $data['series'];
842 $sidebar->__data = $data['data'];
843 } else {
844 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
845 if ( $sidebar !== '' ) {
846 $sidebar->__series = $data['series'];
847 $sidebar->__data = $data['data'];
848 }
849 }
850 unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
851 wp_enqueue_style( 'wp-color-picker' );
852 wp_enqueue_style( 'visualizer-frame' );
853 wp_enqueue_script( 'visualizer-preview' );
854 wp_enqueue_script( 'visualizer-chosen' );
855 wp_enqueue_script( 'visualizer-render' );
856
857 if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
858 wp_enqueue_script( 'visualizer-editor-simple' );
859 wp_localize_script(
860 'visualizer-editor-simple',
861 'visualizer1',
862 array(
863 'ajax' => array(
864 'url' => admin_url( 'admin-ajax.php' ),
865 'nonces' => array(
866 ),
867 'actions' => array(
868 ),
869 ),
870 )
871 );
872 }
873
874 $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
875
876 wp_localize_script(
877 'visualizer-render',
878 'visualizer',
879 array(
880 'l10n' => array(
881 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
882 'loading' => esc_html__( 'Loading...', 'visualizer' ),
883 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
884 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
885 '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' ),
886 'copied' => __( 'The data has been copied to your clipboard. Hit Ctrl-V/Cmd-V in your spreadsheet editor to paste the data.', 'visualizer' ),
887 ),
888 'charts' => array(
889 'canvas' => $data,
890 'id' => $this->_chart->ID,
891 ),
892 'language' => $this->get_language(),
893 'map_api_key' => get_option( 'visualizer-map-api-key' ),
894 'ajax' => array(
895 'url' => admin_url( 'admin-ajax.php' ),
896 'nonces' => array(
897 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
898 'db_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ),
899 'json_get_roots' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION ),
900 'json_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION ),
901 'json_set_schedule' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION ),
902 ),
903 'actions' => array(
904 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
905 'db_get_data' => Visualizer_Plugin::ACTION_FETCH_DB_DATA,
906 'json_get_roots' => Visualizer_Plugin::ACTION_JSON_GET_ROOTS,
907 'json_get_data' => Visualizer_Plugin::ACTION_JSON_GET_DATA,
908 'json_set_schedule' => Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE,
909 ),
910 ),
911 'db_query' => array(
912 'tables' => $table_col_mapping,
913 ),
914 'is_pro' => Visualizer_Module::is_pro(),
915 'page_type' => 'chart',
916 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
917 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
918 'is_front' => false,
919 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
920 )
921 );
922
923 $render = new Visualizer_Render_Page_Data();
924 $render->chart = $this->_chart;
925 $render->type = $data['type'];
926 $render->custom_css = $data['css'];
927 $render->sidebar = $sidebar;
928 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
929 $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
930 ? esc_html__( 'Save Chart', 'visualizer' )
931 : esc_html__( 'Create Chart', 'visualizer' );
932
933 $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
934 } else {
935 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
936 }
937
938 do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
939
940 if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
941 global $Visualizer_Pro;
942 $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
943 }
944
945 $this->_addAction( 'admin_head', 'renderFlattrScript' );
946 wp_iframe( array( $render, 'render' ) );
947 }
948
949 /**
950 * Handles chart type selection page.
951 *
952 * @since 1.0.0
953 *
954 * @access private
955 */
956 private function _handleTypesPage() {
957 // process post request
958 if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ), 'visualizer-upload-data' ) ) {
959 $type = filter_input( INPUT_POST, 'type' );
960 $library = filter_input( INPUT_POST, 'chart-library' );
961 if ( Visualizer_Module_Admin::checkChartStatus( $type ) ) {
962 if ( empty( $library ) ) {
963 // library cannot be empty.
964 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
965 return;
966 }
967
968 // save new chart type
969 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, $type );
970 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_LIBRARY, $library );
971 // if the chart has default data, update it with appropriate default data for new type
972 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
973 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
974 $source->fetch();
975 $this->_chart->post_content = $source->getData( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
976 wp_update_post( $this->_chart->to_array() );
977 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
978 }
979
980 Visualizer_Module_Utility::set_defaults( $this->_chart );
981
982 // redirect to next tab
983 // changed by Ash/Upwork
984 wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
985
986 return;
987 }
988 }
989 $render = new Visualizer_Render_Page_Types();
990 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
991 $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
992 $render->chart = $this->_chart;
993 wp_enqueue_style( 'visualizer-frame' );
994 wp_enqueue_script( 'visualizer-frame' );
995 wp_iframe( array( $render, 'render' ) );
996 }
997
998 /**
999 * Renders flattr script in the iframe <head>
1000 *
1001 * @since 1.4.2
1002 * @action admin_head
1003 *
1004 * @access public
1005 */
1006 public function renderFlattrScript() {
1007 echo '';
1008 }
1009
1010 /**
1011 * Processes the CSV that is sent in the request as a string.
1012 *
1013 * @since 3.2.0
1014 */
1015 private function handleCSVasString( $data, $editor_type ) {
1016 $source = null;
1017
1018 switch ( $editor_type ) {
1019 case 'text':
1020 // data coming in from the text editor.
1021 $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1022 $handle = fopen( $tmpfile, 'w' );
1023 $values = preg_split( '/[\n\r]+/', stripslashes( trim( $data ) ) );
1024 if ( $values ) {
1025 foreach ( $values as $row ) {
1026 if ( empty( $row ) ) {
1027 continue;
1028 }
1029 $row = explode( ',', $row );
1030 $row = array_map(
1031 function( $r ) {
1032 return '' === $r ? ' ' : $r;
1033 },
1034 $row
1035 );
1036 $row = implode( ',', $row );
1037 // don't use fpucsv here because we need to just dump the data
1038 // minus the empty rows
1039 // because fputcsv needs to tokenize
1040 // we can standardize the CSV enclosure here and replace all ' with "
1041 // we can assume that if there are an even number of ' they should be changed to "
1042 // but that will screw up words like fo'c'sle
1043 // so here let's just assume ' will NOT be used for enclosures
1044 fwrite( $handle, $row );
1045 fwrite( $handle, PHP_EOL );
1046 }
1047 }
1048 $source = new Visualizer_Source_Csv( $tmpfile );
1049 fclose( $handle );
1050 break;
1051 case 'table':
1052 // Import from Chart
1053 // fall-through.
1054 case 'excel':
1055 // data coming in from the excel editor.
1056 $source = apply_filters( 'visualizer_pro_handle_chart_data', $data, '' );
1057 break;
1058 }
1059 return $source;
1060 }
1061
1062 /**
1063 * Parses the data uploaded as an HTML table.
1064 *
1065 * @since 3.2.0
1066 *
1067 * @access private
1068 */
1069 private function handleTabularData() {
1070 $csv = array();
1071 // the datatable mentions the headers twice, so lets remove the duplicates.
1072 $headers = array_unique( array_filter( $_POST['header'] ) );
1073 $types = $_POST['type'];
1074
1075 // capture all the indexes that correspond to excluded columns.
1076 $exclude = array();
1077 $index = 0;
1078 foreach ( $types as $type ) {
1079 if ( empty( $type ) ) {
1080 $exclude[] = $index;
1081 }
1082 $index++;
1083 }
1084
1085 // when N headers are being renamed, the number of headers increases by N
1086 // because of the way datatable duplicates header information
1087 // so unset the headers that have been renamed.
1088 if ( count( $headers ) !== count( $types ) ) {
1089 $to = count( $headers );
1090 for ( $i = count( $types ); $i < $to; $i++ ) {
1091 unset( $headers[ $i + 1 ] );
1092 }
1093 }
1094
1095 $columns = array();
1096 for ( $i = 0; $i < count( $headers ); $i++ ) {
1097 if ( ! isset( $_POST[ 'data' . $i ] ) ) {
1098 continue;
1099 }
1100 $columns[ $i ] = $_POST[ 'data' . $i ];
1101 }
1102
1103 $csv[] = $headers;
1104 $csv[] = $types;
1105 for ( $j = 0; $j < count( $columns[0] ); $j++ ) {
1106 $row = array();
1107 for ( $i = 0; $i < count( $headers ); $i++ ) {
1108 $row[] = sanitize_text_field( $columns[ $i ][ $j ] );
1109 }
1110 $csv[] = $row;
1111 }
1112
1113 $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1114 $handle = fopen( $tmpfile, 'w' );
1115
1116 if ( $csv ) {
1117 $index = 0;
1118 foreach ( $csv as $row ) {
1119 // remove all the cells corresponding to the excluded headers.
1120 foreach ( $exclude as $j ) {
1121 unset( $row[ $j ] );
1122 }
1123 fputcsv( $handle, $row );
1124 }
1125 }
1126 $source = new Visualizer_Source_Csv( $tmpfile );
1127 fclose( $handle );
1128 return $source;
1129 }
1130
1131 /**
1132 * Parses uploaded CSV file and saves new data for the chart.
1133 *
1134 * @since 1.0.0
1135 *
1136 * @access public
1137 */
1138 public function uploadData() {
1139 // if this is being called internally from pro and VISUALIZER_DO_NOT_DIE is set.
1140 // otherwise, assume this is a normal web request.
1141 $can_die = ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE );
1142
1143 // validate nonce
1144 if (
1145 ! isset( $_GET['nonce'] ) ||
1146 ! wp_verify_nonce( $_GET['nonce'], 'visualizer-upload-data' ) ||
1147 ! current_user_can( 'edit_posts' )
1148 ) {
1149 if ( ! $can_die ) {
1150 return;
1151 }
1152 status_header( 403 );
1153 exit;
1154 }
1155
1156 // check chart, if chart exists
1157 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
1158 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1159 if (
1160 ! $chart_id ||
1161 ! ( $chart = get_post( $chart_id ) ) ||
1162 $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ||
1163 ! current_user_can( 'edit_post', $chart_id )
1164 ) {
1165 if ( ! $can_die ) {
1166 return;
1167 }
1168 status_header( 400 );
1169 exit;
1170 }
1171
1172 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__ );
1173
1174 if ( ! isset( $_POST['vz-import-time'] ) ) {
1175 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
1176 }
1177
1178 if ( ! isset( $_POST['chart_data_src'] ) || Visualizer_Plugin::CF_SOURCE_FILTER !== $_POST['chart_data_src'] ) {
1179 // delete the filters in case this chart is being uploaded from other data sources
1180 delete_post_meta( $chart_id, Visualizer_Plugin::CF_FILTER_CONFIG );
1181 delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_FILTER_CONFIG );
1182 delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_DB_QUERY );
1183
1184 // delete "import from db" specific parameters.
1185 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
1186 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1187 delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
1188 }
1189
1190 // delete json related data.
1191 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_URL );
1192 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_ROOT );
1193 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_PAGING );
1194 delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_HEADERS );
1195
1196 // delete last error
1197 delete_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR );
1198
1199 // delete editor related data.
1200 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1201
1202 // delete this so that a JSON import can be later edited manually without a problem.
1203 delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1204
1205 $source = null;
1206 $render = new Visualizer_Render_Page_Update();
1207
1208 $remote_data = false;
1209 if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1210 $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1211 }
1212 if ( false !== $remote_data ) {
1213 $source = new Visualizer_Source_Csv_Remote( $remote_data );
1214 if ( isset( $_POST['vz-import-time'] ) ) {
1215 apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
1216 }
1217 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
1218 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
1219 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
1220 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
1221 $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1222 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1223 } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1224 $source = $this->handleTabularData();
1225 update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1226 } else {
1227 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__ );
1228 $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1229 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
1230 }
1231
1232 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__ );
1233
1234 if ( $source ) {
1235 if ( $source->fetch() ) {
1236 $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
1237 $populate = true;
1238 if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
1239 $json = unserialize( $content );
1240 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
1241 // if we populate the data even if it is empty, the chart will show "Table has no columns".
1242 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
1243 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__ );
1244 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, sprintf( 'Not populating chart data as source exists (%s) but data is empty!', $json['source'] ) );
1245 $populate = false;
1246 }
1247 }
1248 if ( $populate ) {
1249 $chart->post_content = $content;
1250 }
1251 wp_update_post( $chart->to_array() );
1252
1253 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1254 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1255 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1256
1257 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1258
1259 Visualizer_Module_Utility::set_defaults( $chart, null );
1260
1261 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1262 if ( isset( $settings['series'] ) && ! ( count( $settings['series'] ) - count( $source->getSeries() ) > 1 ) ) {
1263 $diff_total_series = abs( count( $settings['series'] ) - count( $source->getSeries() ) );
1264 if ( $diff_total_series ) {
1265 foreach ( range( 1, $diff_total_series ) as $k => $diff_series ) {
1266 $settings['series'][] = end( $settings['series'] );
1267 }
1268 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
1269 }
1270 }
1271
1272 $render->id = $chart->ID;
1273 $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1274 $render->series = json_encode( $source->getSeries() );
1275 $render->settings = json_encode( $settings );
1276 } else {
1277 $error = $source->get_error();
1278 if ( empty( $error ) ) {
1279 $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1280 }
1281 $render->message = $error;
1282 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1283 update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
1284 }
1285 } else {
1286 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
1287 }
1288 $render->render();
1289 if ( ! $can_die ) {
1290 return;
1291 }
1292 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1293 }
1294
1295 /**
1296 * Clones the chart.
1297 *
1298 * @since 1.0.0
1299 *
1300 * @access public
1301 */
1302 public function cloneChart() {
1303 $chart_id = $success = false;
1304 $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
1305 $capable = current_user_can( 'edit_posts' );
1306 if ( $nonce && $capable ) {
1307 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
1308 if ( $chart_id ) {
1309 $chart = get_post( $chart_id );
1310 $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
1311 }
1312 }
1313 $redirect = remove_query_arg( 'vaction', wp_get_referer() );
1314 if ( $success ) {
1315 $new_chart_id = wp_insert_post(
1316 array(
1317 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
1318 'post_title' => 'Visualization',
1319 'post_author' => get_current_user_id(),
1320 'post_status' => $chart->post_status,
1321 'post_content' => $chart->post_content,
1322 )
1323 );
1324 if ( is_wp_error( $new_chart_id ) ) {
1325 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__ );
1326 } else {
1327 $post_meta = get_post_meta( $chart_id );
1328 foreach ( $post_meta as $key => $value ) {
1329 if ( strpos( $key, 'visualizer-' ) !== false ) {
1330 add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1331 }
1332 }
1333 $redirect = esc_url(
1334 add_query_arg(
1335 array(
1336 'page' => 'visualizer',
1337 'type' => filter_input( INPUT_GET, 'type' ),
1338 'vaction' => false,
1339 ),
1340 admin_url( 'admin.php' )
1341 ),
1342 null,
1343 'db'
1344 );
1345 }
1346 }
1347
1348 if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1349 wp_die();
1350 }
1351 wp_redirect( $redirect );
1352 exit;
1353 }
1354
1355 /**
1356 * Exports the chart data
1357 *
1358 * @since 1.0.0
1359 *
1360 * @access public
1361 */
1362 public function exportData() {
1363 check_ajax_referer( Visualizer_Plugin::ACTION_EXPORT_DATA . Visualizer_Plugin::VERSION, 'security' );
1364 $capable = current_user_can( 'edit_posts' );
1365 if ( $capable ) {
1366 $chart_id = isset( $_GET['chart'] ) ? filter_var(
1367 $_GET['chart'],
1368 FILTER_VALIDATE_INT,
1369 array(
1370 'options' => array(
1371 'min_range' => 1,
1372 ),
1373 )
1374 ) : '';
1375 if ( $chart_id ) {
1376 $data = $this->_getDataAs( $chart_id, 'csv' );
1377 if ( $data ) {
1378 echo wp_send_json_success( $data );
1379 }
1380 }
1381 }
1382
1383 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1384 }
1385
1386 /**
1387 * Handles chart data page.
1388 *
1389 * @since 1.0.0
1390 *
1391 * @access private
1392 */
1393 private function _handleDataPage() {
1394 $data = $this->_getChartArray();
1395 $render = new Visualizer_Render_Page_Data();
1396 $render->chart = $this->_chart;
1397 $render->type = $data['type'];
1398
1399 if ( $data && $data['settings'] ) {
1400 unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1401 }
1402 wp_enqueue_style( 'visualizer-frame' );
1403 wp_enqueue_script( 'visualizer-render' );
1404 wp_localize_script(
1405 'visualizer-render',
1406 'visualizer',
1407 array(
1408 'l10n' => array(
1409 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
1410 'loading' => esc_html__( 'Loading...', 'visualizer' ),
1411 ),
1412 'charts' => array(
1413 'canvas' => $data,
1414 ),
1415 )
1416 );
1417
1418 do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1419
1420 if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
1421 global $Visualizer_Pro;
1422 $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
1423 }
1424
1425 // Added by Ash/Upwork
1426 $this->_addAction( 'admin_head', 'renderFlattrScript' );
1427 wp_iframe( array( $render, 'render' ) );
1428 }
1429
1430 /**
1431 * Returns the data for the query.
1432 *
1433 * @access public
1434 */
1435 public function getQueryData() {
1436 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
1437
1438 if ( ! current_user_can( 'administrator' ) ) {
1439 wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1440 }
1441 if ( ! is_super_admin() ) {
1442 wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1443 }
1444
1445 if ( ! Visualizer_Module::is_pro() ) {
1446 wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) );
1447 }
1448
1449 $params = wp_parse_args( $_POST['params'] );
1450 $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1451 $query = trim( $params['query'], ';' );
1452
1453 $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1454 $html = $source->fetch( true );
1455 $error = $source->get_error();
1456 if ( ! empty( $error ) ) {
1457 wp_send_json_error( array( 'msg' => $error ) );
1458 }
1459 wp_send_json_success( array( 'table' => $html ) );
1460 }
1461
1462 /**
1463 * Saves the query and the schedule.
1464 *
1465 * @access public
1466 */
1467 public function saveQuery() {
1468 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION, 'security' );
1469
1470 if ( ! current_user_can( 'administrator' ) ) {
1471 wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1472 }
1473 if ( ! is_super_admin() ) {
1474 wp_send_json_error( array( 'msg' => __( 'Action not allowed for this user.', 'visualizer' ) ) );
1475 }
1476
1477 if ( ! Visualizer_Module::is_pro() ) {
1478 wp_send_json_error( array( 'msg' => __( 'Feature is not available.', 'visualizer' ) ) );
1479 }
1480
1481 $chart_id = filter_input(
1482 INPUT_GET,
1483 'chart',
1484 FILTER_VALIDATE_INT,
1485 array(
1486 'options' => array(
1487 'min_range' => 1,
1488 ),
1489 )
1490 );
1491
1492 $hours = filter_input(
1493 INPUT_POST,
1494 'refresh',
1495 FILTER_VALIDATE_FLOAT,
1496 array(
1497 'options' => array(
1498 'min_range' => -1,
1499 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1500 ),
1501 )
1502 );
1503
1504 if ( ! is_numeric( $hours ) ) {
1505 $hours = -1;
1506 }
1507
1508 $render = new Visualizer_Render_Page_Update();
1509 if ( $chart_id ) {
1510 $params = wp_parse_args( $_POST['params'] );
1511 $query = trim( $params['query'], ';' );
1512 $source = new Visualizer_Source_Query( stripslashes( $query ), $chart_id, $params );
1513 $source->fetch( false );
1514 $error = $source->get_error();
1515 if ( empty( $error ) ) {
1516 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $query ) );
1517 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
1518 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
1519 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
1520 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1521 if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1522 $remote_db_params = $params;
1523 unset( $remote_db_params['query'] );
1524 unset( $remote_db_params['chart_id'] );
1525 update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1526 }
1527
1528 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
1529 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
1530 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
1531
1532 wp_update_post(
1533 array(
1534 'ID' => $chart_id,
1535 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
1536 )
1537 );
1538 $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
1539 $render->series = json_encode( $source->getSeries() );
1540 $render->id = $chart_id;
1541 } else {
1542 $render->message = $error;
1543 }
1544 }
1545 $render->render();
1546 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1547 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1548 }
1549 }
1550
1551
1552 /**
1553 * Saves the filter query and the schedule.
1554 *
1555 * @access public
1556 */
1557 public function saveFilter() {
1558 check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
1559
1560 $chart_id = filter_input(
1561 INPUT_GET,
1562 'chart',
1563 FILTER_VALIDATE_INT,
1564 array(
1565 'options' => array(
1566 'min_range' => 1,
1567 ),
1568 )
1569 );
1570
1571 $hours = filter_input(
1572 INPUT_POST,
1573 'refresh',
1574 FILTER_VALIDATE_INT,
1575 array(
1576 'options' => array(
1577 'min_range' => -1,
1578 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1579 ),
1580 )
1581 );
1582
1583 if ( 0 !== $hours && empty( $hours ) ) {
1584 $hours = -1;
1585 }
1586
1587 do_action( 'visualizer_save_filter', $chart_id, $hours );
1588
1589 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
1590 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
1591 }
1592 }
1593
1594 /**
1595 * Save chart image.
1596 *
1597 * @param string $base64_img Chart image.
1598 * @param int $chart_id Chart ID.
1599 * @param bool $save_attachment Save attachment.
1600 * @return attachment ID
1601 */
1602 public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1603 // Delete old chart image.
1604 $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
1605 if ( $old_attachment_id ) {
1606 wp_delete_attachment( $old_attachment_id, true );
1607 }
1608
1609 if ( ! $save_attachment ) {
1610 return 0;
1611 }
1612
1613 // Upload dir.
1614 $upload_dir = wp_upload_dir();
1615 $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1616
1617 $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1618 $img = str_replace( ' ', '+', $img );
1619 $decoded = base64_decode( $img );
1620 $filename = 'visualization-' . $chart_id . '.png';
1621 $file_type = 'image/png';
1622 $hashed_filename = $filename;
1623
1624 // Save the image in the uploads directory.
1625 require_once ABSPATH . '/wp-admin/includes/file.php';
1626 \WP_Filesystem();
1627 global $wp_filesystem;
1628 if ( ! is_a( $wp_filesystem, 'WP_Filesystem_Base' ) ) {
1629 $creds = request_filesystem_credentials( site_url() );
1630 wp_filesystem( $creds );
1631 }
1632 $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1633
1634 // Insert new chart image.
1635 $attachment = array(
1636 'post_mime_type' => $file_type,
1637 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1638 'post_content' => '',
1639 'post_status' => 'inherit',
1640 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1641 );
1642
1643 $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1644 return $attach_id;
1645 }
1646 }
1647