WooCommerce Search Engine – INSTANT, RELEVANT AND SMART Search Box

Turn your website search into Smart Search which find products by price, SKU, attributes, meta data, categorys, tags etc.

“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.

Get the Search Box {svg} '; } function guaven_sqlcharts_my_admin_notice() { global $post; if( (!empty($_SERVER["REQUEST_URI"]) and strpos(sanitize_text_field(wp_unslash($_SERVER["REQUEST_URI"])),'post_type=gvn_schart')!==false) or (!empty($post) and $post->post_type == 'gvn_schart') ){ echo str_replace('{svg}',' ',wp_kses_post(guaven_sqlcharts_recommended(),[])); } if (!empty($post) and $post->post_type == 'gvn_schart'): if (!current_user_can('manage_options')) { echo '

Only administrators can manage this page
'; die(); } echo '
'; if (empty($_GET["post"]) && strpos(sanitize_text_field(wp_unslash($_SERVER["REQUEST_URI"])), "post-new") === false) { $gf_message = __( 'Use Add new button above to create a new SQL report. And click on any existing rule names below to manage them.', 'guaven_sqlcharts' ); } else { $gf_message = __( '1. Give any name to your report.
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.
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.', 'guaven_sqlcharts' ); } echo '
'; echo wp_kses_post( $gf_message ); echo '
'; echo '
Get Premium Support OR Make a Small Donation
'; endif; } add_action('admin_notices', 'guaven_sqlcharts_my_admin_notice'); function guaven_sqlcharts_onboarding_notice(){ if((function_exists('wp_doing_ajax') and wp_doing_ajax()) or get_option('guaven_sqlcharts_onboarding_notice_dismissed') == 1) return; printf('

Welcome aboard on MySQL Charts!

