PluginProbe
M Chart / 1.0
M Chart v1.0
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.php

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

541 lines 15.3 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 {
4 public $dev = true;
5 public $slug = 'm-chart';
6 public $plugin_name = 'Chart';
7 public $chart_meta_fields = array(
8 'library' => 'highcharts',
9 'type' => 'line',
10 'parse_in' => 'rows',
11 'labels' => true,
12 'subtitle' => '',
13 'y_title' => '',
14 'y_units' => '',
15 'y_min' => false,
16 'x_title' => '',
17 'x_units' => '',
18 'height' => 400,
19 'legend' => true,
20 'source' => '',
21 'source_url' => '',
22 'data' => array(),
23 );
24 public $get_chart_default_args = array(
25 'img' => 'no',
26 'width' => 'responsive',
27 );
28 public $parse_options = array(
29 'columns',
30 'rows',
31 );
32 public $options_set;
33 public $plugin_url;
34
35 private $admin;
36 private $highcharts;
37 private $parse;
38 private $valid_libraries = array(
39 'highcharts',
40 );
41
42 /**
43 * Constructor
44 */
45 public function __construct() {
46 add_action( 'init', array( $this, 'init' ) );
47 add_action( 'save_post', array( $this, 'save_post' ) );
48
49 add_shortcode( 'chart', array( $this, 'chart_shortcode' ) );
50 }
51
52 /**
53 * Admin object accessor
54 */
55 public function admin() {
56 if ( ! $this->admin ) {
57 require_once __DIR__ . '/class-m-chart-admin.php';
58 $this->admin = new M_Chart_Admin;
59 }
60
61 return $this->admin;
62 }
63
64 /**
65 * Highcharts object accessor
66 */
67 public function highcharts() {
68 if ( ! $this->highcharts ) {
69 require_once __DIR__ . '/class-m-chart-highcharts.php';
70 $this->highcharts = new M_Chart_Highcharts;
71 }
72
73 return $this->highcharts;
74 }
75
76 /**
77 * Parse object accessor
78 */
79 public function parse() {
80 if ( ! $this->parse ) {
81 require_once __DIR__ . '/class-m-chart-parse.php';
82 $this->parse = new M_Chart_Parse;
83 }
84
85 return $this->parse;
86 }
87
88 /**
89 * Do init stuff
90 */
91 public function init() {
92 // Register the units taxonomy
93 register_taxonomy(
94 $this->slug . '-units',
95 array( $this->slug ),
96 array(
97 'hierarchical' => true,
98 'labels' => array(
99 'name' => esc_html__( 'Chart Units', 'm-chart' ),
100 'singular_name' => esc_html__( 'Chart Unit', 'm-chart' ),
101 'search_items' => esc_html__( 'Search Chart Units', 'm-chart' ),
102 'all_items' => esc_html__( 'All Chart Units', 'm-chart' ),
103 'parent_item' => esc_html__( 'Parent Chart Unit', 'm-chart' ),
104 'parent_item_colon' => esc_html__( 'Parent Chart Unit:', 'm-chart' ),
105 'edit_item' => esc_html__( 'Edit Chart Unit', 'm-chart' ),
106 'update_item' => esc_html__( 'Update Chart Unit', 'm-chart' ),
107 'add_new_item' => esc_html__( 'Add New Chart Unit', 'm-chart' ),
108 'new_item_name' => esc_html__( 'Chart Unit Name', 'm-chart' ),
109 'menu_name' => esc_html__( 'Chart Units', 'm-chart' ),
110 ),
111 'show_ui' => true,
112 'query_var' => true,
113 'rewrite' => array(
114 'slug' => $this->slug . '-units',
115 ),
116 )
117 );
118
119 // Get the public taxonomies so the custom post type can use them
120 $taxonomies = get_taxonomies( array( 'public' => true ) );
121
122 // Register the charts custom post type
123 register_post_type(
124 $this->slug,
125 array(
126 'labels' => array(
127 'name' => esc_html__( 'Charts', 'm-chart' ),
128 'singular_name' => esc_html__( 'Chart', 'm-chart' ),
129 'add_new' => esc_html__( 'Add Chart', 'm-chart' ),
130 'add_new_item' => esc_html__( 'Add Chart', 'm-chart' ),
131 'edit' => esc_html__( 'Edit', 'm-chart' ),
132 'edit_item' => esc_html__( 'Edit Chart', 'm-chart' ),
133 'new_item' => esc_html__( 'New Chart', 'm-chart' ),
134 'view' => esc_html__( 'View', 'm-chart' ),
135 'view_item' => esc_html__( 'View Chart', 'm-chart' ),
136 'search_items' => esc_html__( 'Search Charts', 'm-chart' ),
137 'not_found' => esc_html__( 'No Charts found', 'm-chart' ),
138 'not_found_in_trash' => esc_html__( 'No Charts found in the Trash', 'm-chart' ),
139 ),
140 'register_meta_box_cb' => is_admin() ? array( $this->admin(), 'meta_boxes' ) : null,
141 'public' => true,
142 'hierarchical' => false,
143 'exclude_from_search' => false,
144 'menu_position' => 9,
145 'menu_icon' => 'dashicons-chart-pie',
146 'query_var' => true,
147 'can_export' => true,
148 'has_archive' => 'charts',
149 'description' => esc_html__( 'Manage data sets and display them as charts in WordPress.', $this->slug ),
150 'rewrite' => array( 'slug' => 'chart' ),
151 'supports' => array(
152 'author',
153 'title',
154 'excerpt',
155 'comments',
156 ),
157 'taxonomies' => $taxonomies,
158 )
159 );
160
161 $this->plugin_url = $this->plugin_url();
162
163 // Register the graphing library scripts
164 wp_register_script(
165 'highcharts-js',
166 $this->plugin_url . '/components/external/highcharts/highcharts.js',
167 array( 'jquery' )
168 );
169 }
170
171 /**
172 * VIP's CDN was breaking Highcharts ability to handle embedded SVGs so this should circumvent that
173 * If you wanted to say, watermark your charts, SVGs suddenly become very important
174 *
175 * @param string $path option additional path to be used (e.g. components)
176 *
177 * @return string URL to the plugin directory with path if parameter was passed
178 */
179 public function plugin_url( $path = '' ) {
180 if ( is_admin() ) {
181 $url_base = parse_url( admin_url() );
182 }
183 else
184 {
185 $url_base = parse_url( home_url() );
186 }
187
188 $url_path = parse_url( plugins_url( $path, __DIR__ ) );
189
190 return $url_base['scheme'] . '://' . $url_base['host'] . preg_replace( '#/$#', '', $url_path['path'] ) . ( empty( $url_path['query'] ) ? '' : '?' . $url_path['query'] );
191 }
192
193 /**
194 * Get chart post meta
195 *
196 * @param int $post_id WP post ID of the post you want post meta from
197 * @param string $field optional field to be returend instead of all post meta
198 *
199 * @return string URL to the plugin directory with path if parameter was passed
200 */
201 public function get_post_meta( $post_id, $field = false ) {
202 $post_meta = get_post_meta( $post_id, $this->slug, true );
203 $post_meta = wp_parse_args( $post_meta, $this->chart_meta_fields );
204
205 if ( $field && isset( $post_meta[ $field ] ) ) {
206 return $post_meta[ $field ];
207 }
208 elseif ( $field ) {
209 return null;
210 }
211
212 return $post_meta;
213 }
214
215 /**
216 * Update the post meta based and set unit terms if appropriate
217 *
218 * @param int $post_id WP post ID of the post you want to attach post meta to
219 * @param array $meta an array of the post meta you want to attach to the post
220 */
221 public function update_post_meta( $post_id, $meta ) {
222 // Make sure the meta is formatted correctly and validated
223 $parsed_meta = $this->validate_post_meta( $meta );
224
225 // Set unit terms
226 $terms = array();
227
228 if ( '' != $parsed_meta['y_units'] ) {
229 $terms[] = $parsed_meta['y_units'];
230 }
231
232 if ( '' != $parsed_meta['x_units'] ) {
233 $terms[] = $parsed_meta['x_units'];
234 }
235
236 wp_set_object_terms( $post_id, $terms, $this->slug . '-units' );
237
238 // Save meta to the post
239 update_post_meta( $post_id, $this->slug, $parsed_meta );
240 do_action( 'm_chart_update_post_meta', $post_id, $parsed_meta );
241 }
242
243 /**
244 * Parses a $meta array and returns a cleaned and validated array
245 *
246 * @param array $meta an array of the post meta you want to attach to the post
247 *
248 * @return array of cleaned/validated post meta
249 */
250 public function validate_post_meta( $meta ) {
251 // Need to set checkboxes before checking or they can't be deselected
252 $chart_meta['labels'] = false;
253 $chart_meta['y_min'] = false;
254 $chart_meta['legend'] = false;
255
256 // Filter values so we know the data is clean
257 foreach ( $this->chart_meta_fields as $field => $default ) {
258 if ( isset( $meta[ $field ] ) ) {
259 if ( 'source_url' == $field && '' != $meta[ $field ] ) {
260 $chart_meta[ $field ] = esc_url_raw( $meta[ $field ] );
261 } elseif ( 'data' == $field ) {
262 $chart_meta[ $field ] = $meta[ $field ];
263 } elseif ( in_array( $field, array( 'labels', 'y_min', 'legend' ) ) ) {
264 $chart_meta[ $field ] = (bool) $meta[ $field ];
265 } elseif ( 'height' == $field ) {
266 $chart_meta[ $field ] = absint( $meta[ $field ] );
267
268 if ( $chart_meta[ $field ] > 900 ) {
269 $chart_meta[ $field ] = 900;
270 } else if ( $chart_meta[ $field ] < 300 ) {
271 $chart_meta[ $field ] = 300;
272 }
273 } else {
274 $chart_meta[ $field ] = wp_filter_nohtml_kses( $meta[ $field ] );
275 }
276 }
277 elseif ( ! isset( $chart_meta[ $field ] ) ) {
278 // Fall back on the default value if there wasn't one in the given meta
279 $chart_meta[ $field ] = $default;
280 }
281 }
282
283 // If the data value is not an array we asume it is JSON encoded (i.e. from Handsontable)
284 if ( ! is_array( $chart_meta['data'] ) && '' != $chart_meta['data'] ) {
285 $chart_meta['data'] = json_decode( stripslashes( $chart_meta['data'] ) );
286 }
287
288 // Validate the data array
289 $chart_meta['data'] = $this->validate_data( $chart_meta['data'] );
290
291 return $chart_meta;
292 }
293
294 /**
295 * Runs all of the raw data array values through wp_filter_nohtml_kses
296 *
297 * @param array $data an array of data as passed by the user
298 *
299 * @return array of validated data
300 */
301 function validate_data( $data ) {
302 if ( ! is_array( $data ) ) {
303 return wp_filter_nohtml_kses( $data );
304 }
305
306 foreach ( $data as $key => $value ) {
307 if ( is_array( $value ) ) {
308 $data[ $key ] = $this->validate_data( $value );
309 }
310 else {
311 $data[ $key ] = wp_filter_nohtml_kses( $value );
312 }
313 }
314
315 return $data;
316 }
317
318
319 /**
320 * Hook to save_post action and save chart related post meta
321 *
322 * @param int $post_id WP post ID of the post
323 */
324 public function save_post( $post_id ) {
325 // We do this in the main class because otherwise it won't get hooked soon enough
326 $this->admin()->save_post( $post_id );
327 }
328
329 /**
330 * Returns a chart
331 *
332 * @param int $post_id WP post ID of the chart you want
333 * @param array $args an array of args
334 *
335 * @return string HTML and Javascript needed to display a chart (or if appropriate an HTML image tag)
336 */
337 public function get_chart( $post_id, $args = array() ) {
338 $args = wp_parse_args( $args, $this->get_chart_default_args );
339
340 // If they want the image version or the request is happening from a feed we return the image tag
341 if ( 'yes' == $args['img'] || is_feed() ) {
342 $image = $this->get_chart_image( $post_id );
343
344 // Default behavior is to return the full size image but with the width/height values halved
345 // This should result in an image that looks nice on retina or better screens
346 $image['width'] = $image['width'] / 2;
347 $image['height'] = $image['height'] / 2;
348
349 $image = apply_filters( 'm_chart_get_chart_image_tag', $image, $post_id, $args );
350
351 ob_start();
352 ?>
353 <img src="<?php echo esc_url( $image['url'] ); ?>" alt="<?php echo esc_attr( $image['name'] ); ?>" width="<?php echo absint( $image['width'] ); ?>" height="<?php echo absint( $image['height'] ); ?>" />
354 <?php
355 return ob_get_clean();
356 }
357
358 $library = $this->get_post_meta( $post_id, 'library' );
359
360 // If it's not a valid library we don't proceed
361 if ( ! $this->is_valid_library( $library ) ) {
362 return;
363 }
364
365 // If we haven't enqueued the right library yet lets do it
366 if ( ! wp_script_is( $library . '-js', 'enqueued' ) ) {
367 wp_enqueue_script( $library . '-js' );
368 }
369
370 ob_start();
371 require __DIR__ . '/templates/' . $library . '-chart.php';
372 return ob_get_clean();
373 }
374
375 /**
376 * Return an array of image values for a chart
377 *
378 * @param int $post_id WP post ID of the chart you want an image for
379 *
380 * @return array an arry of image values url, width, height, etc...
381 */
382 public function get_chart_image( $post_id ) {
383 if ( ! $thumbnail_id = get_post_meta( $post_id, '_thumbnail_id', true ) ) {
384 return false;
385 }
386
387 if ( ! $thumbnail = wp_get_attachment_image_src( $thumbnail_id, 'full' ) ) {
388 return false;
389 }
390
391 return array(
392 'url' => $thumbnail[0],
393 'file' => basename( $thumbnail[0] ),
394 'width' => $thumbnail[1],
395 'height' => $thumbnail[2],
396 'name' => get_the_title( $thumbnail_id ),
397 );
398 }
399
400 /**
401 * Return a chart via the [chart id="x"] short code
402 *
403 * @param array $args an array of arguments passed by the WP short code API
404 *
405 * @return string the chart requested in Javascript or HTML form
406 */
407 public function chart_shortcode( $args ) {
408 $default_args = $this->get_chart_default_args;
409 $default_args['id'] = '';
410
411 $args = shortcode_atts( $default_args, $args );
412
413 $post_id = $args['id'];
414 unset( $args['id'] );
415
416 // Did we get a chart ID?
417 if ( ! is_numeric( $post_id ) ) {
418 return;
419 }
420
421 // Make sure the chart actually exists and that it's a chart so we don't fatal later
422 if ( $this->slug != get_post_type( $post_id ) ) {
423 return;
424 }
425
426 // Is it published?
427 if ( 'publish' != get_post_status( $post_id ) ) {
428 return;
429 }
430
431 return $this->get_chart( $post_id, $args );
432 }
433
434 /**
435 * Helper function that prevents issues with stripslashes and unicode characters
436 * stripslashes will strip off the slash before unicode characters which is sucky, this prevents that
437 *
438 * @param string $string a string that may have unicode characters as well as unecessary escaping
439 *
440 * @return string a string with any unecessary escaping removed
441 */
442 public function unicode_aware_stripslashes( $string ) {
443 return stripslashes( preg_replace( '#\\\u[a-fA-F0-9]{4}#', '\\\\$0', $string ) );
444 }
445
446 /**
447 * Helper function that returns the chart unit terms
448 *
449 * @return array an array of generated and/or compiled unit terms
450 */
451 public function get_unit_terms() {
452 $terms = get_terms( $this->slug . '-units', array( 'hide_empty' => false ) );
453
454 if ( empty( $terms ) ) {
455 $terms = $this->generate_unit_terms();
456 }
457
458 return $this->compile_unit_terms( $terms );
459 }
460
461 /**
462 * Helper function that returns the chart unit terms
463 *
464 * @param array an array of unit terms
465 *
466 * @return array an array of compiled unit terms
467 */
468 public function compile_unit_terms( $terms ) {
469 $compiled_terms = array();
470 $parents = array();
471
472 foreach ( $terms as $unit ) {
473 if ( 0 == $unit->parent ) {
474 $parents[ $unit->term_id ] = $unit->name;
475 }
476 }
477
478 foreach ( $terms as $unit ) {
479 if ( 0 != $unit->parent && isset( $parents[ $unit->parent ] ) ) {
480 $compiled_terms[ $parents[ $unit->parent ] ][] = $unit;
481 }
482 }
483
484 ksort( $compiled_terms );
485
486 return $compiled_terms;
487 }
488
489 /**
490 * Helper function that populates the unit terms taxonomy with a default set of terms
491 *
492 * @return array an array of the newly generated unit terms
493 */
494 public function generate_unit_terms() {
495 // Load the default terms array
496 $default_terms = require __DIR__ .'/array-default-unit-terms.php';
497
498 $terms = array();
499
500 foreach ( $default_terms as $parent_term => $child_terms ) {
501 $parent = (object) wp_insert_term( $parent_term, $this->slug . '-units' );
502 $terms[] = get_term( $parent->term_id, $this->slug . '-units' );
503
504 foreach ( $child_terms as $child_term ) {
505 $term = (object) wp_insert_term( $child_term, $this->slug . '-units', array( 'parent' => $parent->term_id ) );
506 $terms[] = get_term( $term->term_id, $this->slug . '-units' );
507 }
508 }
509
510 return $terms;
511 }
512
513 /**
514 * If the passed library is valid return TRUE otherwise FALSE
515 *
516 * @param string library string (i.e. 'highcharts')
517 *
518 * @return bool
519 */
520 public function is_valid_library( $library ) {
521 if ( ! in_array( $library, $this->valid_libraries ) ) {
522 return false;
523 }
524
525 return true;
526 }
527 }
528
529 /**
530 * Plugin object accessor
531 */
532 function m_chart() {
533 global $m_chart;
534
535 if ( ! $m_chart ) {
536 $m_chart = new M_Chart;
537 }
538
539 return $m_chart;
540 }
541