PluginProbe
SQL Chart Builder / 3.0.3
SQL Chart Builder v3.0.3
3.0.5 3.0.4 3.0.3 3.0.2 3.0.1 trunk 1.0.2 1.0.3 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 2.3.7.1 2.3.7.2 2.3.8 3.0.0
← All changes | functions.php +656 -207 2.3.63.0.3 View file →
@@ -31,14 +31,12 @@
31 31 <tbody><tr><td style="width: auto;vertical-align: top;padding: 20px;">
32 32 <h2>WooCommerce Search Engine – INSTANT, RELEVANT AND SMART Search Box</h2>
33 33 <h3>Turn your website search into Smart Search which find products by price, SKU, attributes, meta data, categorys, tags etc. </h3>
34 34 <p>“WooCommerce Search Engine” is a very powerful and easy to use WooCommerce Search Plugin which turns a simple search box of your WooCommerce Store to the powerful multifunctional magic box which helps you to sell more products. The plugin UI is compatible with ALL THEMES.</p>
35 - <a target="_blank" style="border:0px solid #6200ee;border-radius:0px;color:white;font-weight:bold;background: #6200ee;" class="button button-secondary"
35 + <a target="_blank" style="border:0px solid #6200ee;border-radius:0px;color:white;font-weight:bold;background: #6200ee;" class="button button-secondary"
36 36 href="https://codecanyon.net/item/woocommerce-search-box/15685698">Get the Search Box </a>
37 37 </td><td style="position:relative">
38 - <a href="'.admin_url().'/edit.php?post_type=gvn_schart&gvnsql_dismiss_recommendation=1'.'" style="position: absolute;right: 0;top: -15px;right: -10px;"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="11" height="14" viewBox="0 0 11 14">
39 - <path d="M10.141 10.328q0 0.312-0.219 0.531l-1.062 1.062q-0.219 0.219-0.531 0.219t-0.531-0.219l-2.297-2.297-2.297 2.297q-0.219 0.219-0.531 0.219t-0.531-0.219l-1.062-1.062q-0.219-0.219-0.219-0.531t0.219-0.531l2.297-2.297-2.297-2.297q-0.219-0.219-0.219-0.531t0.219-0.531l1.062-1.062q0.219-0.219 0.531-0.219t0.531 0.219l2.297 2.297 2.297-2.297q0.219-0.219 0.531-0.219t0.531 0.219l1.062 1.062q0.219 0.219 0.219 0.531t-0.219 0.531l-2.297 2.297 2.297 2.297q0.219 0.219 0.219 0.531z"></path>
40 - </svg>
38 + <a href="'.admin_url().'/edit.php?post_type=gvn_schart&gvnsql_dismiss_recommendation=1'.'" style="position: absolute;right: 0;top: -15px;right: -10px;">{svg}
41 39 </a>
42 40 <img src="'.plugin_dir_url( __FILE__ ) . 'asset/img/recommended1.jpg" style="max-width: 430px;"></td></tr>
43 41 </tbody></table>';
44 42 }
@@ -49,18 +47,20 @@
49 47 {
50 48 global $post;
51 49
52 50
53 - if(
54 - (!empty($_SERVER["REQUEST_URI"]) and strpos($_SERVER["REQUEST_URI"],'post_type=gvn_schart')!==false)
55 - or
51 + if(
52 + (!empty($_SERVER["REQUEST_URI"]) and strpos(sanitize_text_field(wp_unslash($_SERVER["REQUEST_URI"])),'post_type=gvn_schart')!==false)
53 + or
56 54 (!empty($post) and $post->post_type == 'gvn_schart')
57 55 ){
58 - echo guaven_sqlcharts_recommended();
56 + echo str_replace('{svg}','<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="11" height="14" viewBox="0 0 11 14">
57 + <path d="M10.141 10.328q0 0.312-0.219 0.531l-1.062 1.062q-0.219 0.219-0.531 0.219t-0.531-0.219l-2.297-2.297-2.297 2.297q-0.219 0.219-0.531 0.219t-0.531-0.219l-1.062-1.062q-0.219-0.219-0.219-0.531t0.219-0.531l2.297-2.297-2.297-2.297q-0.219-0.219-0.219-0.531t0.219-0.531l1.062-1.062q0.219-0.219 0.531-0.219t0.531 0.219l2.297 2.297 2.297-2.297q0.219-0.219 0.531-0.219t0.531 0.219l1.062 1.062q0.219 0.219 0.219 0.531t-0.219 0.531l-2.297 2.297 2.297 2.297q0.219 0.219 0.219 0.531z"></path>
58 + </svg>',wp_kses_post(guaven_sqlcharts_recommended(),[]));
59 59 }
60 -
61 60
62 61
62 +
63 63 if (!empty($post) and $post->post_type == 'gvn_schart'):
64 64 if (!current_user_can('manage_options')) {
65 65 echo '<br><br>
66 66 <div class="updated gf-alert gf-alert-danger">Only administrators can manage this page</div>';
@@ -66,24 +66,31 @@
66 66 <div class="updated gf-alert gf-alert-danger">Only administrators can manage this page</div>';
67 67 die();
68 68 }
69 69 echo '<div class="updated gf-alert gf-alert-info">';
70 - if (empty($_GET["post"]) and strpos($_SERVER["REQUEST_URI"], "post-new") === false):
71 - $gf_message = 'Use <b>Add new</b> button above to create new sql report. And click on any existing rule names below
72 - to manage them. ';
73 - else:
74 - $gf_message = '
75 - 1. Give any name to your report.<br>
76 - 2. Choose chart type, type sql query, enter field names, labels and then press to Publish/Update<br>
77 - 3. After update you will see the name and the demo of the needed shortcode at the bottom of this admin page. You can use that shortcode anywhere in your website: in pages, posts, widgets etc. <br>
78 - ';
79 - endif;
80 - _e('<div style="float:left;max-width:calc(100% - 345px)">' . $gf_message . '</div>', 'guaven_sqlcharts');
70 + if (empty($_GET["post"]) && strpos(sanitize_text_field(wp_unslash($_SERVER["REQUEST_URI"])), "post-new") === false) {
71 + $gf_message = __(
72 + 'Use <b>Add new</b> button above to create a new SQL report. And click on any existing rule names below to manage them.',
73 + 'guaven_sqlcharts'
74 + );
75 + } else {
76 + $gf_message = __(
77 + '1. Give any name to your report.<br>
78 + 2. Pick a chart type, build your SQL query with the visual builder (or type it — autocomplete will help), map the X/Y columns and press Publish/Update.<br>
79 + 3. After update, you will see the shortcode of this chart at the bottom of the page. You can use that shortcode anywhere in your website: in pages, posts, widgets, etc.',
80 + 'guaven_sqlcharts'
81 + );
82 + }
83 +
84 + echo '<div style="float:left;max-width:calc(100% - 345px)">';
85 + echo wp_kses_post( $gf_message );
86 + echo '</div>';
87 +
81 88 echo '<div style="float: right;
82 89 margin-top: 0px;
83 90 padding-top: 0px;"><a target="_blank" style="text-align:center;border:0px solid #6200ee;border-radius:0px;color:white;font-weight:bold;background: #6200ee;"
84 91 class="button button-secondary" href="https://guaven.com/contact/solution-request/">Get Premium Support </a>
85 - <span style="line-height: 30px;padding: 0 5px;">OR</span>
92 + <span style="line-height: 30px;padding: 0 5px;">OR</span>
86 93 <a target="_blank" style="text-align:center;border:0px solid #26b286;border-radius:0px;color:white;font-weight:bold;background: #26b286;"
87 94 class="button button-secondary" href="https://guaven.com/service/small-thankyou-premium-support-service/">Make a Small Donation</a>
88 95 </div> </div>';
89 96 endif;
@@ -103,11 +110,13 @@
103 110 add_action('admin_notices', 'guaven_sqlcharts_onboarding_notice');
104 111
105 112 function guaven_sqlcharts_onboarding_notice_dismissed(){
106 113 check_ajax_referer('notice_dismissed', 'nonce');
114 +
115 + if(empty($_POST['type']))return;
107 116 switch ($_POST['type']){
108 117 case 'onboarding_notice':
109 - update_option('guaven_sqlcharts_onboarding_notice_dismissed', 1);
118 + update_option('guaven_sqlcharts_onboarding_notice_dismissed', 1);
110 119 break;
111 120 }
112 121 }
113 122 add_action('wp_ajax_guaven_sqlcharts_onboarding_notice_dismissed', 'guaven_sqlcharts_onboarding_notice_dismissed');
@@ -113,10 +122,12 @@
113 122 add_action('wp_ajax_guaven_sqlcharts_onboarding_notice_dismissed', 'guaven_sqlcharts_onboarding_notice_dismissed');
114 123
115 124 function guaven_sqlcharts_enqueue_chart()
116 125 {
117 - wp_enqueue_script('guaven_sqlcharts_chartjs', plugins_url('asset/bundle.min.js', __FILE__),array('jquery'),GVNSQLCHARTS_VERSION);
118 - wp_localize_script('guaven_sqlcharts_chartjs', 'guaven_sqlcharts_notice_dismissed', array(
126 + wp_enqueue_script('guaven_sqlcharts_chartjs', plugins_url('asset/chart.umd.min.js', __FILE__),array('jquery'),GVNSQLCHARTS_VERSION,false);
127 + wp_enqueue_script('guaven_sqlcharts_datepicker', plugins_url('asset/datepicker.min.js', __FILE__),array('jquery'),GVNSQLCHARTS_VERSION,false);
128 + wp_enqueue_script('guaven_sqlcharts_front', plugins_url('asset/front.js', __FILE__),array('jquery','guaven_sqlcharts_chartjs','guaven_sqlcharts_datepicker'),GVNSQLCHARTS_VERSION,false);
129 + wp_localize_script('guaven_sqlcharts_front', 'guaven_sqlcharts_notice_dismissed', array(
119 130 'action' => 'guaven_sqlcharts_onboarding_notice_dismissed',
120 131 'nonce' => wp_create_nonce('notice_dismissed')
121 132 ));
122 133
@@ -130,9 +141,52 @@
130 141 }
131 142 add_action('wp_enqueue_scripts', 'guaven_sqlcharts_enqueue_main_style');
132 143 add_action('admin_enqueue_scripts', 'guaven_sqlcharts_enqueue_main_style');
133 144
145 +// admin-only assets: SQL builder, autocomplete, new metabox UI
146 +function guaven_sqlcharts_admin_assets($hook)
147 +{
148 + if (!in_array($hook, array('post.php', 'post-new.php'))) return;
149 + $screen = function_exists('get_current_screen') ? get_current_screen() : null;
150 + if (empty($screen->post_type) or $screen->post_type != 'gvn_schart') return;
151 + if (!current_user_can('manage_options')) return;
134 152
153 + wp_enqueue_style('guaven_sqlcharts_admin_style', plugins_url('asset/admin.css', __FILE__), array(), GVNSQLCHARTS_VERSION);
154 + wp_enqueue_script('guaven_sqlcharts_admin', plugins_url('asset/admin.js', __FILE__), array('jquery'), GVNSQLCHARTS_VERSION, true);
155 +
156 + global $wpdb;
157 + $tables = $wpdb->get_col('SHOW TABLES');
158 + if (!is_array($tables)) $tables = array();
159 + wp_localize_script('guaven_sqlcharts_admin', 'gvnSqlBuilder', array(
160 + 'ajaxurl' => admin_url('admin-ajax.php'),
161 + 'nonce' => wp_create_nonce('gvnsql_schema'),
162 + 'tables' => array_values($tables),
163 + 'prefix' => $wpdb->prefix,
164 + ));
165 +}
166 +add_action('admin_enqueue_scripts', 'guaven_sqlcharts_admin_assets');
167 +
168 +// returns column names of one table for the live SQL builder/autocomplete
169 +function guaven_sqlcharts_ajax_columns()
170 +{
171 + check_ajax_referer('gvnsql_schema', 'nonce');
172 + if (!current_user_can('manage_options')) wp_send_json_error('forbidden', 403);
173 + global $wpdb;
174 + $table = isset($_POST['table']) ? sanitize_text_field(wp_unslash($_POST['table'])) : '';
175 + $tables = $wpdb->get_col('SHOW TABLES');
176 + if (!is_array($tables) or !in_array($table, $tables, true)) wp_send_json_error('unknown table', 400);
177 + $cols = $wpdb->get_results('SHOW COLUMNS FROM `' . str_replace('`', '', $table) . '`');
178 + $out = array();
179 + if (is_array($cols)) {
180 + foreach ($cols as $col) {
181 + $out[] = array('name' => $col->Field, 'type' => $col->Type);
182 + }
183 + }
184 + wp_send_json_success($out);
185 +}
186 +add_action('wp_ajax_gvnsql_get_columns', 'guaven_sqlcharts_ajax_columns');
187 +
188 +
135 189 function guaven_sqlcharts_isJson($string)
136 190 {
137 191 json_decode($string);
138 192 return (json_last_error() == JSON_ERROR_NONE);
@@ -140,17 +194,39 @@
140 194
141 195 add_action('init', 'guaven_sqlcharts_register_post');
142 196 function guaven_sqlcharts_register_post()
143 197 {
144 - //register_taxonomy('guaven_update_push_tag', 'termin');
145 198 register_post_type('gvn_schart', array(
146 199 'labels' => array(
147 - 'name' => __('My SQL Charts'),
148 - 'singular_name' => __('My SQL chart')
200 + 'name' => __('My SQL Charts','guaven_sqlcharts'),
201 + 'singular_name' => __('SQL Chart','guaven_sqlcharts'),
202 + 'menu_name' => __('My SQL Charts','guaven_sqlcharts'),
203 + 'add_new' => __('Add Chart','guaven_sqlcharts'),
204 + 'add_new_item' => __('Add New Chart','guaven_sqlcharts'),
205 + 'edit_item' => __('Edit Chart','guaven_sqlcharts'),
206 + 'new_item' => __('New Chart','guaven_sqlcharts'),
207 + 'view_item' => __('View Chart','guaven_sqlcharts'),
208 + 'view_items' => __('View Charts','guaven_sqlcharts'),
209 + 'search_items' => __('Search Charts','guaven_sqlcharts'),
210 + 'not_found' => __('No charts found','guaven_sqlcharts'),
211 + 'not_found_in_trash' => __('No charts found in Trash','guaven_sqlcharts'),
212 + 'all_items' => __('All Charts','guaven_sqlcharts'),
213 + 'archives' => __('Chart Archives','guaven_sqlcharts'),
214 + 'attributes' => __('Chart Attributes','guaven_sqlcharts'),
215 + 'insert_into_item' => __('Insert into chart','guaven_sqlcharts'),
216 + 'uploaded_to_this_item' => __('Uploaded to this chart','guaven_sqlcharts'),
217 + 'filter_items_list' => __('Filter charts list','guaven_sqlcharts'),
218 + 'items_list_navigation' => __('Charts list navigation','guaven_sqlcharts'),
219 + 'items_list' => __('Charts list','guaven_sqlcharts'),
220 + 'item_published' => __('Chart published.','guaven_sqlcharts'),
221 + 'item_published_privately' => __('Chart published privately.','guaven_sqlcharts'),
222 + 'item_reverted_to_draft' => __('Chart reverted to draft.','guaven_sqlcharts'),
223 + 'item_scheduled' => __('Chart scheduled.','guaven_sqlcharts'),
224 + 'item_updated' => __('Chart updated.','guaven_sqlcharts'),
149 225 ),
150 226
151 227 'public' => true,
152 - //'taxonomies' => array('guaven_update_push_tag'),
228 + 'menu_icon' => 'dashicons-chart-pie',
153 229 'supports' => array(
154 230 'title',
155 231 'postmeta'
156 232 ),
@@ -159,8 +235,14 @@
159 235
160 236 guaven_sqlcharts_load_defaults();
161 237 }
162 238
239 +// "Add title" placeholder on the chart edit screen
240 +add_filter('enter_title_here', function ($title, $post) {
241 + if (!empty($post) and $post->post_type == 'gvn_schart') return __('Chart name', 'guaven_sqlcharts');
242 + return $title;
243 +}, 10, 2);
244 +
163 245 add_action('admin_footer', 'guaven_sqlcharts_admin_front');
164 246
165 247
166 248 function guaven_sqlcharts_admin_front()
@@ -175,9 +257,9 @@
175 257
176 258 // metabox for editor
177 259 function guaven_sqlcharts_metabox_area()
178 260 {
179 - add_meta_box('guaven_sqlcharts_metabox', 'Configure your graph chart', 'guaven_sqlcharts_metabox', 'gvn_schart', 'advanced', 'default');
261 + add_meta_box('guaven_sqlcharts_metabox', 'Chart Builder', 'guaven_sqlcharts_metabox', 'gvn_schart', 'advanced', 'default');
180 262 }
181 263
182 264 function guaven_sqlcharts_metabox()
183 265 {
@@ -183,25 +265,127 @@
183 265 {
184 266 require_once(dirname(__FILE__) . "/admin_metabox.php");
185 267 }
186 268
269 +/**
270 + * Catalog of all supported chart types: label, group, per-type usage guide and example query.
271 + * Used by the admin UI to render the type cards and the contextual guides.
272 + */
273 +function guaven_sqlcharts_type_catalog()
274 +{
275 + global $wpdb;
276 + $p = $wpdb->posts;
277 + $u = $wpdb->users;
278 + return array(
279 + 'pie_l' => array(
280 + 'label' => 'Pie',
281 + 'group' => 'Circular',
282 + 'guide' => 'Best for showing how a total splits into a few parts (shares/percentages). Use one query that returns a label column (X) and a numeric value column (Y). Keep it under ~8 slices for readability.',
283 + 'example_sql' => "select count(*) postcount, SUBSTR(post_date,1,4) yearnum from $p group by yearnum order by yearnum asc limit 10",
284 + 'example_x' => 'yearnum', 'example_y' => 'postcount',
285 + ),
286 + 'donut_l' => array(
287 + 'label' => 'Doughnut',
288 + 'group' => 'Circular',
289 + 'guide' => 'Same as Pie but with a hole in the middle — slightly easier to compare slice sizes. One query: label column (X) + numeric column (Y).',
290 + 'example_sql' => "select count(*) postcount, post_type from $p group by post_type order by postcount desc limit 8",
291 + 'example_x' => 'post_type', 'example_y' => 'postcount',
292 + ),
293 + 'polar_l' => array(
294 + 'label' => 'Polar Area',
295 + 'group' => 'Circular',
296 + 'guide' => 'Like a pie, but every slice has the same angle and the value controls the radius. Good for cyclic data (months, weekdays). One query: label (X) + numeric value (Y).',
297 + 'example_sql' => "select count(*) postcount, SUBSTRING(post_date,6,2) monthnum from $p group by monthnum order by monthnum",
298 + 'example_x' => 'monthnum', 'example_y' => 'postcount',
299 + ),
300 + 'radar_l' => array(
301 + 'label' => 'Radar',
302 + 'group' => 'Circular',
303 + 'guide' => 'Compares one or more series across several categories arranged in a circle. Great for profiles/ratings. Use one query per series, separated with ";".',
304 + 'example_sql' => "select count(*) postcount, SUBSTRING(post_date,6,2) monthnum from $p where post_type=\"post\" group by monthnum order by monthnum",
305 + 'example_x' => 'monthnum', 'example_y' => 'postcount',
306 + ),
307 + 'line_l' => array(
308 + 'label' => 'Line',
309 + 'group' => 'Line',
310 + 'guide' => 'The classic choice for trends over time (per day/month/year). X should be an ordered value like a date. Add more queries separated with ";" for comparison lines.',
311 + 'example_sql' => "select count(*) postcount, SUBSTRING(post_date,1,7) monthandyear from $p group by monthandyear order by monthandyear",
312 + 'example_x' => 'monthandyear', 'example_y' => 'postcount',
313 + ),
314 + 'area_l' => array(
315 + 'label' => 'Area',
316 + 'group' => 'Line',
317 + 'guide' => 'A line chart with the region under the line filled — emphasizes volume/magnitude of a trend. Works well with 2 queries (";" separated) to compare filled regions.',
318 + 'example_sql' => "select count(*) postcount, SUBSTRING(post_date,1,7) monthandyear from $p where post_type=\"post\" group by monthandyear order by monthandyear;\nselect count(*) postcount, SUBSTRING(post_date,1,7) monthandyear from $p where post_type!=\"post\" group by monthandyear order by monthandyear",
319 + 'example_x' => 'monthandyear', 'example_y' => 'Posts;Other content',
320 + ),
321 + 'steppedline_l' => array(
322 + 'label' => 'Stepped Line',
323 + 'group' => 'Line',
324 + 'guide' => 'Line chart that moves in steps instead of slopes — perfect for values that change at discrete moments (prices, stock level, settings history).',
325 + 'example_sql' => "select count(*) postcount, SUBSTRING(post_date,1,4) yearnum from $p group by yearnum order by yearnum",
326 + 'example_x' => 'yearnum', 'example_y' => 'postcount',
327 + ),
328 + 'bar_l' => array(
329 + 'label' => 'Bar',
330 + 'group' => 'Bar',
331 + 'guide' => 'Compares values across categories with vertical bars. One query: category (X) + numeric value (Y). Multiple ";" separated queries become grouped/stacked bars.',
332 + 'example_sql' => "select count(*) postcount, SUBSTRING(post_date,1,7) monthandyear from $p group by monthandyear order by monthandyear",
333 + 'example_x' => 'monthandyear', 'example_y' => 'postcount',
334 + ),
335 + 'horizontalbar_l' => array(
336 + 'label' => 'Horizontal Bar',
337 + 'group' => 'Bar',
338 + 'guide' => 'Bar chart rotated 90° — the best pick when category names are long (user names, product titles).',
339 + 'example_sql' => "select count(*) as postcount, b.display_name as dname from $p a inner join $u b ON a.post_author=b.ID where a.post_status=\"publish\" group by a.post_author order by postcount desc limit 10",
340 + 'example_x' => 'dname', 'example_y' => 'postcount',
341 + ),
342 + 'stackedbar_l' => array(
343 + 'label' => 'Stacked Bar',
344 + 'group' => 'Bar',
345 + 'guide' => 'Shows how each category total is composed of parts. Use 2+ queries separated with ";" — each query becomes one segment color of the stack.',
346 + 'example_sql' => "select count(*) postcount, SUBSTRING(post_date,1,4) yearnum from $p where post_type=\"post\" group by yearnum order by yearnum;\nselect count(*) postcount, SUBSTRING(post_date,1,4) yearnum from $p where post_type!=\"post\" group by yearnum order by yearnum",
347 + 'example_x' => 'yearnum', 'example_y' => 'Posts;Other content',
348 + ),
349 + 'scatter_l' => array(
350 + 'label' => 'Scatter',
351 + 'group' => 'Other',
352 + 'guide' => 'Plots points on numeric X/Y axes to reveal correlation between two numbers. Both X and Y columns must be numeric (e.g. comment_count vs menu_order).',
353 + 'example_sql' => "select comment_count ccount, ID from $p where post_status=\"publish\" order by ID limit 100",
354 + 'example_x' => 'ID', 'example_y' => 'ccount',
355 + ),
356 + );
357 +}
358 +
359 +/**
360 + * Maps deprecated Google-Chart era type slugs to their Chart.js equivalents.
361 + * Old charts keep working without any manual migration; when the post is re-saved
362 + * from the new UI the normalized value is stored automatically.
363 + */
364 +function guaven_sqlcharts_normalize_type($type)
365 +{
366 + $map = array(
367 + 'pie' => 'pie_l',
368 + '3dpie' => 'pie_l',
369 + 'column' => 'bar_l',
370 + 'bar' => 'horizontalbar_l',
371 + 'area' => 'area_l',
372 + );
373 + return isset($map[$type]) ? $map[$type] : $type;
374 +}
375 +
187 376 function guaven_gutenberg_wrapper($atts){
188 377 if(isset($atts['sqlcharts_inserted_script'])){
189 378 global $sqlcharts_inserted_script;
190 379 $sqlcharts_inserted_script = $atts['sqlcharts_inserted_script'];
191 380 }
192 -
193 - // if( isset($_GET["post_id"],$_GET["context"]) and $_GET["context"]=='edit'
194 - // and strpos($_SERVER["REQUEST_URI"],'block-renderer/guaven-sqlcharts/gvn-chart-gutenberg')!==false
195 - // ){
196 - // $atts['chart_id']=$_GET["post_id"];
197 - // }
381 +
198 382 $post = get_post($atts['chart_id']);
199 383 if( ! isset($atts['chart_id']) or !isset($post) or $post->post_type != 'gvn_schart'){
200 -
384 +
201 385 return "Invalid id";
202 386 }
203 -
387 +
204 388 return guaven_sqlcharts_local_shortcode(array('id' => $atts['chart_id'])); // temporary explicit value
205 389 }
206 390 function guaven_register_gutenberg_blocks()
207 391 {
@@ -208,14 +392,15 @@
208 392 wp_register_script(
209 393 'gvn_gutenberg_charts',
210 394 plugins_url( 'asset/guaven_gutenberg_charts.js', __FILE__ ),
211 395 array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-server-side-render' ),
212 - GVNSQLCHARTS_VERSION.'_'.filemtime( plugin_dir_path( __FILE__ ) . 'asset/guaven_gutenberg_charts.js' )
396 + GVNSQLCHARTS_VERSION.'_'.filemtime( plugin_dir_path( __FILE__ ) . 'asset/guaven_gutenberg_charts.js'),
397 + false
213 398 );
214 399 wp_localize_script('gvn_gutenberg_charts', 'guaven', array(
215 400 'description' => 'Add My SQL Chart to your post',
216 401 ));
217 -
402 +
218 403 register_block_type( 'guaven-sqlcharts/gvn-chart-gutenberg', array(
219 404 'editor_script' => 'gvn_gutenberg_charts',
220 405 'render_callback' => 'guaven_gutenberg_wrapper',
221 406 'attributes' => array(
@@ -259,9 +444,10 @@
259 444 "guaven_sqlcharts_begin_with_0_y",
260 445 "guaven_sqlcharts_round_y_values",
261 446 "guaven_sqlcharts_legend_position",
262 447 "guaven_sqlcharts_nostacked",
263 - "guaven_sqlcharts_forcetooltips"
448 + "guaven_sqlcharts_forcetooltips",
449 + "guaven_sqlcharts_timeaxis"
264 450 );
265 451 foreach ($fields as $key => $value) {
266 452 if(isset($_POST[$value]))$newval=esc_attr($_POST[$value]);
267 453 else $newval='';
@@ -271,10 +457,26 @@
271 457 if(!empty($_POST["guaven_sqlcharts_dbpass"])){
272 458 $encpass=guaven_sqlcharts_encrypt_decrypt('encrypt',$_POST["guaven_sqlcharts_dbpass"]);
273 459 update_post_meta($post->ID, 'guaven_sqlcharts_dbpass', ['encrypted',$encpass]);
274 460 }
275 - update_post_meta($post->ID, 'guaven_sqlcharts_code', esc_attr(str_replace("'",'"',stripslashes($_POST['guaven_sqlcharts_code']))) );
461 + // Store the SQL as typed. Do not HTML-encode it and do not rewrite quotes:
462 + // the editor escapes it on output and the front end decodes entities before running it.
463 + $sql_code = isset($_POST['guaven_sqlcharts_code']) ? wp_check_invalid_utf8(wp_unslash($_POST['guaven_sqlcharts_code'])) : '';
464 + update_post_meta($post->ID, 'guaven_sqlcharts_code', $sql_code);
465 + // Flag that this chart stores raw SQL. Charts without the flag were saved by
466 + // versions before 3.0.1, which HTML-encoded the query, and still need decoding.
467 + update_post_meta($post->ID, 'guaven_sqlcharts_code_raw', 1);
276 468 }
469 +
470 +// Returns the stored SQL query exactly as the user typed it.
471 +function guaven_sqlcharts_get_code($post_id)
472 +{
473 + $sql = get_post_meta($post_id, 'guaven_sqlcharts_code', true);
474 + if (get_post_meta($post_id, 'guaven_sqlcharts_code_raw', true) != 1) {
475 + $sql = html_entity_decode($sql, ENT_QUOTES, 'UTF-8');
476 + }
477 + return $sql;
478 +}
277 479 add_action('save_post', 'guaven_sqlcharts_save_metabox_area', 1, 2);
278 480 // save the custom fields
279 481
280 482
@@ -280,22 +482,11 @@
280 482
281 483
282 484 function gvn_chart_check_sql_query($sql)
283 485 {
284 - $blacklister = array(
285 - "delete",
286 - "update",
287 - "insert",
288 - "drop",
289 - "truncate",
290 - "alter"
291 - ); //add all
292 - $blacklister_f = 0;
293 - foreach ($blacklister as $key => $value) {
294 - if (strpos($sql, $value) !== false)
295 - $blacklister_f = 1;
296 - }
297 - return $blacklister_f;
486 + // case-insensitive, word-boundary check: only read-only SELECT queries are allowed
487 + $pattern = '/\b(delete|update|insert|replace|drop|truncate|alter|create|rename|grant|revoke|call|handler|load\s+data|load_file|outfile|dumpfile)\b/i';
488 + return preg_match($pattern, $sql) ? 1 : 0;
298 489 }
299 490
300 491 function guaven_get_labels_and_values($id, $fvs)
301 492 {
@@ -301,19 +492,13 @@
301 492 {
302 493 $values = array();
303 494 $labels = array();
304 495 $xarg_s = get_post_meta($id, 'guaven_sqlcharts_xarg_s', true);
305 - $xarg_l = get_post_meta($id, 'guaven_sqlcharts_xarg_l', true);
306 496 $yarg_s = get_post_meta($id, 'guaven_sqlcharts_yarg_s', true);
307 - $yarg_l = get_post_meta($id, 'guaven_sqlcharts_yarg_l', true);
308 - $chartype = array(
309 - 'line_l' => 'Line',
310 - 'pie_l' => 'Pie',
311 - 'donut_l' => 'Pie',
312 - 'bar_l' => 'Bar',
313 - 'horizontalbar_l' => 'Horizontal Bar',
314 - 'area_l' => 'Line'
315 - );
497 + // labels are saved through esc_attr, so "&" is stored as "&amp;"; decode before splitting on ";"
498 + // or the entity's own ";" would be taken as a series separator
499 + $xarg_l = html_entity_decode((string) get_post_meta($id, 'guaven_sqlcharts_xarg_l', true), ENT_QUOTES, 'UTF-8');
500 + $yarg_l = html_entity_decode((string) get_post_meta($id, 'guaven_sqlcharts_yarg_l', true), ENT_QUOTES, 'UTF-8');
316 501 foreach ($fvs as $key => $value) {
317 502 $values[$value->$xarg_s] = $value->$yarg_s;
318 503 $labels[$value->$xarg_s] = '"' . $value->$xarg_s . '"';
319 504 }
@@ -327,29 +512,48 @@
327 512
328 513 function guaven_sqlcharts_print_chart_js($print_data)
329 514 {
330 515 extract($print_data);
516 + $tip_g = guaven_sqlcharts_normalize_type($tip_g);
331 517
332 - if ($tip_g == 'line_l') {
333 - guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, 'false', $pid);
518 + switch ($tip_g) {
519 + case 'line_l':
520 + guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, 'false', $pid);
521 + break;
522 + case 'area_l':
523 + guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, 'true', $pid);
524 + break;
525 + case 'steppedline_l':
526 + guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, 'false', $pid, 'line', true);
527 + break;
528 + case 'radar_l':
529 + guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, 'radarfill', $pid, 'radar');
530 + break;
531 + case 'pie_l':
532 + guaven_sqlcharts_piedata($title, $labels, $values, $ylabel, $pid);
533 + break;
534 + case 'donut_l':
535 + guaven_sqlcharts_piedata($title, $labels, $values, $ylabel, $pid, 'doughnut');
536 + break;
537 + case 'polar_l':
538 + guaven_sqlcharts_piedata($title, $labels, $values, $ylabel, $pid, 'polarArea');
539 + break;
540 + case 'bar_l':
541 + guaven_sqlcharts_bardata($title, $labels, $values, $ylabel, 'bar', $pid);
542 + break;
543 + case 'horizontalbar_l':
544 + guaven_sqlcharts_bardata($title, $labels, $values, $ylabel, 'horizontalBar', $pid);
545 + break;
546 + case 'stackedbar_l':
547 + guaven_sqlcharts_bardata($title, $labels, $values, $ylabel, 'stackedBar', $pid);
548 + break;
549 + case 'scatter_l':
550 + guaven_sqlcharts_scatterdata($title, $labels, $values, $ylabel, $pid);
551 + break;
552 + case 'custom':
553 + guaven_sqlcharts_custom($title, $labels, $values, $ylabel, $pid);
554 + break;
334 555 }
335 - if ($tip_g == 'area_l') {
336 - guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, 'true', $pid);
337 - } elseif ($tip_g == 'pie_l') {
338 - guaven_sqlcharts_piedata($title, $labels, $values, $ylabel, $pid);
339 - } elseif ($tip_g == 'donut_l') {
340 - guaven_sqlcharts_piedata($title, $labels, $values, $ylabel, $pid, 'doughnut');
341 - } elseif ($tip_g == 'bar_l') {
342 - guaven_sqlcharts_bardata($title, $labels, $values, $ylabel, 'bar', $pid);
343 - } elseif ($tip_g == 'horizontalbar_l') {
344 - guaven_sqlcharts_bardata($title, $labels, $values, $ylabel, 'horizontalBar', $pid);
345 - }
346 - elseif($tip_g == 'custom'){
347 - guaven_sqlcharts_custom($title, $labels, $values, $ylabel, $pid);
348 - }
349 - elseif ($tip_g == 'polar_l') {
350 - guaven_sqlcharts_piedata($title, $labels, $values, $ylabel, $pid, 'polarArea');
351 - }
352 556 }
353 557
354 558 function guaven_sqlcharts_custom($title, $labels, $values, $ylabel, $pid){
355 559 do_action('guaven_sqlcharts_custom',$title, $labels, $values, $ylabel, $pid);
@@ -374,10 +578,23 @@
374 578 foreach($variables_arr as $varfield){
375 579 $varfield_arr=explode("~",$varfield);
376 580 if (count($varfield_arr)<3) continue;
377 581 $varfield_arr=array_map("trim",$varfield_arr);
378 - if (!empty($_GET[$varfield_arr[0]])) $varreplacement=$_GET[$varfield_arr[0]]; else $varreplacement=$varfield_arr[1];
379 - if (!is_numeric($varreplacement) and strpos($varreplacement,'()')===false) $varreplacement='"'.$varreplacement.'"';
582 + if (!empty($_GET[$varfield_arr[0]])) {
583 + // User-supplied input: no () bypass allowed — sanitize strictly
584 + $varreplacement = sanitize_text_field(wp_unslash($_GET[$varfield_arr[0]]));
585 + if (is_numeric($varreplacement)) {
586 + $varreplacement = $varreplacement + 0;
587 + } else {
588 + $varreplacement = '"' . esc_sql($varreplacement) . '"';
589 + }
590 + } else {
591 + // Admin-configured default value: allow () for SQL functions (e.g. NOW())
592 + $varreplacement = $varfield_arr[1];
593 + if (!is_numeric($varreplacement) && strpos($varreplacement,'()')===false) {
594 + $varreplacement = '"' . esc_sql($varreplacement) . '"';
595 + }
596 + }
380 597
381 598 $sql_initial=str_replace('{'.$varfield_arr[0].'}',$varreplacement,$sql_initial);
382 599 }
383 600 return $sql_initial;
@@ -395,38 +612,59 @@
395 612 $gvalue=!empty($_GET[$vrow_arr[0]])?esc_attr(urldecode($_GET[$vrow_arr[0]])):'';
396 613 $dvalue=(strpos($vrow_arr[1],'()')===false)?esc_attr($vrow_arr[1]):'';
397 614 if ($vrow_arr[3]=='date') {
398 615 $dateexists=true;
399 - $topform.= $vrow_arr[2].' <input autocomplete="off" style="max-width:210px" type="text"
616 + $topform.= '<span class="gvn-filter-field"><label>'.$vrow_arr[2].'</label> <input class="gws_datepicker" autocomplete="off" type="text"
400 617 value="'.$gvalue.'"
401 - data-toggle="datepicker" name="'.$vrow_arr[0].'" placeholder="'.$dvalue.'">
618 + data-toggle="datepicker" name="'.$vrow_arr[0].'" placeholder="'.$dvalue.'"></span>
402 619 ';}
403 620 else {
404 - $topform.= $vrow_arr[2].' <input autocomplete="off" style="max-width:210px;'.($vrow_arr[3]=='number'?'width:100px;':'').'"
621 + $topform.= '<span class="gvn-filter-field"><label>'.$vrow_arr[2].'</label> <input autocomplete="off"
405 622 type="'.$vrow_arr[3].'"
406 - value="'.$gvalue.'" name="'.$vrow_arr[0].'" placeholder="'.$dvalue.'">
623 + value="'.$gvalue.'" name="'.$vrow_arr[0].'" placeholder="'.$dvalue.'"></span>
407 624 ';
408 625 }
409 626 }
410 627 if (!empty($topform)) {
411 - $topform='<form method="get" action="" class="guaven_sqlcharts_form">'.$topform.'
412 - <input type="submit"
413 - value="'.(get_post_meta($atts['id'], 'guaven_sqlcharts_formpartbutton', true)!=''?esc_attr(get_post_meta($atts['id'], 'guaven_sqlcharts_formpartbutton', true)):'OK').'"></form>';
414 - if ($dateexists) $topform.='<script>setTimeout(function(){jQuery(\'[data-toggle="datepicker"]\').datepicker({format: \'yyyy-mm-dd\'});
415 -},300);</script>';
416 - return $topform ;
628 + $allowed_html = array(
629 + 'form' => array(
630 + 'method' => array(),
631 + 'action' => array(),
632 + 'class' => array()
633 + ),
634 + 'input' => array(
635 + 'type' => array(),
636 + 'value' => array(),
637 + 'name' => array(),
638 + 'class' => array(),
639 + 'data-toggle'=>array(),
640 + 'placeholder'=>array(),
641 + 'autocomplete'=>array(),
642 + 'style'=>[]
643 + ),
644 + 'span' => array('class' => array()),
645 + 'label' => array(),
646 + );
647 +
648 + $submit_button_value = get_post_meta($atts['id'], 'guaven_sqlcharts_formpartbutton', true) != ''
649 + ? esc_attr(get_post_meta($atts['id'], 'guaven_sqlcharts_formpartbutton', true))
650 + : 'OK';
651 +
652 + $topform = '<form method="get" action="" class="guaven_sqlcharts_form">' . $topform . '
653 + <input type="submit" value="' . $submit_button_value . '"></form>';
654 +
655 + echo wp_kses($topform, $allowed_html);
417 656 }
418 - return;
419 657 }
420 658
421 659
422 -function guaven_sqlcharts_encrypt_decrypt($action, $string)
660 +function guaven_sqlcharts_encrypt_decrypt($action, $string)
423 661 {
424 662 $output = false;
425 663 $encrypt_method = "AES-256-CBC";
426 664 $secret_key = 'GWSCHARTPL2022.2016.';
427 665 $secret_iv = 'GWSCHARTPL2016.2022';
428 - $key = hash('sha256', $secret_key);
666 + $key = hash('sha256', $secret_key);
429 667 $iv = substr(hash('sha256', $secret_iv), 0, 16);
430 668 if ( $action == 'encrypt' ) {
431 669 $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
432 670 $output = base64_encode($output);
@@ -437,8 +675,9 @@
437 675 }
438 676
439 677 function guaven_sqlcharts_local_shortcode($atts) {
440 678 if(empty($atts['id']))return 'ID is missing.';
679 + $atts['id']=intval($atts['id']);
441 680 $remote_host=get_post_meta($atts['id'], 'guaven_sqlcharts_dbhost', true);
442 681 if ($remote_host!=''){
443 682 $remote_db=get_post_meta($atts['id'], 'guaven_sqlcharts_dbname', true);
444 683 $remote_login=get_post_meta($atts['id'], 'guaven_sqlcharts_dblogin', true);
@@ -453,9 +692,9 @@
453 692 }
454 693
455 694 $GLOBALS["guaven_sqlcharts_atts"]=$atts;
456 695
457 - $sql = html_entity_decode(get_post_meta($atts['id'], 'guaven_sqlcharts_code', true));
696 + $sql = guaven_sqlcharts_get_code($atts['id']);
458 697 if(empty($sql))return 'SQL query is missing.';
459 698 $sql=gvn_chart_put_variables($sql,$atts['id']);
460 699
461 700
@@ -462,17 +701,30 @@
462 701 $sql=apply_filters('guaven_sqlcharts_rendered_sql',$sql,$atts);
463 702
464 703 $blacklister_f = gvn_chart_check_sql_query($sql);
465 704 if ($blacklister_f == 1)return 'You given SQL code contains forbidden commands. Remember that you should only use SELECT queries';
466 - $tip_g = get_post_meta($atts['id'], 'guaven_sqlcharts_graphtype', true);
705 + $tip_g = guaven_sqlcharts_normalize_type(get_post_meta($atts['id'], 'guaven_sqlcharts_graphtype', true));
467 706
468 - for($i=1;$i<20;$i++){ $replacearg=!empty($atts["arg".$i])?$atts["arg".$i]:0;
469 - $sql=str_replace("{arg".$i."}",esc_sql($replacearg),$sql);}
707 + // {arg1}..{arg19} come from shortcode attributes: [gvn_schart_2 id="1" arg1="41"].
708 + // Substituted directly (not via wpdb::prepare) so the same tag may appear any number of times,
709 + // e.g. in every query of a ";"-separated comparison chart. Numbers are inserted as-is, anything
710 + // else is escaped and quoted; a tag already wrapped in quotes ('{arg1}') is not double-quoted.
711 + for($i=1;$i<20;$i++){
712 + $tag = '{arg'.$i.'}';
713 + if (strpos($sql, $tag) === false) continue;
714 + $replacearg = !empty($atts['arg'.$i]) ? $atts['arg'.$i] : 0;
715 + if (is_numeric($replacearg)) $replacearg = $replacearg + 0;
716 + else $replacearg = "'" . esc_sql($replacearg) . "'";
717 + $sql = str_replace(array("'".$tag."'", '"'.$tag.'"', $tag), $replacearg, $sql);
718 + }
719 +
470 720 $sql_split = explode(';', $sql);
471 721 $labels_and_values = array();
722 + $labels = $values = $ylabel = $xlabel = array();
472 723 $post_g = get_post($atts['id']);
473 724
474 725 global $sqlcharts_inserted_script;
726 + ob_start();
475 727 for ($i = 0; $i < count($sql_split); $i++) {
476 728 if (!empty($sql_split[$i])) {
477 729
478 730 $fvs = $wpdb->get_results($sql_split[$i]);
@@ -480,16 +732,18 @@
480 732 $wpdb->show_errors();
481 733 ob_start();
482 734 $wpdb->print_error();
483 735 $printerror = ob_get_clean();
484 - if ($printerror != '' and strpos($printerror, "[]") === false)
736 + if ($printerror != '' and strpos($printerror, "[]") === false){
737 + ob_end_clean();
485 738 return $printerror;
486 - elseif (empty($fvs))
739 + }
740 + elseif (empty($fvs)){
741 + ob_end_clean();
487 742 return 'Your SQL returnes empty data, please recheck your SQL query above';
743 + }
488 744 }
489 745
490 - ob_start();
491 -
492 746 if (empty($sqlcharts_inserted_script))
493 747 $sqlcharts_inserted_script = 1;
494 748 $labels_and_values[$i] = guaven_get_labels_and_values($atts['id'], $fvs);
495 749 $labels[$i] = $labels_and_values[$i][0];
@@ -498,23 +752,31 @@
498 752 $xlabel[$i] = !empty($labels_and_values[$i][3][$i]) ? $labels_and_values[$i][3][$i] : '';
499 753 }
500 754 }
501 755
502 - echo gvn_chart_top_form($atts);
756 + gvn_chart_top_form($atts);
757 +
758 + // shortcode width/height attributes override the saved defaults
759 + $chart_w = !empty($atts['width']) ? $atts['width'] : get_post_meta($atts['id'], 'guaven_sqlcharts_chartwidth', true);
760 + $chart_h = !empty($atts['height']) ? $atts['height'] : get_post_meta($atts['id'], 'guaven_sqlcharts_chartheight', true);
761 + $wrap_style = '';
762 + if ($chart_w != '') $wrap_style .= 'max-width:' . (int) $chart_w . 'px;';
763 + if ($chart_h != '') $wrap_style .= 'height:' . (int) $chart_h . 'px;';
503 764 ?>
504 - <canvas
505 - id="ct-chart_<?php echo $sqlcharts_inserted_script; ?>"
765 + <div class="gvn-chartwrap"<?php echo $wrap_style != '' ? ' style="' . esc_attr($wrap_style) . '"' : ''; ?>>
766 + <canvas
767 + id="ct-chart_<?php echo esc_attr($sqlcharts_inserted_script); ?>"
506 768 class="guaven_chart_canvas"
507 - style="width: <?php echo get_post_meta($atts['id'], 'guaven_sqlcharts_chartwidth', true); ?>px !important; height: <?php echo get_post_meta($atts['id'], 'guaven_sqlcharts_chartheight', true); ?>px !important;"
508 769 ></canvas>
770 + </div>
509 771
510 772 <script type="text/javascript" class="gvn_charts_script" async>
511 773 var ctx = jQuery("#ct-chart_<?php
512 - echo $sqlcharts_inserted_script;
774 + echo esc_attr($sqlcharts_inserted_script);
513 775 ?>");
514 776
515 777 <?php
516 - $print_data=apply_filters('guaven_sqlcharts_pre_print_vars',['tip_g'=>$tip_g, 'title'=>$post_g->post_title,
778 + $print_data=apply_filters('guaven_sqlcharts_pre_print_vars',['tip_g'=>$tip_g, 'title'=>$post_g->post_title,
517 779 'labels'=>$labels, 'values'=>$values, 'ylabel'=>$ylabel, 'pid'=>$atts['id']]);
518 780 guaven_sqlcharts_print_chart_js($print_data);
519 781 ?>
520 782 </script>
@@ -519,25 +781,49 @@
519 781 ?>
520 782 </script>
521 783
522 784 <?php
523 - if (!empty($atts["table"])) echo guaven_sqlcharts_tablepart($post_g->post_title, $labels, $values, $ylabel,$xlabel);
785 + if (!empty($atts["table"])) guaven_sqlcharts_tablepart($post_g->post_title, $labels, $values, $ylabel,$xlabel);
524 786 $sqlcharts_inserted_script++;
525 - return ob_get_clean();
787 + $ret=ob_get_clean();
788 + $ret=apply_filters( 'guaven_sqlcharts_final_output', $ret, $atts );
789 + return $ret;
526 790 }
527 791
528 792 add_shortcode('gvn_schart_2', 'guaven_sqlcharts_local_shortcode');
529 793
794 +// legacy alias: old Google-Chart era posts produced [gvn_schart id=".."] shortcodes
795 +if (!shortcode_exists('gvn_schart')) {
796 + add_shortcode('gvn_schart', 'guaven_sqlcharts_local_shortcode');
797 +}
798 +
799 +// [gvn_schart_2_cached id="1" expire="3600" arg1=".."] – same as gvn_schart_2 but the output is kept in a
800 +// transient. All other attributes (argN, width, height, table, params) are passed through, and each
801 +// distinct set of attributes gets its own cache entry. Append ?force_sql_cache_reload to the URL to bypass.
530 802 add_shortcode("gvn_schart_2_cached",function($atts){
531 803 if(empty($atts["id"]))return;
804 + $atts["id"]=intval($atts["id"]);
805 + $is_logged_in=is_user_logged_in()?'':'_guest';
532 806 $expire=!empty($atts["expire"])?intval($atts["expire"]):3600;
533 - $cached=get_transient('cached_sql_charts_'.$atts["id"]);
807 + $inner_atts=$atts;
808 + unset($inner_atts['expire']);
809 + $key='cached_sql_charts_'.$atts["id"].$is_logged_in;
810 + if (count($inner_atts) > 1) $key .= '_'.md5(serialize($inner_atts));
811 + $cached=get_transient($key);
534 812 if(!empty($cached) and !isset($_GET["force_sql_cache_reload"]) )return $cached;
535 - $tobecached=do_shortcode('[gvn_schart_2 id="'.$atts["id"].'"]');
536 - set_transient('cached_sql_charts_'.$atts["id"], $tobecached,$expire);//you can change 3600 yourself
813 + $tobecached=guaven_sqlcharts_local_shortcode($inner_atts);
814 + set_transient($key, $tobecached,$expire);
537 815 return $tobecached;
538 816 });
539 817
818 +// fixed, colorblind-friendly default palette (Tableau 10) used when no custom colors are set
819 +function guaven_sqlcharts_default_palette(){
820 + return apply_filters('guaven_sqlcharts_default_palette', array(
821 + '#4E79A7', '#F28E2B', '#E15759', '#76B7B2', '#59A14F',
822 + '#EDC948', '#B07AA1', '#FF9DA7', '#9C755F', '#BAB0AC'
823 + ));
824 +}
825 +
540 826 function guaven_sqlcharts_colors($index, $pid = null){
541 827 if(!isset($pid)) {
542 828 global $post;
543 829 $pid = $post->ID;
@@ -543,31 +829,89 @@
543 829 $pid = $post->ID;
544 830 }
545 831 $colors=get_post_meta($pid,'guaven_sqlcharts_colors',true);
546 832 $colors=explode(",",$colors);
547 - //var_dump($colors,$index,$colors[$index]);
548 833 if (!empty($colors[$index])) return $colors[$index];
549 - return rand(0, 255) . ',' . rand(0, 255) . ',' . rand(0, 255);
834 + $palette = guaven_sqlcharts_default_palette();
835 + return $palette[$index % count($palette)];
550 836 }
551 837
552 -function guaven_sqlcharts_toshowlegend($pid){
553 - $guaven_sqlcharts_legend_position=get_post_meta($pid, 'guaven_sqlcharts_legend_position', true);
554 - if(in_array($guaven_sqlcharts_legend_position,['top','bottom','left','right'])){
555 - $display='true';$position=$guaven_sqlcharts_legend_position;
838 +// outputs 'maintainAspectRatio:false,' when an explicit height is set, so the
839 +// chart fills its sized wrapper instead of keeping the default aspect ratio
840 +function guaven_sqlcharts_mar($pid){
841 + $atts = isset($GLOBALS["guaven_sqlcharts_atts"]) ? $GLOBALS["guaven_sqlcharts_atts"] : array();
842 + $h = !empty($atts['height']) ? $atts['height'] : get_post_meta($pid, 'guaven_sqlcharts_chartheight', true);
843 + return $h != '' ? 'maintainAspectRatio: false,' : '';
844 +}
845 +
846 +// outputs 'showAllTooltips: true,' when "Value labels" is checked; the values are drawn by the
847 +// gvnShowAllValues plugin in asset/front.js (works for every chart type)
848 +function guaven_sqlcharts_value_labels($pid){
849 + return get_post_meta($pid, 'guaven_sqlcharts_forcetooltips', true) != '' ? 'showAllTooltips: true,' : '';
850 +}
851 +
852 +// Chart.js scale title block built from the "X axis label" / "Y axis label" fields.
853 +// $which is 'x' or 'y' (the *field* to use, not the scale). The Y label is only used as an axis
854 +// title for single-series charts; with several ";"-separated series the legend names them instead.
855 +function guaven_sqlcharts_axis_title($pid, $which){
856 + $key = $which == 'x' ? 'guaven_sqlcharts_xarg_l' : 'guaven_sqlcharts_yarg_l';
857 + $text = trim(html_entity_decode((string) get_post_meta($pid, $key, true), ENT_QUOTES, 'UTF-8'));
858 + if ($text === '' or ($which == 'y' and strpos($text, ';') !== false)) return '';
859 + return 'title: {display: true, text: ' . wp_json_encode($text) . '},';
860 +}
861 +
862 +// dataset label as a safe JS string literal (labels saved before 3.0.1 may hold HTML entities)
863 +function guaven_sqlcharts_js_label($label){
864 + return wp_json_encode(html_entity_decode((string) $label, ENT_QUOTES, 'UTF-8'));
865 +}
866 +
867 +// Parses an X value for the "time axis" option. Accepts YYYY, YYYY-MM, YYYY-MM-DD, optionally followed
868 +// by HH:MM or HH:MM:SS. Returns a UTC timestamp in milliseconds, or false when the value is not a date.
869 +function guaven_sqlcharts_parse_date($str){
870 + $str = trim((string) $str);
871 + if (!preg_match('/^(\d{4})(?:-(\d{1,2})(?:-(\d{1,2})(?:[ T](\d{1,2}):(\d{2})(?::(\d{2}))?)?)?)?$/', $str, $m)) return false;
872 + $y = (int) $m[1]; $mo = isset($m[2]) ? (int) $m[2] : 1; $d = isset($m[3]) ? (int) $m[3] : 1;
873 + $h = isset($m[4]) ? (int) $m[4] : 0; $mi = isset($m[5]) ? (int) $m[5] : 0; $sec = isset($m[6]) ? (int) $m[6] : 0;
874 + if (!checkdate($mo, $d, $y) or $h > 23 or $mi > 59 or $sec > 59) return false;
875 + return gmmktime($h, $mi, $sec, $mo, $d, $y) * 1000;
876 +}
877 +
878 +// "Scale X axis by date/time" option. Returns, per dataset, a list of "{x:<ms>,y:<value>}" JS point
879 +// literals when the option is on and every X value is a date; false otherwise (normal category axis).
880 +function guaven_sqlcharts_time_axis_points($pid, $values){
881 + if (get_post_meta($pid, 'guaven_sqlcharts_timeaxis', true) != 1) return false;
882 + $out = array();
883 + $has_point = false;
884 + foreach ($values as $key_ak => $series) {
885 + $out[$key_ak] = array();
886 + foreach ($series as $x => $y) {
887 + $ts = guaven_sqlcharts_parse_date($x);
888 + if ($ts === false) return false;
889 + $out[$key_ak][] = '{x:' . $ts . ',y:' . (is_numeric($y) ? $y + 0 : 'null') . '}';
890 + $has_point = true;
891 + }
556 892 }
557 - else {
558 - $display='false';$position='top';
559 - }
560 - return "legend: {display: ".$display.",position:'".$position."'},";
893 + return $has_point ? $out : false;
561 894 }
562 895
896 +// X scale options for time-axis mode; gvnSqlChartsTimeTick (asset/front.js) formats the ticks as dates
897 +function guaven_sqlcharts_time_axis_scale(){
898 + return "type: 'linear', offset: true, ticks: {callback: gvnSqlChartsTimeTick, maxRotation: 45},";
899 +}
900 +// extra entry for the Chart.js "plugins" object in time-axis mode (tooltip title shown as a date)
901 +function guaven_sqlcharts_time_axis_plugins($time_points){
902 + return $time_points !== false ? 'tooltip: {callbacks: {title: gvnSqlChartsTimeTooltipTitle}}' : '';
903 +}
904 +
563 905 function guaven_sqlcharts_bardata($title, $labels, $values, $ylabel, $type = 'bar', $pid = null)
564 906 {
907 + $horizontal = ($type == 'horizontalBar');
908 + $forcestack = ($type == 'stackedBar');
909 + $stacked = ($forcestack or get_post_meta($pid, 'guaven_sqlcharts_nostacked', true) != 1) ? 'true' : 'false';
910 + $time_points = $horizontal ? false : guaven_sqlcharts_time_axis_points($pid, $values);
565 911 ?>
566 912 var data = {
567 - labels: [<?php
568 - echo implode(",", guaven_sqlcharts_merge_labeldata($labels) );
569 -?>],
913 + <?php if ($time_points === false) { ?>labels: [<?php guaven_sqlcharts_merge_labeldata($labels);?>],<?php } ?>
570 914 datasets: [
571 915 <?php
572 916 $values_new=guaven_sqlcharts_key_normalizer($values,$labels,$ylabel)[0];
573 917 $i=-1;
@@ -572,32 +916,32 @@
572 916 $values_new=guaven_sqlcharts_key_normalizer($values,$labels,$ylabel)[0];
573 917 $i=-1;
574 918 foreach ($values_new as $key_ak=>$value_ak) {
575 919 $i++;
920 + $points = $time_points !== false ? $time_points[$key_ak] : $values_new[$key_ak];
576 921 ?>
577 922 {
578 - <?php
923 + <?php
579 924 if(!empty($GLOBALS["guaven_sqlcharts_atts"]["params"])){
580 925 //passing chartJS params via the shortcode
581 - echo esc_js($GLOBALS["guaven_sqlcharts_atts"]["params"]);
582 - }
926 + echo wp_kses($GLOBALS["guaven_sqlcharts_atts"]["params"],[]);
927 + }
583 928 ?>
584 - label: "<?php
585 - echo esc_attr($ylabel[$key_ak]);
586 -?>",
929 + label: <?php echo guaven_sqlcharts_js_label($ylabel[$key_ak]); ?>,
587 930 backgroundColor: [
588 931 <?php
589 - echo guaven_sqlcharts_colorgenerator(count($values_new[$key_ak]), 0, 0, guaven_sqlcharts_colors($i, $pid));
932 + echo wp_kses(guaven_sqlcharts_colorgenerator(count($points), 0, 0, guaven_sqlcharts_colors($i, $pid)),[]);
590 933 ?>
591 934 ],
592 935 borderColor: [
593 936 <?php
594 - echo guaven_sqlcharts_colorgenerator(count($values_new[$key_ak]), 0, 0.2, guaven_sqlcharts_colors($i, $pid));
937 + echo wp_kses(guaven_sqlcharts_colorgenerator(count($points), 0, 0.2, guaven_sqlcharts_colors($i, $pid)),[]);
595 938 ?>
596 939 ],
597 940 borderWidth: 1,
941 + <?php if ($time_points !== false) echo 'maxBarThickness: 48,'; ?>
598 942 data: [<?php
599 - echo implode(",", $values_new[$key_ak]);
943 + echo wp_kses(implode(",", $points),[]);
600 944 ?>],
601 945 },
602 946 <?php
603 947 }
@@ -604,30 +948,34 @@
604 948 ?>
605 949 ]
606 950 };
607 951 var options={
608 - <?php echo guaven_sqlcharts_toshowlegend($pid);?>
609 952 responsive: true,
953 + <?php echo wp_kses(guaven_sqlcharts_mar($pid),[]); ?>
954 + <?php echo wp_kses(guaven_sqlcharts_value_labels($pid),[]); ?>
955 + <?php if ($horizontal) echo "indexAxis: 'y',"; ?>
610 956 scales: {
611 - xAxes: [{
612 - <?php echo (get_post_meta($pid, 'guaven_sqlcharts_nostacked', true) == 1) ? '':'stacked: true,';?>
957 + x: {
958 + <?php if ($time_points !== false) echo guaven_sqlcharts_time_axis_scale(); ?>
959 + <?php echo guaven_sqlcharts_axis_title($pid, $horizontal ? 'y' : 'x'); ?>
960 + stacked: <?php echo esc_js($stacked); ?>,
961 + beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_x', true) == 1) ? 'true':'false'; ?>
962 + },
963 + y: {
964 + <?php echo guaven_sqlcharts_axis_title($pid, $horizontal ? 'x' : 'y'); ?>
965 + stacked: <?php echo esc_js($stacked); ?>,
966 + beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_y', true) == 1) ? 'true':'false'; ?>,
613 967 ticks: {
614 - beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_x', true) == 1) ? 'true':'false'; ?>
615 - }
616 - }],
617 - yAxes: [{
618 - <?php echo (get_post_meta($pid, 'guaven_sqlcharts_nostacked', true) == 1) ? '':'stacked: true,';?>
619 - ticks: {
620 968 <?php if(get_post_meta($pid, 'guaven_sqlcharts_round_y_values', true) == 1) echo 'precision: 0,'; ?>
621 - beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_y', true) == 1) ? 'true':'false'; ?>
622 969 }
623 - }]
970 + }
624 971 }
972 + <?php
973 + guaven_sqlcharts_maybe_additional_parameters($pid, guaven_sqlcharts_time_axis_plugins($time_points));
974 + ?>
625 975 };
626 976 var myBarChart = new Chart(ctx, {
627 - type: '<?php
628 - echo $type;
629 -?>',
977 + type: 'bar',
630 978 data: data,
631 979 options: options
632 980 });
633 981 <?php
@@ -633,60 +981,63 @@
633 981 <?php
634 982 }
635 983
636 984 function guaven_sqlcharts_merge_labeldata($labels){
637 - if(count($labels)==1)return $labels[0];
985 + if(count($labels)==1){echo wp_kses(implode(",",$labels[0]),[]);return;}
638 986 $merged=[];
639 987 foreach($labels as $label){
640 988 $merged=array_merge($merged,$label);
641 989 }
642 - return array_unique($merged);
990 + echo wp_kses(implode(",",array_unique($merged)),[]);
643 991 }
644 992
645 -function guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, $type = 'false', $pid = null)
993 +function guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, $type = 'false', $pid = null, $charttype = 'line', $stepped = false)
646 994 {
995 + $time_points = ($charttype == 'radar') ? false : guaven_sqlcharts_time_axis_points($pid, $values);
647 996 ?>
648 997 var data = {
649 - labels: [<?php
650 - echo implode(",", guaven_sqlcharts_merge_labeldata($labels));
651 -?>],
998 + <?php if ($time_points === false) { ?>labels: [<?php guaven_sqlcharts_merge_labeldata($labels);?>],<?php } ?>
652 999 datasets: [
653 - <?php
1000 + <?php
654 1001 $values_new=guaven_sqlcharts_key_normalizer($values,$labels,$ylabel)[0];
1002 + $dataset_count=count($values_new);
655 1003 $i=-1;
656 1004 foreach ($values_new as $key_ak=>$value_ak) {
657 1005 $i++;
1006 + $points = $time_points !== false ? $time_points[$key_ak] : $values_new[$key_ak];
1007 + if ($type == 'radarfill') $fill = "'origin'";
1008 + elseif ($type == 'false') $fill = 'false';
1009 + else $fill = ($i == 0 and $dataset_count > 1) ? '"+1"' : '"origin"';
658 1010 ?>
659 1011 {
660 - <?php
1012 + <?php
661 1013 if(!empty($GLOBALS["guaven_sqlcharts_atts"]["params"])){
662 1014 //passing chartJS params via the shortcode
663 - echo esc_js($GLOBALS["guaven_sqlcharts_atts"]["params"]);
664 - }
1015 + echo wp_kses($GLOBALS["guaven_sqlcharts_atts"]["params"],[]);
1016 + }
665 1017 ?>
666 - label: "<?php
667 - echo esc_attr($ylabel[$key_ak]);
668 -?>",
669 - fill: <?php echo $type=="false"?$type:($i==0?'"+1"':'"origin"');
1018 + label: <?php echo guaven_sqlcharts_js_label($ylabel[$key_ak]); ?>,
1019 + fill: <?php echo wp_kses($fill,[]);
670 1020 ?>,
671 - lineTension: 0.1,
1021 + tension: 0.1,
1022 + <?php if ($stepped) echo 'stepped: true,'; ?>
672 1023 backgroundColor: <?php
673 - echo guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid));
1024 + echo wp_kses_post(guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid)));
674 1025 ?>
675 1026 borderColor: <?php
676 - echo guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid));
1027 + echo wp_kses_post(guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid)));
677 1028 ?>
678 1029 pointBorderColor: <?php
679 - echo guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid));
1030 + echo wp_kses_post(guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid)));
680 1031 ?>
681 1032 pointHoverBackgroundColor: <?php
682 - echo guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid));
1033 + echo wp_kses_post(guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid)));
683 1034 ?>
684 1035 pointHoverBorderColor: <?php
685 - echo guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid));
1036 + echo wp_kses_post(guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid)));
686 1037 ?>
687 1038 data: [<?php
688 - echo implode(",", $values_new[$key_ak]);
1039 + echo wp_kses_post(implode(",", $points));
689 1040 ?>],
690 1041 spanGaps: false,
691 1042 },
692 1043 <?php
@@ -694,27 +1045,106 @@
694 1045 ?>
695 1046 ]
696 1047 };
697 1048 var myLineChart = new Chart(ctx, {
698 - type: 'line',
1049 + type: '<?php echo esc_attr($charttype); ?>',
699 1050 data: data,
700 1051 options: {
701 - <?php echo guaven_sqlcharts_toshowlegend($pid);?>
702 1052 responsive: true,
1053 + <?php echo wp_kses(guaven_sqlcharts_mar($pid),[]); ?>
1054 + <?php echo wp_kses(guaven_sqlcharts_value_labels($pid),[]); ?>
1055 + <?php if ($charttype == 'radar') { ?>
703 1056 scales: {
704 - xAxes: [{
1057 + r: {
1058 + beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_y', true) == 1) ? 'true':'false'; ?>
1059 + }
1060 + }
1061 + <?php } else { ?>
1062 + scales: {
1063 + x: {
705 1064 display: true,
1065 + <?php if ($time_points !== false) echo guaven_sqlcharts_time_axis_scale(); ?>
1066 + <?php echo guaven_sqlcharts_axis_title($pid, 'x'); ?>
1067 + beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_x', true) == 1) ? 'true':'false'; ?>
1068 + },
1069 + y: {
1070 + <?php echo guaven_sqlcharts_axis_title($pid, 'y'); ?>
1071 + beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_y', true) == 1) ? 'true':'false'; ?>,
706 1072 ticks: {
707 - beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_x', true) == 1) ? 'true':'false'; ?>
1073 + <?php if(get_post_meta($pid, 'guaven_sqlcharts_round_y_values', true) == 1) echo 'precision: 0,'; ?>
708 1074 }
709 - }],
710 - yAxes: [{
1075 + }
1076 + }
1077 + <?php } ?>
1078 + <?php
1079 + guaven_sqlcharts_maybe_additional_parameters($pid, guaven_sqlcharts_time_axis_plugins($time_points));
1080 + ?>
1081 +
1082 + }
1083 +});
1084 + <?php
1085 +}
1086 +
1087 +function guaven_sqlcharts_scatterdata($title, $labels, $values, $ylabel, $pid = null)
1088 +{
1089 +?>
1090 +var data = {
1091 + datasets: [
1092 + <?php
1093 + $i=-1;
1094 + foreach ($values as $key_ak=>$value_ak) {
1095 + $i++;
1096 + $points=array();
1097 + foreach ($value_ak as $xval=>$yval) {
1098 + $x = is_numeric($xval) ? $xval : '"'.esc_js($xval).'"';
1099 + $y = is_numeric($yval) ? $yval : '"'.esc_js($yval).'"';
1100 + $points[] = '{x:'.$x.',y:'.$y.'}';
1101 + }
1102 +?>
1103 + {
1104 + <?php
1105 + if(!empty($GLOBALS["guaven_sqlcharts_atts"]["params"])){
1106 + //passing chartJS params via the shortcode
1107 + echo wp_kses($GLOBALS["guaven_sqlcharts_atts"]["params"],[]);
1108 + }
1109 + ?>
1110 + label: <?php echo guaven_sqlcharts_js_label(isset($ylabel[$key_ak])?$ylabel[$key_ak]:''); ?>,
1111 + backgroundColor: <?php
1112 + echo wp_kses_post(guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid)));
1113 +?>
1114 + borderColor: <?php
1115 + echo wp_kses_post(guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid)));
1116 +?>
1117 + data: [<?php echo wp_kses(implode(",", $points),[]); ?>],
1118 + },
1119 + <?php
1120 + }
1121 +?>
1122 + ]
1123 +};
1124 +var myScatterChart = new Chart(ctx, {
1125 + type: 'scatter',
1126 + data: data,
1127 + options: {
1128 + responsive: true,
1129 + <?php echo wp_kses(guaven_sqlcharts_mar($pid),[]); ?>
1130 + <?php echo wp_kses(guaven_sqlcharts_value_labels($pid),[]); ?>
1131 + scales: {
1132 + x: {
1133 + <?php echo guaven_sqlcharts_axis_title($pid, 'x'); ?>
1134 + beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_x', true) == 1) ? 'true':'false'; ?>
1135 + },
1136 + y: {
1137 + <?php echo guaven_sqlcharts_axis_title($pid, 'y'); ?>
1138 + beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_y', true) == 1) ? 'true':'false'; ?>,
711 1139 ticks: {
712 - <?php if(get_post_meta($pid, 'guaven_sqlcharts_round_y_values', true) == 1) echo 'precision: 0,'; ?>
713 - beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_y', true) == 1) ? 'true':'false'; ?>
1140 + <?php if(get_post_meta($pid, 'guaven_sqlcharts_round_y_values', true) == 1) echo 'precision: 0,'; ?>
714 1141 }
715 - }]
1142 + }
716 1143 }
1144 + <?php
1145 + guaven_sqlcharts_maybe_additional_parameters($pid);
1146 + ?>
717 1147 }
718 1148 });
719 1149 <?php
720 1150 }
@@ -719,42 +1149,60 @@
719 1149 <?php
720 1150 }
721 1151
722 1152
1153 +function guaven_sqlcharts_maybe_additional_parameters($pid, $extra_plugins = ''){
1154 + if(function_exists('guaven_sqlcharts_maybe_additional_parameters_custom')){
1155 + wp_kses(guaven_sqlcharts_maybe_additional_parameters_custom($pid),[]);
1156 + return;
1157 + }
1158 + $guaven_sqlcharts_legend_position=get_post_meta($pid, 'guaven_sqlcharts_legend_position', true);
1159 + if(in_array($guaven_sqlcharts_legend_position,['top','bottom','left','right'])){
1160 + $display='true';$position=$guaven_sqlcharts_legend_position;
1161 + }
1162 + else {
1163 + $display='false';$position='top';
1164 + }
1165 + echo wp_kses( ",plugins: {legend: {display: ".$display.",position:'".$position."'}".($extra_plugins !== '' ? ','.$extra_plugins : '')."}",[]);
1166 +}
1167 +
1168 +
1169 +
1170 +
723 1171 function guaven_sqlcharts_piedata($title, $labels, $values, $ylabel, $pid, $type = 'pie')
724 1172 {
725 - if(get_post_meta($pid,'guaven_sqlcharts_forcetooltips',true)!=''){
726 - echo 'guaven_sqlcharts_show_pie_labels();'.PHP_EOL;
727 - }
728 1173 ?>
729 1174 var options={
730 - showAllTooltips: true,
731 - <?php echo guaven_sqlcharts_toshowlegend($pid);?>
732 - responsive: true,
1175 + <?php echo wp_kses(guaven_sqlcharts_value_labels($pid),[]); ?>
1176 + responsive: true
1177 + <?php echo get_post_meta($pid,'guaven_sqlcharts_chartheight',true)!=''||!empty($GLOBALS["guaven_sqlcharts_atts"]['height'])?',maintainAspectRatio: false':''; ?>
1178 + <?php
1179 + guaven_sqlcharts_maybe_additional_parameters($pid);
1180 + ?>
733 1181 };
734 1182 var data = {
735 - labels: [ <?php echo implode(",", guaven_sqlcharts_merge_labeldata($labels));?>],
1183 + labels: [ <?php guaven_sqlcharts_merge_labeldata($labels);?>],
736 1184 datasets: [
737 1185 <?php
738 1186 for ($i = 0; $i < count($values); $i++) {
739 1187 ?>
740 1188 {
741 - <?php
1189 + <?php
742 1190 if(!empty($GLOBALS["guaven_sqlcharts_atts"]["params"])){
743 1191 //passing chartJS params via the shortcode
744 - echo esc_js($GLOBALS["guaven_sqlcharts_atts"]["params"]);
745 - }
1192 + echo wp_kses($GLOBALS["guaven_sqlcharts_atts"]["params"],[]);
1193 + }
746 1194 ?>
747 1195 data: [<?php
748 - echo implode(",", $values[$i]);
1196 + echo wp_kses(implode(",", $values[$i]),[]);
749 1197 ?>],
750 1198 backgroundColor: [
751 1199 <?php
752 1200 $ii=0;
753 1201 foreach($values[$i] as $vci=>$valuecolor){
754 - echo guaven_sqlcharts_colorgenerator(1, 0, -0.1, guaven_sqlcharts_colors($ii, $pid));
1202 + echo wp_kses_post(guaven_sqlcharts_colorgenerator(1, 0, -0.1, guaven_sqlcharts_colors($ii, $pid)));
755 1203 $ii++;
756 - }
1204 + }
757 1205 ?>
758 1206 ],
759 1207 hoverBackgroundColor: [
760 1208 <?php
@@ -759,11 +1207,11 @@
759 1207 hoverBackgroundColor: [
760 1208 <?php
761 1209 $ii=0;
762 1210 foreach($values[$i] as $vci=>$valuecolor){
763 - echo guaven_sqlcharts_colorgenerator(1, 0, 0.2, guaven_sqlcharts_colors($ii, $pid));
1211 + echo wp_kses_post(guaven_sqlcharts_colorgenerator(1, 0, 0.2, guaven_sqlcharts_colors($ii, $pid)));
764 1212 $ii++;
765 - }
1213 + }
766 1214 ?>
767 1215 ]
768 1216 },
769 1217 <?php
@@ -772,9 +1220,9 @@
772 1220 ]
773 1221 };
774 1222 var myPieChart = new Chart(ctx,{
775 1223 type: '<?php
776 - echo $type;
1224 + echo esc_attr($type);
777 1225 ?>',
778 1226 data: data,
779 1227 options: options
780 1228 });
@@ -825,9 +1273,9 @@
825 1273 foreach ($valuerow as $key => $value) {
826 1274 $putval=$labels[$row][$key]??'';
827 1275 $fcol[$key]='<td>'.str_replace('"',"",$putval).'</td>';
828 1276 $scol[$key][$row]='<td>'.$value.'</td>';
829 - }
1277 + }
830 1278 foreach($scol as $scolkey=>$scolvalue){
831 1279 for($i=0;$i<count($values);$i++){
832 1280 //echo $i;
833 1281 if(!isset($scolvalue[$i]))$scol[$scolkey][$i]=$empty_cell;;
@@ -833,17 +1281,18 @@
833 1281 if(!isset($scolvalue[$i]))$scol[$scolkey][$i]=$empty_cell;;
834 1282 }
835 1283 ksort($scol[$scolkey]);
836 1284 }
837 - }
838 -
1285 + }
1286 +
839 1287 foreach($fcol as $key=>$value){
840 1288 $tablein.='<tr>'.$value.implode(" ",$scol[$key]).'</tr>'.PHP_EOL;
841 1289 }
842 - $tabledata.='<table><tr><th>'.$xlabel[0].'</th><th>'.implode("</th><th>",$ylabel).'</th></tr>
843 - '.$tablein.'</table><br>';
844 - return $tabledata;
1290 + $tabledata.='<div class="gvn-tablewrap"><table class="gvn-table"><tr><th>'.$xlabel[0].'</th><th>'.implode("</th><th>",$ylabel).'</th></tr>
1291 + '.$tablein.'</table></div><br>';
845 1292
1293 + echo wp_kses_post($tabledata);
1294 +
846 1295 }
847 1296
848 1297 function guaven_sqlcharts_graphtype($post){
849 1298 if (strpos(get_post_meta($post->ID, 'guaven_sqlcharts_graphtype', true), "_l") !== false)
@@ -879,5 +1328,5 @@
879 1328 }
880 1329
881 1330
882 1331 add_filter('guaven_sqlcharts_table_empty_cell',function($str){return '<td>#</td>';});
883 -add_filter('guaven_sqlcharts_table_empty_value',function($str){return 'N/A';});
1332 +add_filter('guaven_sqlcharts_table_empty_value',function($str){return 'N/A';});