' ); } add_action('admin_notices', 'guaven_sqlcharts_onboarding_notice'); function guaven_sqlcharts_onboarding_notice_dismissed(){ check_ajax_referer('notice_dismissed', 'nonce'); if (!current_user_can('manage_options')) return; if(empty($_POST['type']))return; switch ($_POST['type']){ case 'onboarding_notice': update_option('guaven_sqlcharts_onboarding_notice_dismissed', 1); break; } } add_action('wp_ajax_guaven_sqlcharts_onboarding_notice_dismissed', 'guaven_sqlcharts_onboarding_notice_dismissed'); function guaven_sqlcharts_enqueue_chart() { wp_enqueue_script('guaven_sqlcharts_chartjs', plugins_url('asset/chart.umd.min.js', __FILE__),array('jquery'),GVNSQLCHARTS_VERSION,false); wp_enqueue_script('guaven_sqlcharts_datepicker', plugins_url('asset/datepicker.min.js', __FILE__),array('jquery'),GVNSQLCHARTS_VERSION,false); wp_enqueue_script('guaven_sqlcharts_front', plugins_url('asset/front.js', __FILE__),array('jquery','guaven_sqlcharts_chartjs','guaven_sqlcharts_datepicker'),GVNSQLCHARTS_VERSION,false); wp_localize_script('guaven_sqlcharts_front', 'guaven_sqlcharts_notice_dismissed', array( 'action' => 'guaven_sqlcharts_onboarding_notice_dismissed', 'nonce' => wp_create_nonce('notice_dismissed') )); } add_action('wp_enqueue_scripts', 'guaven_sqlcharts_enqueue_chart'); add_action('admin_enqueue_scripts', 'guaven_sqlcharts_enqueue_chart'); function guaven_sqlcharts_enqueue_main_style() { wp_enqueue_style('guaven_sqlcharts_main_style', plugins_url('asset/guaven_sqlcharts.css', __FILE__),array(),GVNSQLCHARTS_VERSION); } add_action('wp_enqueue_scripts', 'guaven_sqlcharts_enqueue_main_style'); add_action('admin_enqueue_scripts', 'guaven_sqlcharts_enqueue_main_style'); // admin-only assets: SQL builder, autocomplete, new metabox UI function guaven_sqlcharts_admin_assets($hook) { if (!in_array($hook, array('post.php', 'post-new.php'))) return; $screen = function_exists('get_current_screen') ? get_current_screen() : null; if (empty($screen->post_type) or $screen->post_type != 'gvn_schart') return; if (!current_user_can('manage_options')) return; wp_enqueue_style('guaven_sqlcharts_admin_style', plugins_url('asset/admin.css', __FILE__), array(), GVNSQLCHARTS_VERSION); wp_enqueue_script('guaven_sqlcharts_admin', plugins_url('asset/admin.js', __FILE__), array('jquery'), GVNSQLCHARTS_VERSION, true); global $wpdb; $tables = $wpdb->get_col('SHOW TABLES'); if (!is_array($tables)) $tables = array(); wp_localize_script('guaven_sqlcharts_admin', 'gvnSqlBuilder', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('gvnsql_schema'), 'tables' => array_values($tables), 'prefix' => $wpdb->prefix, )); } add_action('admin_enqueue_scripts', 'guaven_sqlcharts_admin_assets'); // returns column names of one table for the live SQL builder/autocomplete function guaven_sqlcharts_ajax_columns() { check_ajax_referer('gvnsql_schema', 'nonce'); if (!current_user_can('manage_options')) wp_send_json_error('forbidden', 403); global $wpdb; $table = isset($_POST['table']) ? sanitize_text_field(wp_unslash($_POST['table'])) : ''; $tables = $wpdb->get_col('SHOW TABLES'); if (!is_array($tables) or !in_array($table, $tables, true)) wp_send_json_error('unknown table', 400); $cols = $wpdb->get_results('SHOW COLUMNS FROM `' . str_replace('`', '', $table) . '`'); $out = array(); if (is_array($cols)) { foreach ($cols as $col) { $out[] = array('name' => $col->Field, 'type' => $col->Type); } } wp_send_json_success($out); } add_action('wp_ajax_gvnsql_get_columns', 'guaven_sqlcharts_ajax_columns'); function guaven_sqlcharts_isJson($string) { json_decode($string); return (json_last_error() == JSON_ERROR_NONE); } add_action('init', 'guaven_sqlcharts_register_post'); function guaven_sqlcharts_register_post() { register_post_type('gvn_schart', array( 'labels' => array( 'name' => __('My SQL Charts','guaven_sqlcharts'), 'singular_name' => __('SQL Chart','guaven_sqlcharts'), 'menu_name' => __('My SQL Charts','guaven_sqlcharts'), 'add_new' => __('Add Chart','guaven_sqlcharts'), 'add_new_item' => __('Add New Chart','guaven_sqlcharts'), 'edit_item' => __('Edit Chart','guaven_sqlcharts'), 'new_item' => __('New Chart','guaven_sqlcharts'), 'view_item' => __('View Chart','guaven_sqlcharts'), 'view_items' => __('View Charts','guaven_sqlcharts'), 'search_items' => __('Search Charts','guaven_sqlcharts'), 'not_found' => __('No charts found','guaven_sqlcharts'), 'not_found_in_trash' => __('No charts found in Trash','guaven_sqlcharts'), 'all_items' => __('All Charts','guaven_sqlcharts'), 'archives' => __('Chart Archives','guaven_sqlcharts'), 'attributes' => __('Chart Attributes','guaven_sqlcharts'), 'insert_into_item' => __('Insert into chart','guaven_sqlcharts'), 'uploaded_to_this_item' => __('Uploaded to this chart','guaven_sqlcharts'), 'filter_items_list' => __('Filter charts list','guaven_sqlcharts'), 'items_list_navigation' => __('Charts list navigation','guaven_sqlcharts'), 'items_list' => __('Charts list','guaven_sqlcharts'), 'item_published' => __('Chart published.','guaven_sqlcharts'), 'item_published_privately' => __('Chart published privately.','guaven_sqlcharts'), 'item_reverted_to_draft' => __('Chart reverted to draft.','guaven_sqlcharts'), 'item_scheduled' => __('Chart scheduled.','guaven_sqlcharts'), 'item_updated' => __('Chart updated.','guaven_sqlcharts'), ), 'public' => true, 'show_in_rest' => false, 'menu_icon' => 'dashicons-chart-pie', // Charts execute SQL, so every primitive capability of this post type maps to manage_options. // Contributors/Authors cannot create, edit, publish or delete charts through any WordPress // entry point (admin UI, XML-RPC, REST). Published charts stay viewable on the front end. // Only primitive capabilities are remapped: mapping the meta capabilities edit_post/read_post/ // delete_post to manage_options would make WordPress treat manage_options itself as a meta // capability and break that check site-wide. 'capability_type' => 'post', 'map_meta_cap' => true, 'capabilities' => array( 'edit_posts' => 'manage_options', 'edit_others_posts' => 'manage_options', 'edit_published_posts' => 'manage_options', 'edit_private_posts' => 'manage_options', 'publish_posts' => 'manage_options', 'read_private_posts' => 'manage_options', 'delete_posts' => 'manage_options', 'delete_private_posts' => 'manage_options', 'delete_published_posts' => 'manage_options', 'delete_others_posts' => 'manage_options', 'create_posts' => 'manage_options', ), 'supports' => array( 'title', 'postmeta' ), 'register_meta_box_cb' => 'guaven_sqlcharts_metabox_area' )); guaven_sqlcharts_load_defaults(); } // All guaven_sqlcharts_* meta keys are protected: they cannot be written through the Custom Fields box, // XML-RPC or the REST API. The plugin's own save handler (update_post_meta) is not affected. add_filter('is_protected_meta', function ($protected, $meta_key) { return strpos((string) $meta_key, 'guaven_sqlcharts_') === 0 ? true : $protected; }, 10, 2); // "Add title" placeholder on the chart edit screen add_filter('enter_title_here', function ($title, $post) { if (!empty($post) and $post->post_type == 'gvn_schart') return __('Chart name', 'guaven_sqlcharts'); return $title; }, 10, 2); add_action('admin_footer', 'guaven_sqlcharts_admin_front'); function guaven_sqlcharts_admin_front() { global $post; if (!empty($post) and $post->post_type == 'gvn_schart') { ?> posts; $u = $wpdb->users; return array( 'pie_l' => array( 'label' => 'Pie', 'group' => 'Circular', '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.', 'example_sql' => "select count(*) postcount, SUBSTR(post_date,1,4) yearnum from $p group by yearnum order by yearnum asc limit 10", 'example_x' => 'yearnum', 'example_y' => 'postcount', ), 'donut_l' => array( 'label' => 'Doughnut', 'group' => 'Circular', '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).', 'example_sql' => "select count(*) postcount, post_type from $p group by post_type order by postcount desc limit 8", 'example_x' => 'post_type', 'example_y' => 'postcount', ), 'polar_l' => array( 'label' => 'Polar Area', 'group' => 'Circular', '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).', 'example_sql' => "select count(*) postcount, SUBSTRING(post_date,6,2) monthnum from $p group by monthnum order by monthnum", 'example_x' => 'monthnum', 'example_y' => 'postcount', ), 'radar_l' => array( 'label' => 'Radar', 'group' => 'Circular', 'guide' => 'Compares one or more series across several categories arranged in a circle. Great for profiles/ratings. Use one query per series, separated with ";".', 'example_sql' => "select count(*) postcount, SUBSTRING(post_date,6,2) monthnum from $p where post_type=\"post\" group by monthnum order by monthnum", 'example_x' => 'monthnum', 'example_y' => 'postcount', ), 'line_l' => array( 'label' => 'Line', 'group' => 'Line', '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.', 'example_sql' => "select count(*) postcount, SUBSTRING(post_date,1,7) monthandyear from $p group by monthandyear order by monthandyear", 'example_x' => 'monthandyear', 'example_y' => 'postcount', ), 'area_l' => array( 'label' => 'Area', 'group' => 'Line', '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.', '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", 'example_x' => 'monthandyear', 'example_y' => 'Posts;Other content', ), 'steppedline_l' => array( 'label' => 'Stepped Line', 'group' => 'Line', 'guide' => 'Line chart that moves in steps instead of slopes — perfect for values that change at discrete moments (prices, stock level, settings history).', 'example_sql' => "select count(*) postcount, SUBSTRING(post_date,1,4) yearnum from $p group by yearnum order by yearnum", 'example_x' => 'yearnum', 'example_y' => 'postcount', ), 'bar_l' => array( 'label' => 'Bar', 'group' => 'Bar', 'guide' => 'Compares values across categories with vertical bars. One query: category (X) + numeric value (Y). Multiple ";" separated queries become grouped/stacked bars.', 'example_sql' => "select count(*) postcount, SUBSTRING(post_date,1,7) monthandyear from $p group by monthandyear order by monthandyear", 'example_x' => 'monthandyear', 'example_y' => 'postcount', ), 'horizontalbar_l' => array( 'label' => 'Horizontal Bar', 'group' => 'Bar', 'guide' => 'Bar chart rotated 90° — the best pick when category names are long (user names, product titles).', '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", 'example_x' => 'dname', 'example_y' => 'postcount', ), 'stackedbar_l' => array( 'label' => 'Stacked Bar', 'group' => 'Bar', 'guide' => 'Shows how each category total is composed of parts. Use 2+ queries separated with ";" — each query becomes one segment color of the stack.', '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", 'example_x' => 'yearnum', 'example_y' => 'Posts;Other content', ), 'scatter_l' => array( 'label' => 'Scatter', 'group' => 'Other', '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).', 'example_sql' => "select comment_count ccount, ID from $p where post_status=\"publish\" order by ID limit 100", 'example_x' => 'ID', 'example_y' => 'ccount', ), ); } /** * Maps deprecated Google-Chart era type slugs to their Chart.js equivalents. * Old charts keep working without any manual migration; when the post is re-saved * from the new UI the normalized value is stored automatically. */ function guaven_sqlcharts_normalize_type($type) { $map = array( 'pie' => 'pie_l', '3dpie' => 'pie_l', 'column' => 'bar_l', 'bar' => 'horizontalbar_l', 'area' => 'area_l', ); return isset($map[$type]) ? $map[$type] : $type; } function guaven_gutenberg_wrapper($atts){ if(isset($atts['sqlcharts_inserted_script'])){ global $sqlcharts_inserted_script; $sqlcharts_inserted_script = $atts['sqlcharts_inserted_script']; } $post = get_post($atts['chart_id']); if( ! isset($atts['chart_id']) or !isset($post) or $post->post_type != 'gvn_schart'){ return "Invalid id"; } return guaven_sqlcharts_local_shortcode(array('id' => $atts['chart_id'])); // temporary explicit value } function guaven_register_gutenberg_blocks() { wp_register_script( 'gvn_gutenberg_charts', plugins_url( 'asset/guaven_gutenberg_charts.js', __FILE__ ), array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-server-side-render' ), GVNSQLCHARTS_VERSION.'_'.filemtime( plugin_dir_path( __FILE__ ) . 'asset/guaven_gutenberg_charts.js'), false ); wp_localize_script('gvn_gutenberg_charts', 'guaven', array( 'description' => 'Add My SQL Chart to your post', )); register_block_type( 'guaven-sqlcharts/gvn-chart-gutenberg', array( 'editor_script' => 'gvn_gutenberg_charts', 'render_callback' => 'guaven_gutenberg_wrapper', 'attributes' => array( 'chart_id' => array( 'type' => 'string', 'default' => null ), 'sqlcharts_inserted_script' => array( 'type' => 'number', 'default' => null ) ) )); } add_action('init', 'guaven_register_gutenberg_blocks'); function guaven_sqlcharts_save_metabox_area($post_id, $post) { if (!isset($_POST['meta_box_nonce_field']) or !wp_verify_nonce($_POST['meta_box_nonce_field'], 'meta_box_nonce_action')) { return $post->ID; } if ($post->post_type != 'gvn_schart' or !current_user_can('manage_options') or (defined('DOING_AUTOSAVE') and DOING_AUTOSAVE)) { return $post->ID; } $fields = array( "guaven_sqlcharts_chartheight", "guaven_sqlcharts_chartwidth", "guaven_sqlcharts_graphtype", "guaven_sqlcharts_xarg_s", "guaven_sqlcharts_xarg_l", "guaven_sqlcharts_yarg_s", "guaven_sqlcharts_yarg_l", "guaven_sqlcharts_tablepart", "guaven_sqlcharts_variables", "guaven_sqlcharts_formpartrole", "guaven_sqlcharts_formpartbutton", "guaven_sqlcharts_dbhost", "guaven_sqlcharts_dblogin", "guaven_sqlcharts_dbname", "guaven_sqlcharts_colors", "guaven_sqlcharts_begin_with_0_x", "guaven_sqlcharts_begin_with_0_y", "guaven_sqlcharts_round_y_values", "guaven_sqlcharts_legend_position", "guaven_sqlcharts_nostacked", "guaven_sqlcharts_forcetooltips", "guaven_sqlcharts_timeaxis" ); foreach ($fields as $key => $value) { if(isset($_POST[$value]))$newval=esc_attr($_POST[$value]); else $newval=''; update_post_meta($post->ID, $value, $newval); } if(!empty($_POST["guaven_sqlcharts_dbpass"])){ $encpass=guaven_sqlcharts_encrypt_decrypt('encrypt',$_POST["guaven_sqlcharts_dbpass"]); update_post_meta($post->ID, 'guaven_sqlcharts_dbpass', ['encrypted',$encpass]); } // Store the SQL as typed. Do not HTML-encode it and do not rewrite quotes: // the editor escapes it on output and the front end decodes entities before running it. $sql_code = isset($_POST['guaven_sqlcharts_code']) ? wp_check_invalid_utf8(wp_unslash($_POST['guaven_sqlcharts_code'])) : ''; update_post_meta($post->ID, 'guaven_sqlcharts_code', $sql_code); // Flag that this chart stores raw SQL. Charts without the flag were saved by // versions before 3.0.1, which HTML-encoded the query, and still need decoding. update_post_meta($post->ID, 'guaven_sqlcharts_code_raw', 1); } // Returns the stored SQL query exactly as the user typed it. function guaven_sqlcharts_get_code($post_id) { $sql = get_post_meta($post_id, 'guaven_sqlcharts_code', true); if (get_post_meta($post_id, 'guaven_sqlcharts_code_raw', true) != 1) { $sql = html_entity_decode($sql, ENT_QUOTES, 'UTF-8'); } return $sql; } add_action('save_post', 'guaven_sqlcharts_save_metabox_area', 1, 2); // save the custom fields // Removes string literals (contents only), backtick identifiers and comments from SQL so keyword checks // see the same code MySQL will execute. "/*!" and "/*+" comments are executable in MySQL and are kept. function guaven_sqlcharts_strip_sql_literals($sql) { $out = ''; $len = strlen($sql); $i = 0; while ($i < $len) { $c = $sql[$i]; if ($c === "'" or $c === '"' or $c === '`') { $out .= $c . $c; $i++; while ($i < $len) { if ($sql[$i] === '\\' and $c !== '`') { $i += 2; continue; } if ($sql[$i] === $c) { if ($i + 1 < $len and $sql[$i + 1] === $c) { $i += 2; continue; } $i++; break; } $i++; } continue; } if ($c === '#' or ($c === '-' and substr($sql, $i, 2) === '--' and ($i + 2 >= $len or ctype_space($sql[$i + 2])))) { $nl = strpos($sql, "\n", $i); $i = ($nl === false) ? $len : $nl; continue; } if ($c === '/' and substr($sql, $i, 2) === '/*' and !in_array(substr($sql, $i + 2, 1), array('!', '+'), true)) { $close = strpos($sql, '*/', $i + 2); $i = ($close === false) ? $len : $close + 2; $out .= ' '; continue; } $out .= $c; $i++; } return $out; } // Returns 1 when the (fully substituted) SQL must not run, 0 when it is a read-only query. // Called after every {tag}/{argN} replacement so user-supplied values are covered too. function gvn_chart_check_sql_query($sql) { // 1) data-changing statements: checked on the raw text, exactly as in every previous version $write = '/\b(delete|update|insert|replace|drop|truncate|alter|create|rename|grant|revoke|call|handler|load\s+data|load_file|outfile|dumpfile)\b/i'; if (preg_match($write, $sql)) return 1; // 2) further dangerous statements, matched outside string literals and comments so that ordinary // values such as status = 'reset' keep working $danger = '/\b(prepare|execute|deallocate|lock|unlock|kill|shutdown|flush|reset|purge|install|uninstall|import' . '|set\s+(?:global|session|persist|persist_only|password|@@)|start\s+(?:replica|slave|group_replication)|stop\s+(?:replica|slave)|change\s+(?:master|replication))\b/i'; if (preg_match($danger, guaven_sqlcharts_strip_sql_literals($sql))) return 1; // 3) every ";"-separated statement must be a read statement. The renderer sends each segment to the // database on its own, so this stops a value from smuggling a second statement behind a ";". foreach (explode(';', $sql) as $segment) { $segment = ltrim(guaven_sqlcharts_strip_sql_literals($segment), " \t\r\n("); if ($segment === '') continue; if (!preg_match('/^(select|with|show|describe|desc|explain)\b/i', $segment)) return 1; } return 0; } function guaven_get_labels_and_values($id, $fvs) { $values = array(); $labels = array(); $xarg_s = get_post_meta($id, 'guaven_sqlcharts_xarg_s', true); $yarg_s = get_post_meta($id, 'guaven_sqlcharts_yarg_s', true); // labels are saved through esc_attr, so "&" is stored as "&"; decode before splitting on ";" // or the entity's own ";" would be taken as a series separator $xarg_l = html_entity_decode((string) get_post_meta($id, 'guaven_sqlcharts_xarg_l', true), ENT_QUOTES, 'UTF-8'); $yarg_l = html_entity_decode((string) get_post_meta($id, 'guaven_sqlcharts_yarg_l', true), ENT_QUOTES, 'UTF-8'); foreach ($fvs as $key => $value) { $values[$value->$xarg_s] = $value->$yarg_s; $labels[$value->$xarg_s] = '"' . $value->$xarg_s . '"'; } return array( $labels, $values, explode(";", $yarg_l), explode(";", $xarg_l) ); } function guaven_sqlcharts_print_chart_js($print_data) { extract($print_data); $tip_g = guaven_sqlcharts_normalize_type($tip_g); switch ($tip_g) { case 'line_l': guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, 'false', $pid); break; case 'area_l': guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, 'true', $pid); break; case 'steppedline_l': guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, 'false', $pid, 'line', true); break; case 'radar_l': guaven_sqlcharts_linedata($title, $labels, $values, $ylabel, 'radarfill', $pid, 'radar'); break; case 'pie_l': guaven_sqlcharts_piedata($title, $labels, $values, $ylabel, $pid); break; case 'donut_l': guaven_sqlcharts_piedata($title, $labels, $values, $ylabel, $pid, 'doughnut'); break; case 'polar_l': guaven_sqlcharts_piedata($title, $labels, $values, $ylabel, $pid, 'polarArea'); break; case 'bar_l': guaven_sqlcharts_bardata($title, $labels, $values, $ylabel, 'bar', $pid); break; case 'horizontalbar_l': guaven_sqlcharts_bardata($title, $labels, $values, $ylabel, 'horizontalBar', $pid); break; case 'stackedbar_l': guaven_sqlcharts_bardata($title, $labels, $values, $ylabel, 'stackedBar', $pid); break; case 'scatter_l': guaven_sqlcharts_scatterdata($title, $labels, $values, $ylabel, $pid); break; case 'custom': guaven_sqlcharts_custom($title, $labels, $values, $ylabel, $pid); break; } } function guaven_sqlcharts_custom($title, $labels, $values, $ylabel, $pid){ do_action('guaven_sqlcharts_custom',$title, $labels, $values, $ylabel, $pid); } function gvn_chart_put_variables($sql,$pid){ $sql_initial=$sql; $default_tag_keys=['{current_user_id}','{current_user_login}','{current_user_email}','{current_user_display_name}']; if(is_user_logged_in( )){ $currentuser=wp_get_current_user(); $default_tag_values=[$currentuser->ID,$currentuser->user_login,$currentuser->user_email,$currentuser->user_display_name]; } else { $default_tag_values=''; } $sql_initial=str_replace($default_tag_keys,$default_tag_values,$sql_initial); $variables_raw=get_post_meta($pid,'guaven_sqlcharts_variables',true); $variables_arr=explode("|",$variables_raw); foreach($variables_arr as $varfield){ $varfield_arr=explode("~",$varfield); if (count($varfield_arr)<3) continue; $varfield_arr=array_map("trim",$varfield_arr); if (!empty($_GET[$varfield_arr[0]])) { // User-supplied input: no () bypass allowed — sanitize strictly $varreplacement = str_replace(';', '', sanitize_text_field(wp_unslash($_GET[$varfield_arr[0]]))); if (is_numeric($varreplacement)) { $varreplacement = $varreplacement + 0; } else { $varreplacement = '"' . esc_sql($varreplacement) . '"'; } } else { // Admin-configured default value: allow () for SQL functions (e.g. NOW()) $varreplacement = $varfield_arr[1]; if (!is_numeric($varreplacement) && strpos($varreplacement,'()')===false) { $varreplacement = '"' . esc_sql($varreplacement) . '"'; } } $sql_initial=str_replace('{'.$varfield_arr[0].'}',$varreplacement,$sql_initial); } return $sql_initial; } function gvn_chart_top_form($atts){ if (get_post_meta($atts["id"],'guaven_sqlcharts_formpartrole',true)!='' and !is_user_logged_in()) return; $topform='';$dateexists=false; $variables_raw=get_post_meta($atts['id'],'guaven_sqlcharts_variables',true); $variables_raw=explode("|",$variables_raw); foreach ($variables_raw as $vrow){ $vrow_arr=explode("~",$vrow); $vrow_arr=array_map("trim",$vrow_arr); if (empty($vrow_arr[3])) continue; $gvalue=!empty($_GET[$vrow_arr[0]])?esc_attr(urldecode($_GET[$vrow_arr[0]])):''; $dvalue=(strpos($vrow_arr[1],'()')===false)?esc_attr($vrow_arr[1]):''; if ($vrow_arr[3]=='date') { $dateexists=true; $topform.= ' ';} else { $topform.= ' '; } } if (!empty($topform)) { $allowed_html = array( 'form' => array( 'method' => array(), 'action' => array(), 'class' => array() ), 'input' => array( 'type' => array(), 'value' => array(), 'name' => array(), 'class' => array(), 'data-toggle'=>array(), 'placeholder'=>array(), 'autocomplete'=>array(), 'style'=>[] ), 'span' => array('class' => array()), 'label' => array(), ); $submit_button_value = get_post_meta($atts['id'], 'guaven_sqlcharts_formpartbutton', true) != '' ? esc_attr(get_post_meta($atts['id'], 'guaven_sqlcharts_formpartbutton', true)) : 'OK'; $topform = '
' . $topform . '
'; echo wp_kses($topform, $allowed_html); } } function guaven_sqlcharts_encrypt_decrypt($action, $string) { $output = false; $encrypt_method = "AES-256-CBC"; $secret_key = 'GWSCHARTPL2022.2016.'; $secret_iv = 'GWSCHARTPL2016.2022'; $key = hash('sha256', $secret_key); $iv = substr(hash('sha256', $secret_iv), 0, 16); if ( $action == 'encrypt' ) { $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv); $output = base64_encode($output); } else if( $action == 'decrypt' ) { $output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv); } return $output; } function guaven_sqlcharts_local_shortcode($atts) { if(empty($atts['id']))return 'ID is missing.'; $atts['id']=intval($atts['id']); $post_g = get_post($atts['id']); if (!$post_g or $post_g->post_type != 'gvn_schart') return 'Chart not found.'; $remote_host=get_post_meta($atts['id'], 'guaven_sqlcharts_dbhost', true); if ($remote_host!=''){ $remote_db=get_post_meta($atts['id'], 'guaven_sqlcharts_dbname', true); $remote_login=get_post_meta($atts['id'], 'guaven_sqlcharts_dblogin', true); $remote_pass=get_post_meta($atts['id'], 'guaven_sqlcharts_dbpass', true); if(is_array($remote_pass)){ $remote_pass=guaven_sqlcharts_encrypt_decrypt('decrypt',$remote_pass[1]); } $wpdb=new wpdb($remote_login,$remote_pass,$remote_db,$remote_host); } else { global $wpdb; } $GLOBALS["guaven_sqlcharts_atts"]=$atts; $sql = guaven_sqlcharts_get_code($atts['id']); if(empty($sql))return 'SQL query is missing.'; // {arg1}..{arg19} come from shortcode attributes: [gvn_schart_2 id="1" arg1="41"]. // Substituted directly (not via wpdb::prepare) so the same tag may appear any number of times, // e.g. in every query of a ";"-separated comparison chart. Numbers are inserted as-is, anything // else is escaped and quoted; a tag already wrapped in quotes ('{arg1}') is not double-quoted. // ";" is removed from values because the finished SQL is split on ";" below. for($i=1;$i<20;$i++){ $tag = '{arg'.$i.'}'; if (strpos($sql, $tag) === false) continue; $replacearg = !empty($atts['arg'.$i]) ? $atts['arg'.$i] : 0; if (is_numeric($replacearg)) $replacearg = $replacearg + 0; else $replacearg = "'" . esc_sql(str_replace(';', '', sanitize_text_field((string) $replacearg))) . "'"; $sql = str_replace(array("'".$tag."'", '"'.$tag.'"', $tag), $replacearg, $sql); } $sql=gvn_chart_put_variables($sql,$atts['id']); $sql=apply_filters('guaven_sqlcharts_rendered_sql',$sql,$atts); // command check on the final SQL, after every shortcode argument and filter value is in place $blacklister_f = gvn_chart_check_sql_query($sql); if ($blacklister_f == 1)return 'You given SQL code contains forbidden commands. Remember that you should only use SELECT queries'; $tip_g = guaven_sqlcharts_normalize_type(get_post_meta($atts['id'], 'guaven_sqlcharts_graphtype', true)); $sql_split = explode(';', $sql); $labels_and_values = array(); $labels = $values = $ylabel = $xlabel = array(); global $sqlcharts_inserted_script; ob_start(); for ($i = 0; $i < count($sql_split); $i++) { if (!empty($sql_split[$i])) { $fvs = $wpdb->get_results($sql_split[$i]); if (strpos($_SERVER["REQUEST_URI"],'wp-admin')!==false){ $wpdb->show_errors(); ob_start(); $wpdb->print_error(); $printerror = ob_get_clean(); if ($printerror != '' and strpos($printerror, "[]") === false){ ob_end_clean(); return $printerror; } elseif (empty($fvs)){ ob_end_clean(); return 'Your SQL returnes empty data, please recheck your SQL query above'; } } if (empty($sqlcharts_inserted_script)) $sqlcharts_inserted_script = 1; $labels_and_values[$i] = guaven_get_labels_and_values($atts['id'], $fvs); $labels[$i] = $labels_and_values[$i][0]; $values[$i] = $labels_and_values[$i][1]; $ylabel[$i] = !empty($labels_and_values[$i][2][$i]) ? $labels_and_values[$i][2][$i] : ''; $xlabel[$i] = !empty($labels_and_values[$i][3][$i]) ? $labels_and_values[$i][3][$i] : ''; } } gvn_chart_top_form($atts); // shortcode width/height attributes override the saved defaults $chart_w = !empty($atts['width']) ? $atts['width'] : get_post_meta($atts['id'], 'guaven_sqlcharts_chartwidth', true); $chart_h = !empty($atts['height']) ? $atts['height'] : get_post_meta($atts['id'], 'guaven_sqlcharts_chartheight', true); $wrap_style = ''; if ($chart_w != '') $wrap_style .= 'max-width:' . (int) $chart_w . 'px;'; if ($chart_h != '') $wrap_style .= 'height:' . (int) $chart_h . 'px;'; ?>
>
post_title, $labels, $values, $ylabel,$xlabel); $sqlcharts_inserted_script++; $ret=ob_get_clean(); $ret=apply_filters( 'guaven_sqlcharts_final_output', $ret, $atts ); return $ret; } add_shortcode('gvn_schart_2', 'guaven_sqlcharts_local_shortcode'); // legacy alias: old Google-Chart era posts produced [gvn_schart id=".."] shortcodes if (!shortcode_exists('gvn_schart')) { add_shortcode('gvn_schart', 'guaven_sqlcharts_local_shortcode'); } // [gvn_schart_2_cached id="1" expire="3600" arg1=".."] – same as gvn_schart_2 but the output is kept in a // transient. All other attributes (argN, width, height, table, params) are passed through, and each // distinct set of attributes gets its own cache entry. Append ?force_sql_cache_reload to the URL to bypass. add_shortcode("gvn_schart_2_cached",function($atts){ if(empty($atts["id"]))return; $atts["id"]=intval($atts["id"]); $expire=!empty($atts["expire"])?intval($atts["expire"]):3600; $inner_atts=$atts; unset($inner_atts['expire']); // One cache entry per user (charts may use {current_user_*} tags), per set of shortcode attributes // and per value of every dynamic filter this chart reads from the URL. A visitor can therefore // never be served, or pre-seed, a result computed for someone else or for other filter values. $key_parts = array('atts' => $inner_atts, 'user' => is_user_logged_in() ? get_current_user_id() : 0, 'get' => array()); foreach (explode('|', (string) get_post_meta($atts['id'], 'guaven_sqlcharts_variables', true)) as $vrow) { $vname = trim(current(explode('~', $vrow))); if ($vname !== '' and isset($_GET[$vname])) $key_parts['get'][$vname] = sanitize_text_field(wp_unslash($_GET[$vname])); } $key = 'cached_sql_charts_' . $atts["id"] . '_' . md5(serialize($key_parts)); $cached=get_transient($key); if(!empty($cached) and !isset($_GET["force_sql_cache_reload"]) )return $cached; $tobecached=guaven_sqlcharts_local_shortcode($inner_atts); set_transient($key, $tobecached,$expire); return $tobecached; }); // fixed, colorblind-friendly default palette (Tableau 10) used when no custom colors are set function guaven_sqlcharts_default_palette(){ return apply_filters('guaven_sqlcharts_default_palette', array( '#4E79A7', '#F28E2B', '#E15759', '#76B7B2', '#59A14F', '#EDC948', '#B07AA1', '#FF9DA7', '#9C755F', '#BAB0AC' )); } function guaven_sqlcharts_colors($index, $pid = null){ if(!isset($pid)) { global $post; $pid = $post->ID; } $colors=get_post_meta($pid,'guaven_sqlcharts_colors',true); $colors=explode(",",$colors); if (!empty($colors[$index])) return $colors[$index]; $palette = guaven_sqlcharts_default_palette(); return $palette[$index % count($palette)]; } // outputs 'maintainAspectRatio:false,' when an explicit height is set, so the // chart fills its sized wrapper instead of keeping the default aspect ratio function guaven_sqlcharts_mar($pid){ $atts = isset($GLOBALS["guaven_sqlcharts_atts"]) ? $GLOBALS["guaven_sqlcharts_atts"] : array(); $h = !empty($atts['height']) ? $atts['height'] : get_post_meta($pid, 'guaven_sqlcharts_chartheight', true); return $h != '' ? 'maintainAspectRatio: false,' : ''; } // outputs 'showAllTooltips: true,' when "Value labels" is checked; the values are drawn by the // gvnShowAllValues plugin in asset/front.js (works for every chart type) function guaven_sqlcharts_value_labels($pid){ return get_post_meta($pid, 'guaven_sqlcharts_forcetooltips', true) != '' ? 'showAllTooltips: true,' : ''; } // Chart.js scale title block built from the "X axis label" / "Y axis label" fields. // $which is 'x' or 'y' (the *field* to use, not the scale). The Y label is only used as an axis // title for single-series charts; with several ";"-separated series the legend names them instead. function guaven_sqlcharts_axis_title($pid, $which){ $key = $which == 'x' ? 'guaven_sqlcharts_xarg_l' : 'guaven_sqlcharts_yarg_l'; $text = trim(html_entity_decode((string) get_post_meta($pid, $key, true), ENT_QUOTES, 'UTF-8')); if ($text === '' or ($which == 'y' and strpos($text, ';') !== false)) return ''; return 'title: {display: true, text: ' . wp_json_encode($text) . '},'; } // "params" shortcode attribute: extra Chart.js dataset options, e.g. params="borderWidth: 3, borderDash: [5,5],". // The text is placed inside the inline