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 +78 -20 3.0.23.0.3 View file →
@@ -444,9 +444,10 @@
444 444 "guaven_sqlcharts_begin_with_0_y",
445 445 "guaven_sqlcharts_round_y_values",
446 446 "guaven_sqlcharts_legend_position",
447 447 "guaven_sqlcharts_nostacked",
448 - "guaven_sqlcharts_forcetooltips"
448 + "guaven_sqlcharts_forcetooltips",
449 + "guaven_sqlcharts_timeaxis"
449 450 );
450 451 foreach ($fields as $key => $value) {
451 452 if(isset($_POST[$value]))$newval=esc_attr($_POST[$value]);
452 453 else $newval='';
@@ -702,19 +703,24 @@
702 703 $blacklister_f = gvn_chart_check_sql_query($sql);
703 704 if ($blacklister_f == 1)return 'You given SQL code contains forbidden commands. Remember that you should only use SELECT queries';
704 705 $tip_g = guaven_sqlcharts_normalize_type(get_post_meta($atts['id'], 'guaven_sqlcharts_graphtype', true));
705 706
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.
706 711 for($i=1;$i<20;$i++){
707 - if(strpos($sql,"{arg".$i."}")!==false){
708 - $replacearg=!empty($atts["arg".$i])?$atts["arg".$i]:0;
709 - $sql = str_replace("{arg".$i."}", "%s", $sql);
710 - $sql=$wpdb->prepare($sql,$replacearg);
711 - }
712 -
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);
713 718 }
714 719
715 720 $sql_split = explode(';', $sql);
716 721 $labels_and_values = array();
722 + $labels = $values = $ylabel = $xlabel = array();
717 723 $post_g = get_post($atts['id']);
718 724
719 725 global $sqlcharts_inserted_script;
720 726 ob_start();
@@ -789,17 +795,24 @@
789 795 if (!shortcode_exists('gvn_schart')) {
790 796 add_shortcode('gvn_schart', 'guaven_sqlcharts_local_shortcode');
791 797 }
792 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.
793 802 add_shortcode("gvn_schart_2_cached",function($atts){
794 803 if(empty($atts["id"]))return;
795 804 $atts["id"]=intval($atts["id"]);
796 805 $is_logged_in=is_user_logged_in()?'':'_guest';
797 806 $expire=!empty($atts["expire"])?intval($atts["expire"]):3600;
798 - $cached=get_transient('cached_sql_charts_'.$atts["id"].$is_logged_in);
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);
799 812 if(!empty($cached) and !isset($_GET["force_sql_cache_reload"]) )return $cached;
800 - $tobecached=do_shortcode('[gvn_schart_2 id="'.$atts["id"].'"]');
801 - set_transient('cached_sql_charts_'.$atts["id"].$is_logged_in, $tobecached,$expire);//you can change 3600 yourself
813 + $tobecached=guaven_sqlcharts_local_shortcode($inner_atts);
814 + set_transient($key, $tobecached,$expire);
802 815 return $tobecached;
803 816 });
804 817
805 818 // fixed, colorblind-friendly default palette (Tableau 10) used when no custom colors are set
@@ -850,16 +863,55 @@
850 863 function guaven_sqlcharts_js_label($label){
851 864 return wp_json_encode(html_entity_decode((string) $label, ENT_QUOTES, 'UTF-8'));
852 865 }
853 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 + }
892 + }
893 + return $has_point ? $out : false;
894 +}
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 +
854 905 function guaven_sqlcharts_bardata($title, $labels, $values, $ylabel, $type = 'bar', $pid = null)
855 906 {
856 907 $horizontal = ($type == 'horizontalBar');
857 908 $forcestack = ($type == 'stackedBar');
858 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);
859 911 ?>
860 912 var data = {
861 - labels: [<?php guaven_sqlcharts_merge_labeldata($labels);?>],
913 + <?php if ($time_points === false) { ?>labels: [<?php guaven_sqlcharts_merge_labeldata($labels);?>],<?php } ?>
862 914 datasets: [
863 915 <?php
864 916 $values_new=guaven_sqlcharts_key_normalizer($values,$labels,$ylabel)[0];
865 917 $i=-1;
@@ -864,8 +916,9 @@
864 916 $values_new=guaven_sqlcharts_key_normalizer($values,$labels,$ylabel)[0];
865 917 $i=-1;
866 918 foreach ($values_new as $key_ak=>$value_ak) {
867 919 $i++;
920 + $points = $time_points !== false ? $time_points[$key_ak] : $values_new[$key_ak];
868 921 ?>
869 922 {
870 923 <?php
871 924 if(!empty($GLOBALS["guaven_sqlcharts_atts"]["params"])){
@@ -875,19 +928,20 @@
875 928 ?>
876 929 label: <?php echo guaven_sqlcharts_js_label($ylabel[$key_ak]); ?>,
877 930 backgroundColor: [
878 931 <?php
879 - echo wp_kses(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)),[]);
880 933 ?>
881 934 ],
882 935 borderColor: [
883 936 <?php
884 - echo wp_kses(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)),[]);
885 938 ?>
886 939 ],
887 940 borderWidth: 1,
941 + <?php if ($time_points !== false) echo 'maxBarThickness: 48,'; ?>
888 942 data: [<?php
889 - echo wp_kses(implode(",", $values_new[$key_ak]),[]);
943 + echo wp_kses(implode(",", $points),[]);
890 944 ?>],
891 945 },
892 946 <?php
893 947 }
@@ -900,8 +954,9 @@
900 954 <?php echo wp_kses(guaven_sqlcharts_value_labels($pid),[]); ?>
901 955 <?php if ($horizontal) echo "indexAxis: 'y',"; ?>
902 956 scales: {
903 957 x: {
958 + <?php if ($time_points !== false) echo guaven_sqlcharts_time_axis_scale(); ?>
904 959 <?php echo guaven_sqlcharts_axis_title($pid, $horizontal ? 'y' : 'x'); ?>
905 960 stacked: <?php echo esc_js($stacked); ?>,
906 961 beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_x', true) == 1) ? 'true':'false'; ?>
907 962 },
@@ -914,9 +969,9 @@
914 969 }
915 970 }
916 971 }
917 972 <?php
918 - guaven_sqlcharts_maybe_additional_parameters($pid);
973 + guaven_sqlcharts_maybe_additional_parameters($pid, guaven_sqlcharts_time_axis_plugins($time_points));
919 974 ?>
920 975 };
921 976 var myBarChart = new Chart(ctx, {
922 977 type: 'bar',
@@ -936,11 +991,12 @@
936 991 }
937 992
938 993 function guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, $type = 'false', $pid = null, $charttype = 'line', $stepped = false)
939 994 {
995 + $time_points = ($charttype == 'radar') ? false : guaven_sqlcharts_time_axis_points($pid, $values);
940 996 ?>
941 997 var data = {
942 - labels: [<?php guaven_sqlcharts_merge_labeldata($labels);?>],
998 + <?php if ($time_points === false) { ?>labels: [<?php guaven_sqlcharts_merge_labeldata($labels);?>],<?php } ?>
943 999 datasets: [
944 1000 <?php
945 1001 $values_new=guaven_sqlcharts_key_normalizer($values,$labels,$ylabel)[0];
946 1002 $dataset_count=count($values_new);
@@ -946,8 +1002,9 @@
946 1002 $dataset_count=count($values_new);
947 1003 $i=-1;
948 1004 foreach ($values_new as $key_ak=>$value_ak) {
949 1005 $i++;
1006 + $points = $time_points !== false ? $time_points[$key_ak] : $values_new[$key_ak];
950 1007 if ($type == 'radarfill') $fill = "'origin'";
951 1008 elseif ($type == 'false') $fill = 'false';
952 1009 else $fill = ($i == 0 and $dataset_count > 1) ? '"+1"' : '"origin"';
953 1010 ?>
@@ -978,9 +1035,9 @@
978 1035 pointHoverBorderColor: <?php
979 1036 echo wp_kses_post(guaven_sqlcharts_colorgenerator(1, 1, 0.2, guaven_sqlcharts_colors($i, $pid)));
980 1037 ?>
981 1038 data: [<?php
982 - echo wp_kses_post(implode(",", $values_new[$key_ak]));
1039 + echo wp_kses_post(implode(",", $points));
983 1040 ?>],
984 1041 spanGaps: false,
985 1042 },
986 1043 <?php
@@ -1004,8 +1061,9 @@
1004 1061 <?php } else { ?>
1005 1062 scales: {
1006 1063 x: {
1007 1064 display: true,
1065 + <?php if ($time_points !== false) echo guaven_sqlcharts_time_axis_scale(); ?>
1008 1066 <?php echo guaven_sqlcharts_axis_title($pid, 'x'); ?>
1009 1067 beginAtZero: <?php echo (get_post_meta($pid, 'guaven_sqlcharts_begin_with_0_x', true) == 1) ? 'true':'false'; ?>
1010 1068 },
1011 1069 y: {
@@ -1017,9 +1075,9 @@
1017 1075 }
1018 1076 }
1019 1077 <?php } ?>
1020 1078 <?php
1021 - guaven_sqlcharts_maybe_additional_parameters($pid);
1079 + guaven_sqlcharts_maybe_additional_parameters($pid, guaven_sqlcharts_time_axis_plugins($time_points));
1022 1080 ?>
1023 1081
1024 1082 }
1025 1083 });
@@ -1091,9 +1149,9 @@
1091 1149 <?php
1092 1150 }
1093 1151
1094 1152
1095 -function guaven_sqlcharts_maybe_additional_parameters($pid){
1153 +function guaven_sqlcharts_maybe_additional_parameters($pid, $extra_plugins = ''){
1096 1154 if(function_exists('guaven_sqlcharts_maybe_additional_parameters_custom')){
1097 1155 wp_kses(guaven_sqlcharts_maybe_additional_parameters_custom($pid),[]);
1098 1156 return;
1099 1157 }
@@ -1103,9 +1161,9 @@
1103 1161 }
1104 1162 else {
1105 1163 $display='false';$position='top';
1106 1164 }
1107 - echo wp_kses( ",plugins: {legend: {display: ".$display.",position:'".$position."'}}",[]);
1165 + echo wp_kses( ",plugins: {legend: {display: ".$display.",position:'".$position."'}".($extra_plugins !== '' ? ','.$extra_plugins : '')."}",[]);
1108 1166 }
1109 1167
1110 1168
1111 1169