Plugin Directory

source: visualizer/trunk/classes/Visualizer/Module/Chart.php

Last change on this file was 3474710, checked in by themeisle, 3 weeks ago

Update to version 3.11.15 from GitHub

File size: 57.7 KB
Line 
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 */
29class 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}
Note: See TracBrowser for help on using the repository browser.