PluginProbe
M Chart / 1.11.2
M Chart v1.11.2
2.3.2 2.3.1 2.3 2.2.2 2.2.1 2.2 trunk 1.0 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.10 1.10.1 1.11 1.11.1 1.11.2 1.12 1.2 1.2.1 1.3 1.3.1 1.3.2 All 53 releases
m-chart / components / class-m-chart-admin.php

class-m-chart-admin.php in M Chart 1.11.2, at components/class-m-chart-admin.php

909 lines 28.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class M_Chart_Admin {
4 private $safe_settings = array(
5 'performance' => array(
6 'default',
7 'no-images',
8 'no-preview',
9 ),
10 'csv_delimiter' => array(
11 ',',
12 "\t",
13 ' ',
14 ';',
15 ),
16 );
17 private $plugin_url;
18
19 /**
20 * Constructor
21 */
22 public function __construct() {
23 $this->plugin_url = m_chart()->plugin_url();
24
25 add_action( 'admin_init', array( $this, 'admin_init' ) );
26 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
27 add_action( 'current_screen', array( $this, 'current_screen' ) );
28 add_action( 'admin_footer', array( $this, 'admin_footer' ) );
29 add_action( 'wp_ajax_m_chart_export_csv', array( $this, 'ajax_export_csv' ) );
30 add_action( 'wp_ajax_m_chart_get_chart_args', array( $this, 'ajax_get_chart_args' ) );
31 add_action( 'wp_ajax_m_chart_import_csv', array( $this, 'ajax_import_csv' ) );
32 add_action( 'edit_form_before_permalink', array( $this, 'edit_form_before_permalink' ) );
33 add_action( 'manage_' . m_chart()->slug . '_posts_custom_column', array( $this, 'manage_posts_custom_column' ), 10, 2 );
34 add_action( 'm_chart_settings_admin', array( $this, 'm_chart_settings_admin' ) );
35
36 add_filter( 'manage_' . m_chart()->slug . '_posts_columns', array( $this, 'manage_posts_columns' ) );
37 }
38
39 /**
40 * Look for save settings submissions
41 */
42 public function admin_init() {
43 $this->save_settings();
44
45 add_action( 'admin_notices', array( $this, 'library_warning' ) );
46 }
47
48 /**
49 * Add settings admin page
50 */
51 public function admin_menu() {
52 add_submenu_page(
53 'edit.php?post_type=' . m_chart()->slug,
54 esc_html__( 'M Chart Settings', 'm-chart' ),
55 esc_html__( 'Settings', 'm-chart' ),
56 'manage_options',
57 m_chart()->slug . '-settings',
58 array( $this, 'm_chart_settings' )
59 );
60
61 // If multiple libraries are active we'll give you the option of using each one
62 // @TODO As written this will break if there's ever more than 10 active libraries... so yeah
63 global $submenu;
64
65 $libraries = m_chart()->get_libraries();
66
67 // If there's only one library we stop here as it's unecessary
68 if ( 1 === count( $libraries ) ) {
69 return;
70 }
71
72 // Put the default library into the admin menu first
73 $args = array(
74 'post_type' => m_chart()->slug,
75 'library' => m_chart()->get_library(),
76 );
77
78 $submenu[ 'edit.php?post_type=' . m_chart()->slug ][10] = array(
79 'Add ' . $libraries[ m_chart()->get_library() ] . ' Chart',
80 'edit_posts',
81 add_query_arg( $args, admin_url( 'post-new.php' ) ),
82 );
83
84 unset( $libraries[ m_chart()->get_library() ] );
85
86 // Add a Add Chart option for each active library that isn't the current default
87 $key = 11;
88
89 foreach ( $libraries as $library => $library_name ) {
90 $args = array(
91 'post_type' => m_chart()->slug,
92 'library' => $library,
93 );
94
95 $submenu[ 'edit.php?post_type=' . m_chart()->slug ][ $key ] = array(
96 'Add ' . $library_name . ' Chart',
97 'edit_posts',
98 add_query_arg( $args, admin_url( 'post-new.php' ) ),
99 );
100
101 $key++;
102 }
103
104 // Gotta sort them so they're in the right order
105 ksort( $submenu[ 'edit.php?post_type=' . m_chart()->slug ] );
106 }
107
108 /**
109 * Display the M Chart settings admin page
110 */
111 public function m_chart_settings() {
112 $settings = m_chart()->get_settings();
113 require_once __DIR__ . '/templates/m-chart-settings.php';
114 }
115
116 /**
117 * Check for and save M Chart settings
118 */
119 public function save_settings() {
120 if ( ! current_user_can( 'manage_options' ) ) {
121 return;
122 }
123
124 // Check the nonce
125 if (
126 ! isset( $_POST[ m_chart()->slug ] )
127 || ! wp_verify_nonce( $_POST[ m_chart()->slug ]['nonce'], m_chart()->slug . '-save-settings' )
128 ) {
129 return;
130 }
131
132 $validated_settings = array();
133 $submitted_settings = $_POST[ m_chart()->slug ];
134
135 $default_settings = apply_filters( 'm_chart_default_settings', m_chart()->settings );
136
137 foreach ( $default_settings as $setting => $default ) {
138 if ( ! isset( $submitted_settings[ $setting ] ) ) {
139 $validated_settings[ $setting ] = $default;
140 continue;
141 }
142
143 if ( isset( $this->safe_settings[ $setting ] ) ) {
144 // If we've got an array of valid values lets check against that
145 $safe_setting = $this->safe_settings[ $setting ];
146
147 if ( in_array( $submitted_settings[ $setting ], $safe_setting, true ) ) {
148 $validated_settings[ $setting ] = $submitted_settings[ $setting ];
149 } else {
150 $validated_settings[ $setting ] = $default;
151 }
152 } elseif ( 'lang_settings' == $setting ) {
153 // The language settinsg require a bit more checking
154 foreach ( $default_settings['lang_settings'] as $lang_setting => $lang_default ) {
155 $lang_value = $submitted_settings['lang_settings'][ $lang_setting ];
156
157 if ( 'numericSymbols' == $lang_setting ) {
158 // The numeric symbols are input as a comma seperated string so we'll deal with that here
159 $numeric_symbols = explode( ',', $lang_value );
160 $safe_symbols = array();
161
162 foreach ( $numeric_symbols as $symbol ) {
163 $safe_symbols[] = trim( $symbol );
164 }
165
166 $validated_settings[ $setting ][ $lang_setting ] = $safe_symbols;
167 } elseif ( 'numericSymbolMagnitude' == $lang_setting ) {
168 // Only want positive numbers for the numericSymbolMagnitude value
169 if ( is_numeric( $lang_value ) && 0 < $lang_value ) {
170 $validated_settings[ $setting ][ $lang_setting ] = absint( $lang_value );
171 } else {
172 $validated_settings[ $setting ][ $lang_setting ] = $lang_default;
173 }
174 } else {
175 // The rest of the language settings are all single character values
176 $validated_settings[ $setting ][ $lang_setting ] = sanitize_text_field( substr( $lang_value, 0, 1 ) );
177 }
178 }
179 } else {
180 // Make sure the value is safe before attempting to save it
181 if ( preg_match( '#^[a-zA-Z0-9-_]+$#', $submitted_settings[ $setting ] ) ) {
182 $validated_settings[ $setting ] = $submitted_settings[ $setting ];
183 } else {
184 $validated_settings[ $setting ] = $default;
185 }
186 }
187 }
188
189 // Allow third party libraries to further validate the settings
190 $validated_settings = apply_filters( 'm_chart_validated_settings', $validated_settings, $submitted_settings );
191
192 update_option( m_chart()->slug, $validated_settings );
193
194 // Make sure the embed endpoint makes it into the rewrite rules
195 flush_rewrite_rules();
196
197 add_action( 'admin_notices', array( $this, 'save_success' ) );
198 }
199
200 /**
201 * Display an admin notice that the settings have been saved
202 */
203 public function save_success() {
204 ?>
205 <div class="updated notice notice-success">
206 <p><?php esc_html_e( 'Settings saved', 'm-chart' ); ?></p>
207 </div>
208 <?php
209 }
210
211 /**
212 * Display an admin notice when the site has charts that use Highcharts but M Chart Highcharts Library is not active/installed
213 */
214 public function library_warning() {
215 if ( is_plugin_active( 'm-chart-highcharts-library/m-chart-highcharts-library.php' ) ) {
216 return;
217 }
218
219 $highcharts_check = get_posts(
220 array(
221 'post_type' => m_chart()->slug,
222 'posts_per_page' => 1,
223 'post_status' => 'any',
224 'tax_query' => array(
225 array(
226 'taxonomy' => m_chart()->slug . '-library',
227 'field' => 'slug',
228 'terms' => 'highcharts',
229 ),
230 ),
231 )
232 );
233
234 if ( ! $highcharts_check ) {
235 return;
236 }
237 ?>
238 <div class="warning notice notice-warning">
239 <p>
240 <?php
241 echo str_replace(
242 esc_html__( 'M Chart Highcharts Library', 'm-chart' ),
243 '<strong>' . esc_html__( 'M Chart Highcharts Library', 'm-chart' ) . '</strong>',
244 esc_html__( 'You have charts that require the M Chart Highcharts Library plugin.', 'm-chart' )
245 );
246 ?>
247 </p>
248 <p><?php esc_html_e( 'These charts will no longer display unless you install the plugin:', 'm-chart' ); ?></p>
249 <p><a href="https://github.com/methnen/m-chart-highcharts-library/"
250 class="button-primary"><?php esc_html_e( 'Learn More', 'm-chart' ); ?></a></p>
251 </div>
252 <?php
253 }
254
255 /**
256 * Load CSS/Javascript necessary for the interface
257 *
258 * @param object the current screen object as passed by the current_screen action hook
259 */
260 public function current_screen( $screen ) {
261 if ( m_chart()->slug != $screen->post_type ) {
262 return;
263 }
264
265 // Only load these if we are on a post page
266 if ( 'post' == $screen->base ) {
267 // jQuery Mobile Touch Events
268 wp_enqueue_script(
269 'jquery-mobile-touch-events',
270 $this->plugin_url . '/components/external/jquery-mobile/jquery-mobile-touch-events.js',
271 array(),
272 m_chart()->version
273 );
274
275 // Handsontable
276 wp_enqueue_style(
277 'handsontable',
278 $this->plugin_url . '/components/external/handsontable/handsontable.css',
279 array(),
280 m_chart()->version
281 );
282
283 wp_enqueue_script(
284 'handsontable',
285 $this->plugin_url . '/components/external/handsontable/handsontable.js',
286 array( 'jquery' ),
287 m_chart()->version
288 );
289
290 // Handlebars
291 wp_enqueue_script(
292 'handlebars',
293 $this->plugin_url . '/components/external/handlebars/handlebars.js',
294 array(),
295 m_chart()->version
296 );
297
298 // canvg is useful for SVG -> Canvas conversions
299 wp_enqueue_script(
300 'canvg',
301 $this->plugin_url . '/components/external/canvg/umd.js',
302 array(),
303 m_chart()->version
304 );
305
306 // Admin panel JS
307 wp_enqueue_script(
308 'm-chart-admin',
309 $this->plugin_url . '/components/js/m-chart-admin.js',
310 array( 'jquery', 'handsontable', 'handlebars' ),
311 m_chart()->version
312 );
313
314 // We need the library and post ID for some bunch of stuff below
315 $post_id = isset( $_GET['post'] ) ? (int) $_GET['post'] : '';
316 $library = m_chart()->get_library();
317
318 if ( ! empty( $post_id ) ) {
319 $library = m_chart()->get_post_meta( absint( $post_id ), 'library' );
320 } elseif (
321 'post' == $screen->base
322 && 'add' == $screen->action
323 && isset( $_GET['library'] )
324 && m_chart()->is_valid_library( $_GET['library'] )
325 ) {
326 $library = $_GET['library'];
327 }
328
329 // Only load this if we are on an appropriate post page
330 if ( 'post' == $screen->base && 'chartjs' == $library ) {
331 wp_enqueue_script(
332 'm-chart-chartjs-admin',
333 $this->plugin_url . '/components/js/m-chart-chartjs-admin.js',
334 array( 'm-chart-admin', 'chartjs', 'jquery' ),
335 m_chart()->version
336 );
337 }
338
339 wp_localize_script(
340 'm-chart-admin',
341 'm_chart_admin',
342 array(
343 'refresh_counter' => 0,
344 'allow_form_submission' => false,
345 'request' => false,
346 'performance' => m_chart()->get_settings( 'performance' ),
347 'image_support' => apply_filters( 'm_chart_image_support', 'no', $library ),
348 'instant_preview_support' => apply_filters( 'm_chart_instant_preview_support', 'no', $library ),
349 'image_multiplier' => m_chart()->get_settings( 'image_multiplier' ),
350 'image_width' => m_chart()->get_settings( 'image_width' ),
351 'library' => $library,
352 'set_names' => m_chart()->get_post_meta( $post_id, 'set_names' ),
353 'delete_comfirm' => esc_attr__( 'Are you sure you want to delete this spreadsheet?', 'm-chart' ),
354 )
355 );
356
357 do_action( 'm_chart_admin_scripts', $library, $post_id );
358 }
359
360 // Admin panel CSS
361 wp_enqueue_style(
362 'm-chart-admin',
363 $this->plugin_url . '/components/css/m-chart-admin.css',
364 array(),
365 m_chart()->version
366 );
367 }
368
369 /**
370 * Add all of the metaboxes needed for the data and chart editing interface
371 */
372 public function meta_boxes() {
373 global $wp_meta_boxes;
374
375 // Remove excerpt from it's normal spot in the meta_boxes array so we can put it back in after the spreadsheet
376 // Users can move metaboxes, but this helps put things in a reasonable place on the first visit
377 $excerpt = $wp_meta_boxes[ m_chart()->slug ]['normal']['core']['postexcerpt'];
378 unset( $wp_meta_boxes[ m_chart()->slug ]['normal']['core']['postexcerpt'] );
379
380 add_meta_box(
381 m_chart()->slug . '-spreadsheet',
382 esc_html__( 'Data', 'm-chart' ),
383 array( $this, 'spreadsheet_meta_box' ),
384 m_chart()->slug,
385 'normal',
386 'high'
387 );
388
389 add_meta_box(
390 m_chart()->slug,
391 esc_html__( 'Chart', 'm-chart' ),
392 array( $this, 'chart_meta_box' ),
393 m_chart()->slug,
394 'normal',
395 'high'
396 );
397
398 $wp_meta_boxes[ m_chart()->slug ]['normal']['high']['postexcerpt'] = $excerpt;
399
400 // We are using our own interface for the units so we can remove the units taxonomy metabox
401 remove_meta_box( m_chart()->slug . '-unitsdiv', m_chart()->slug, 'side' );
402 }
403
404 /**
405 * Displays the spread sheet meta box
406 *
407 * @param object the WP post object as returned by the metabox API
408 */
409 public function spreadsheet_meta_box( $post ) {
410 $post_meta = m_chart()->get_post_meta( $post->ID );
411
412 // Setup default empty sheet data if needed
413 $sheet_data = empty( $post_meta['data'] ) ? array( array( '' ) ) : $post_meta['data']['sets'];
414
415 require_once __DIR__ . '/templates/spreadsheet-meta-box.php';
416 }
417
418 /**
419 * Displays the chart meta box
420 *
421 * @param object the WP post object as returned by the metabox API
422 */
423 public function chart_meta_box( $post ) {
424 // Force an instance of 1 since we NEVER show more than one chart at a time inside the admin panel
425 m_chart()->instance = 1;
426
427 $chart = m_chart()->get_chart( $post->ID );
428 $post_meta = m_chart()->get_post_meta( $post->ID );
429 $image = m_chart()->get_chart_image( $post->ID );
430 $settings = m_chart()->get_settings();
431
432 require_once __DIR__ . '/templates/chart-meta-box.php';
433 }
434
435 /**
436 * Insert CSV Import and Export forms into the footer when editing charts
437 */
438 public function admin_footer() {
439 $screen = get_current_screen();
440
441 if ( 'post' != $screen->base || m_chart()->slug != $screen->post_type ) {
442 return;
443 }
444 ?>
445 <form id="<?php echo esc_attr( $this->get_field_id( 'csv-import-form' ) ); ?>" style="display: none;">
446 <input type="file" name="import_csv_file" id="<?php echo esc_attr( $this->get_field_id( 'csv-file' ) ); ?>"
447 class="hide" />
448 </form>
449 <form action="<?php echo esc_url( admin_url( 'admin-ajax.php?action=m_chart_export_csv' ) ); ?>"
450 id="<?php echo esc_attr( $this->get_field_id( 'csv-export-form' ) ); ?>" style="display: none;" method="post">
451 <input type="hidden" name="post_id" value="" id="<?php echo esc_attr( $this->get_field_id( 'csv-post-id' ) ); ?>" />
452 <input type="hidden" name="data" value="" id="<?php echo esc_attr( $this->get_field_id( 'csv-data' ) ); ?>" />
453 <input type="hidden" name="title" value="" id="<?php echo esc_attr( $this->get_field_id( 'csv-title' ) ); ?>" />
454 <input type="hidden" name="set_name" value=""
455 id="<?php echo esc_attr( $this->get_field_id( 'csv-set-name' ) ); ?>" />
456 </form>
457 <script type="text/javascript">
458 <?php do_action( 'm_chart_admin_footer_javascript' ); ?>
459 </script>
460 <?php
461 }
462
463 /**
464 * Inserts a subtitle field under the title field on the chart edit form and includes the handlebars templates we'll need
465 *
466 * @param object the WP post object as returned by the metabox API
467 */
468 public function edit_form_before_permalink( $post ) {
469 if ( m_chart()->slug != $post->post_type ) {
470 return;
471 }
472
473 $post_meta = m_chart()->get_post_meta( $post->ID );
474
475 require_once __DIR__ . '/templates/subtitle-field.php';
476 require_once __DIR__ . '/templates/handlebars.php';
477 }
478
479 /**
480 * Display some additional information about a chart
481 *
482 * @param string the name of the custom column being displayed
483 * @param string the $post_id of the post being displayed in this row
484 */
485 public function manage_posts_custom_column( $column, $post_id ) {
486 if ( m_chart()->slug . '-type' != $column && m_chart()->slug . '-library' != $column ) {
487 return;
488 }
489
490 $library = m_chart()->get_post_meta( $post_id, 'library' );
491
492 if ( m_chart()->library( $library )->library != $library ) {
493 ?>
494 <span aria-hidden="true">—</span>
495 <span class="screen-reader-text"><?php echo esc_html__( 'Library not found', 'm-chart' ); ?></span>
496 <?php
497 return;
498 }
499
500 if ( m_chart()->slug . '-type' == $column ) {
501 $type = m_chart()->get_post_meta( $post_id, 'type' );
502 $type_name = m_chart()->library( $library )->type_option_names[ $type ];
503 ?>
504 <span class="type <?php echo esc_attr( $type ); ?>" title="<?php echo esc_attr( $type_name ); ?>">
505 <?php echo esc_html( $type_name ); ?>
506 </span>
507 <?php
508 }
509
510 if ( m_chart()->slug . '-library' == $column ) {
511 $library_name = m_chart()->library( $library )->library_name;
512 ?>
513 <span class="library <?php echo esc_attr( $library ); ?>" title="<?php echo esc_attr( $library_name ); ?>">
514 <?php echo esc_html( $library_name ); ?>
515 </span>
516 <?php
517 }
518 }
519
520 /**
521 * Add the Chart.js admin settings to the M Chart Settings page
522 */
523 public function m_chart_settings_admin() {
524 $settings = m_chart()->get_settings();
525 require __DIR__ . '/templates/m-chart-settings-chartjs.php';
526 }
527
528 /**
529 * Add our custom column to the array of columns for charts
530 *
531 * @param array the array of columns
532 *
533 * @return array array of columns with the custom column added
534 */
535 public function manage_posts_columns( $columns ) {
536 $new_columns = array();
537
538 foreach ( $columns as $column => $name ) {
539 $new_columns[ $column ] = $name;
540
541 if ( 'author' == $column || 'coauthors' == $column ) {
542 $new_columns[ m_chart()->slug . '-type' ] = 'Type';
543
544 if ( 'yes' == m_chart()->get_settings( 'show_library' ) ) {
545 $new_columns[ m_chart()->slug . '-library' ] = 'Library';
546 }
547 }
548 }
549
550 return $new_columns;
551 }
552
553 /**
554 * Hook to save_post action and save chart related post meta
555 *
556 * @param int the WP post ID of the post being saved
557 */
558 public function save_post( $post_id ) {
559 $post = get_post( $post_id );
560
561 // Check that this isn't an autosave
562 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
563 return;
564 }
565
566 // Check post type
567 if ( ! isset( $post->post_type ) || m_chart()->slug != $post->post_type ) {
568 return;
569 }
570
571 // Don't run on post revisions (almost always happens just before the real post is saved)
572 if ( wp_is_post_revision( $post->ID ) ) {
573 return;
574 }
575
576 // Make sure we've got some actual M Chart related data in the $_POST array
577 if ( ! isset( $_POST[ m_chart()->slug ] ) ) {
578 return;
579 }
580
581 // Check the nonce
582 if ( ! wp_verify_nonce( $_POST[ m_chart()->slug ]['nonce'], m_chart()->slug . '-save-post' ) ) {
583 return;
584 }
585
586 unset( $_POST[ m_chart()->slug ]['nonce'] );
587
588 // Check the permissions
589 if ( ! current_user_can( 'edit_post', $post->ID ) ) {
590 return;
591 }
592
593 // If there's an image being passed attach it to the chart post
594 $this->attach_image();
595
596 // Make sure we don't overwrite existing settings in the case someone hits update too quickly
597 if (
598 isset( $_POST[ m_chart()->slug ]['library'] )
599 // Make sure the library value is clean and valid before trying to use it
600 && $library = m_chart()->is_valid_library( $_POST[ m_chart()->slug ]['library'] )
601 ) {
602 // Load the library in question in case there's a filter/action we'll need
603 m_chart()->library( $library );
604
605 // update_post_meta passes the $_POST values directly to validate_post_meta
606 // validate_post_meta returns only valid post meta values and does data validation on each item
607 m_chart()->update_post_meta( $post->ID, $_POST[ m_chart()->slug ] );
608 }
609 }
610
611 /**
612 * Attach a given image to a chart post
613 *
614 * @param int the WP post ID of the post being saved
615 * @param string a base64 encoded string of the image we want to attach
616 */
617 public function attach_image() {
618 $settings = m_chart()->get_settings();
619
620 // If the performance setting isn't turned to default we don't do this
621 if ( 'default' != $settings['performance'] ) {
622 return;
623 }
624
625 if ( ! is_numeric( $_POST['post_ID'] ) ) {
626 return;
627 }
628
629 $post_id = absint( $_POST['post_ID'] );
630
631 // Make sure the library used on this post supports images
632 if ( 'no' == apply_filters( 'm_chart_image_support', 'no', m_chart()->get_post_meta( $post_id, 'library' ) ) ) {
633 return;
634 }
635
636 if ( ! current_user_can( 'edit_post', $post_id ) ) {
637 return false;
638 }
639
640 if ( ! $post = get_post( $post_id ) ) {
641 return false;
642 }
643
644 if ( '' == $_POST[ m_chart()->slug ]['img'] ) {
645 return false;
646 }
647
648 // Decode the image so we can save it
649 $decoded_img = base64_decode( str_replace( 'data:image/png;base64,', '', $_POST[ m_chart()->slug ]['img'] ) );
650
651 if ( '' == $decoded_img ) {
652 return false;
653 }
654
655 // Check for an existing attached image
656 $attachments = get_posts(
657 array(
658 'post_type' => 'attachment',
659 'posts_per_page' => 1,
660 'post_parent' => $post->ID,
661 'meta_key' => m_chart()->slug . '-image',
662 )
663 );
664
665 // If an existing image was found delete it
666 foreach ( $attachments as $attachment ) {
667 wp_delete_attachment( $attachment->ID, true );
668 }
669
670 // Upload image to WP
671 $file = wp_upload_bits( sanitize_title( $post->post_title . '-' . $post->ID ) . '.png', null, $decoded_img );
672
673 // START acting like media_sideload_image
674 preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $file['file'], $matches );
675
676 $file_array['name'] = basename( $matches[0] );
677 $file_array['tmp_name'] = $file['file'];
678
679 if ( is_wp_error( $file ) ) {
680 @unlink( $file_array['tmp_name'] );
681 $file_array['tmp_name'] = '';
682 }
683
684 $img_id = media_handle_sideload( $file_array, $post->ID, $post->post_title );
685
686 if ( is_wp_error( $img_id ) ) {
687 @unlink( $file_array['tmp_name'] );
688 return $img_id;
689 }
690 // STOP acting like media_sideload_image
691
692 // Set some meta on the attachment so we know it came from m-chart
693 add_post_meta( $img_id, m_chart()->slug . '-image', $post->ID );
694
695 // Set the attachment as the chart's thumbnail
696 update_post_meta( $post->ID, '_thumbnail_id', $img_id );
697 }
698
699 /**
700 * Parses an incoming CSV file and compiles it into an array
701 *
702 * @return array an array fo the data from the imported CSV file ready for use in the chart meta
703 */
704 public function ajax_import_csv() {
705 $post = get_post( absint( $_POST['post_id'] ) );
706
707 // Check post type
708 if ( ! isset( $post->post_type ) || m_chart()->slug != $post->post_type ) {
709 wp_send_json_error( esc_html__( 'Wrong post type', 'm-chart' ) );
710 }
711
712 // Check the nonce
713 if ( ! wp_verify_nonce( $_POST['nonce'], m_chart()->slug . '-save-post' ) ) {
714 wp_send_json_error( esc_html__( 'Invalid nonce', 'm-chart' ) );
715 }
716
717 // Check the permissions
718 if ( ! current_user_can( 'edit_post', $post->ID ) ) {
719 wp_send_json_error( esc_html__( 'Wrong post type', 'm-chart' ) );
720 }
721
722 // Make sure there's a CSV file
723 if ( empty( $_FILES ) || ! isset( $_FILES['import_csv_file']['name'] ) ) {
724 wp_send_json_error( esc_html__( 'No file to import', 'm-chart' ) );
725 }
726
727 // Make sure the file is a CSV file
728 $file_ext = strtolower( pathinfo( $_FILES['import_csv_file']['name'], PATHINFO_EXTENSION ) );
729
730 if ( 'csv' != $file_ext ) {
731 wp_send_json_error( esc_html__( 'Only CSV files can be imported ', 'm-chart' ) );
732 }
733
734 // Do some validation on the CSV file (mirroring what WP does for this sort of thing)
735 $csv_file = realpath( $_FILES['import_csv_file']['tmp_name'] );
736
737 if ( ! $csv_file ) {
738 wp_send_json_error( esc_html__( 'File path not found', 'm-chart' ) );
739 }
740
741 $csv_data = file_get_contents( $csv_file );
742
743 if ( '' == $csv_data ) {
744 wp_send_json_error( esc_html__( 'CSV file was empty', 'm-chart' ) );
745 }
746
747 // Get parseCSV library so we can use it to convert the CSV to a nice array
748 // Yes, PHP does this natively now but I've run into trouble with malformed CSV that parsCSV handles fine
749 require_once __DIR__ . '/external/parsecsv/parsecsv.lib.php';
750
751 $parse_csv = new parseCSV();
752
753 // The "\n" before and after is to deal with CSV files that don't have line breaks above and below the data
754 // Which then seems to confuse parseCSV occasionally
755 $csv_data = "\n" . trim( $csv_data ) . "\n";
756
757 // Set delimiter
758 $parse_csv->delimiter = isset( $_POST['csv_delimiter'] ) ? $_POST['csv_delimiter'] : m_chart()->get_settings( 'csv_delimiter' );
759
760 // Parse the CSV
761 $parse_csv->parse( $csv_data );
762
763 // This deals with Google Doc's crappy CSV exports which don't include columns at the end of a row if they are empty
764 $data_array = $this->fix_csv_data_array( $parse_csv->data );
765
766 wp_send_json_success( $data_array );
767 }
768
769 /**
770 * Helper function makes sure that the data array has matching numbers of array elements for each row
771 * CSV from some sources (Google Docs) doesn't include columns that are empty when they are at the end of a row (Why Google? WHY?)
772 *
773 * @param array an array of data as returned from the parseCSV class
774 *
775 * @param array the array of data with matching array value counts
776 */
777 public function fix_csv_data_array( $data_array ) {
778 $count = 0;
779
780 // Get largest row count
781 foreach ( $data_array as $data ) {
782 $temp_count = count( $data );
783
784 $count = ( $temp_count > $count ) ? $temp_count : $count;
785 }
786
787 // Fix arrays so value counts match
788 foreach ( $data_array as $key => $data ) {
789 $temp_count = count( $data );
790
791 if ( $temp_count < $count ) {
792 $difference = $count - $temp_count;
793
794 for ( $i = 0; $i < $difference; $i++ ) {
795 $data_array[ $key ][] = '';
796 }
797 }
798 }
799
800 return $data_array;
801 }
802
803 /**
804 * Converts data array into CSV and outputs it to the browser
805 */
806 public function ajax_export_csv() {
807 // Purposely using $_REQUEST here since this method can work via a GET and POST request
808 // POST requests are used when passing the data value since it's too big to pass via GET
809 if ( ! is_numeric( $_REQUEST['post_id'] ) || ! current_user_can( 'edit_post', absint( $_REQUEST['post_id'] ) ) ) {
810 wp_die( 'Unauthorized access', 'You do not have permission to do that', array( 'response' => 401 ) );
811 }
812
813 $post = get_post( absint( $_REQUEST['post_id'] ) );
814
815 // If the user passed a data value in their request we'll use it after validation
816 if ( isset( $_POST['data'] ) && isset( $_POST['title'] ) ) {
817 $data = m_chart()->validate_data( json_decode( stripslashes( $_POST['data'] ) ) );
818 $file_name = sanitize_title( $_POST['title'] );
819 } else {
820 $data = m_chart()->get_post_meta( $post->ID, 'data' );
821 $file_name = sanitize_title( get_the_title( $post->ID ) );
822 }
823
824 $set_name = sanitize_title( $_REQUEST['set_name'] );
825
826 if ( empty( $data ) ) {
827 return;
828 }
829
830 require_once __DIR__ . '/external/parsecsv/parsecsv.lib.php';
831 $parse_csv = new parseCSV();
832
833 // Set delimiter
834 $parse_csv->output_delimiter = m_chart()->get_settings( 'csv_delimiter' );
835
836 $parse_csv->output( $file_name . '-' . $set_name . '.csv', $data );
837 die;
838 }
839
840 /**
841 * Returns JSON encoded chart args from $_POST values sent from the admin panel
842 *
843 * @return string a JSON encoded string containing all of the chart args needed to update an active chart
844 */
845 public function ajax_get_chart_args() {
846 // Check the nonce
847 if ( ! wp_verify_nonce( $_POST['nonce'], m_chart()->slug . '-save-post' ) ) {
848 wp_send_json_error( esc_html__( 'Invalid nonce', 'm-chart' ) );
849 }
850
851 // Does the post exist?
852 if ( ! $post = get_post( absint( $_POST['post_id'] ) ) ) {
853 wp_send_json_error( esc_html__( 'Invalid post', 'm-chart' ) );
854 }
855
856 // Can the user edit this post?
857 if ( ! current_user_can( 'edit_post', $post->ID ) ) {
858 wp_send_json_error( esc_html__( 'Permission error', 'm-chart' ) );
859 }
860
861 // Is this a valid library?
862 if ( ! m_chart()->is_valid_library( $_POST['library'] ) ) {
863 wp_send_json_error( esc_html__( 'Invalid library', 'm-chart' ) );
864 }
865
866 if ( 'highcharts' == $_POST['library'] ) {
867 $library = m_chart()->library( $_POST['library'] );
868 }
869
870 $library = apply_filters( 'm_chart_library_class', m_chart()->library_class, $_POST['library'] );
871
872 // Set these values so that get_chart_args has them already available before we call it
873 $library->args = m_chart()->get_chart_default_args;
874 $library->post = $post;
875 $library->post->post_title = sanitize_text_field( $_POST['title'] );
876
877 // validate_post_meta returns only valid post meta values and does data validation on each item
878 $library->post_meta = m_chart()->validate_post_meta( $_POST['post_meta'] );
879
880 wp_send_json_success( $library->get_chart_args( $library->post->ID, $library->args, true, false ) );
881 }
882
883 /**
884 * Return a name spaced field name
885 *
886 * @param string the field name we want to name space
887 *
888 * @param string a name spaced field name
889 */
890 public function get_field_name( $field_name, $parent_field_name = '' ) {
891 if ( '' != $parent_field_name ) {
892 return m_chart()->slug . '[' . $parent_field_name . ']' . '[' . $field_name . ']';
893 }
894
895 return m_chart()->slug . '[' . $field_name . ']';
896 }
897
898 /**
899 * Return a name spaced field id
900 *
901 * @param string the field id we want to name space
902 *
903 * @param string a name spaced field id
904 */
905 public function get_field_id( $field_name ) {
906 return m_chart()->slug . '-' . $field_name;
907 }
908 }
909