sparkline showing the number of posts per day for the current view. Works on home, tags, categories and authors pages Uses jQuery Sparklines.
Author: Beau Lebens
Version: 1.1
Author URI: http://dentedreality.com.au/
@todo add actions to clear cached data on post publishing (selective, based on cat/tag/author)
@todo handle long periods properly (collecting posts from each month in range...)
*/
// Need jQuery and the Sparkline JS files to
wp_enqueue_script( 'jquery' );
if ( defined( WPMU_PLUGIN_URL ) ) // May as well make it MU-friendly while we're at it
wp_enqueue_script( 'jquery-sparkline', WPMU_PLUGIN_URL . '/sparkplug/jquery.sparkline.min.js' );
else
wp_enqueue_script( 'jquery-sparkline', plugins_url( '/sparkplug/jquery.sparkline.min.js' ) );
/**
* Get the data for the entire blog, so that we have the "baseline" data to
* chart.
*
* @param int $days worth of data to get
* @param array $data to merge with
* @return array containing data you sent + global data
**/
function sparkplug_get_home_data( $days ) {
$dates = date_dates_between( strtotime( '-' . $days . ' days' ), time() );
sort($dates);
$data = array();
$args = array( 'year' => date( 'Y' ), 'monthnum' => date( 'm' ) );
if ( function_exists( 'pp_get_category_id' ) ) // Prologue Projects compat
$args['cat'] = pp_get_category_id( 'updates' );
$raw = get_posts( $args );
if ( date( 'd' ) < $days ) {
// We need more than we would have gotten from this month, so get previous month as well
$args['year'] = date( 'Y', time() - ( $days * 24 * 60 * 60 ) );
$args['monthnum'] = date( 'm', time() - ( $days * 24 * 60 * 60 ) );
$raw = array_merge( $raw, get_posts( $args ) );
}
if ( count( $raw ) ) {
// Process results into useful array
$already = array();
foreach ( $raw as $r ) {
$d = trim( substr( $r->post_date, 0, 10 ) );
if ( in_array( $r->ID, $already ) || !in_array( $d, $dates ) )
continue; // Skip it if we already included it
if ( isset( $data[$d] ) ) {
$data[$d]++;
} else {
$data[$d] = 1;
}
$already[] = $r->ID;
}
}
return $data;
}
/**
* Clean up an array of data so that all dates are shown, even if zero.
* Data passed by reference, so updated "in place"
**/
function sparkplug_cleanup_data( &$data, $days = 30, $generated = true ) {
// Now fill out the array with all days
$dates = date_dates_between( strtotime( '-' . $days . ' days' ), time() );
sort($dates);
foreach ( $dates as $date ) {
if ( empty( $data[$date] ) ) {
$data[$date] = '0';
}
}
// When was this chunk of data generated?
if ( $generated )
$data['generated'] = date( 'Y-m-d H:i:s' );
ksort($data);
}
/**
* Output the chart via widget. Grabs data as required etc.
**/
function widget_sparkplug( $args ) {
global $sparkplug_done;
if ( $sparkplug_done )
return; // Can only have 1 per page
$defaults = array( 'before_widget' => '
', 'after_widget' => '
' );
$opts = get_option( 'sparkplug_options' );
$args = array_merge( $defaults, $opts, $args );
extract( $args );
// Handle defaults (may be set via args)
// Gives you a fair amount of control over the display style/content
$do_total = isset( $do_total ) ? $do_total : 'yes'; // This specified whether we should include the line for "all posts" on non-home views
$days = isset( $days ) && (int) $days <= 90 ? (int) $days : 30; // How many days to show
$barColor = isset( $barColor ) ? $barColor : '#0066cc';
$barWidth = isset( $barWidth ) ? $barWidth : 4;
$barHeight = isset( $barHeight ) ? $barHeight : '20px';
$minSpotColor = isset( $minSpotColor ) ? $minSpotColor : 'false'; // Show a spot at the lowest point on the line (use STRING false to disable)
$maxSpotColor = isset( $maxSpotColor ) ? $maxSpotColor : 'false'; // Show a spot at the highest point on the line (use STRING false to disable)
$spotRadius = isset( $spotRadius ) ? $spotRadius : 'false'; // How big should the spot be? (us STRING false to disable)
$fillColor = isset( $fillColor ) ? $fillColor : 'false'; // Hex to color in under the line (not recommended) or STRING false to not color
$lineColor = isset( $lineColor ) ? $lineColor : '#bdbdbd';
$defaultPixelsPerValue = isset( $defaultPixelsPerValue ) ? $defaultPixelsPerValue : '4';
if ( is_singular() )
return; // Doesn't make sense to show on a singular anything
// These vars are all for Prologue Projects compat
global $is_author, $is_category, $is_tag, $mode, $is_front_page;
global $author_id, $category_id, $tag_id, $project_id, $query_categories;
$is_project = $project_id ? true : false;
$mode = !empty( $mode ) ? $mode : 'updates';
// Figure out what we're looking at
$view = false;
$option = false;
$id = false;
if ( is_author() || $is_author ) {
$view = 'author';
$param = 'author';
if ( $is_author ) // Prologue Projects compat
$id = $author_id;
else
$id = get_the_author_ID();
} else if ( is_tag() || $is_tag ) { // @todo figure out why tag queries are returning everything
$view = 'tag';
$param = 'tag_id';
if ( $is_tag ) { // Prolog Projects compat
$id = $tag_id;
} else {
global $wp_query;
$id = $wp_query->get_queried_object();
$id = $id->term_id;
}
} else if ( is_front_page() || $is_front_page ) {
if ( $is_front_page && function_exists( 'pp_get_category_id' ) ) { // Prologue Projects compat
$view = 'pp_home';
$id = pp_get_category_id( 'updates' );
$param = 'cat';
} else {
$view = 'home';
$id = '';
}
} else if ( is_category() || $is_project ) {
$view = 'category';
if ( $is_project ) { // Prologue Projects compat
if ( count( $query_categories ) > 1 ) {
$param = 'category__and';
$id = $query_categories;
} else {
$param = 'cat';
$id = $query_categories[0];
}
} else {
global $wp_query;
$id = $wp_query->get_queried_object();
$id = $id->cat_ID;
$param = 'cat';
}
} else {
return; // Unsupported view
}
// This will contain whatever JS is required to generate this specific
// sparkline chart
$sparkline_js = '';
// Check for cached data for this view
$view_option = "sparkplug_{$view}_" . ( is_array( $id ) ? implode( '_', $id ) : $id );
if ( !$data = get_option( $view_option ) )
$data = false;
// Check if the data we have has "expired"
if ( isset( $data['generated'] ) && $data['generated'] < date( 'Y-m-d H:i:s', strtotime( 'yesterday' ) ) )
$data = false;
// Make sure it contains the right number of days
if ( $days != ( count( $data ) - 1 ) )
$data = false;
// Now see if we need to load data
if ( false === $data ) {
$dates = date_dates_between( strtotime( '-' . $days . ' days' ), time() );
sort($dates);
$data = array();
// Query for # posts based on current view
switch ( $view ) {
case 'author':
case 'category':
case 'pp_home':
$args = array( $param => $id, 'year' => date( 'Y' ), 'monthnum' => date( 'm' ) );
$raw = get_posts( $args );
if ( date( 'd' ) < $days ) {
// We need more than we would have gotten from this month, so get previous month as well
$args['year'] = date( 'Y', time() - ( $days * 24 * 60 * 60 ) );
$args['monthnum'] = date( 'm', time() - ( $days * 24 * 60 * 60 ) );
$raw = array_merge( $raw, get_posts( $args ) );
}
if ( count( $raw ) ) {
// Process results into useful array
$already = array();
foreach ( $raw as $r ) {
$d = trim( substr( $r->post_date, 0, 10 ) );
if ( in_array( $r->ID, $already ) || !in_array( $d, $dates ) )
continue; // Skip it if we already included it
if ( isset( $data[$d] ) ) {
$data[$d]++;
} else {
$data[$d] = 1;
}
$already[] = $r->ID;
}
}
break;
case 'tag':
// Can't seem to do a tag + date query, so getting all, then only using ones with certain tags
$args = array( 'year' => date( 'Y' ), 'monthnum' => date( 'm' ) );
$all_raw = get_posts( $args );
if ( date( 'd' ) < $days ) {
// We need more than we would have gotten from this month, so get previous month as well
$args['year'] = date( 'Y', time() - ( $days * 24 * 60 * 60 ) );
$args['monthnum'] = date( 'm', time() - ( $days * 24 * 60 * 60 ) );
$all_raw = array_merge( $all_raw, get_posts( $args ) );
}
if ( count( $all_raw ) ) {
$raw = array();
foreach ( $all_raw as $post ) {
setup_postdata( $post );
$tags = wp_get_post_tags( $post->ID );
foreach ( $tags as $tag ) {
if ( $tag->term_id == $id) {
$raw[] = $post;
}
}
}
if ( !count( $raw ) ) {
$data = array();
break;
}
// Process results into useful array
$already = array();
foreach ( $raw as $r ) {
$d = trim( substr( $r->post_date, 0, 10 ) );
if ( in_array( $r->ID, $already ) || !in_array( $d, $dates ) )
continue; // Skip it if we already included it
if ( isset( $data[$d] ) ) {
$data[$d]++;
} else {
$data[$d] = 1;
}
$already[] = $r->ID;
}
}
break;
case 'home':
default:
$data = sparkplug_get_home_data( $days );
}
// Now fill out the array with all days
sparkplug_cleanup_data( $data, $days );
update_option( $view_option, $data );
}
unset( $data['generated'] );
// Now generate the JS for the current view chart
ksort($data);
$sparkline_js .= "jQuery('#sparkplug-chart').sparkline([" . implode( ',', $data ) . "], { type: 'bar', barColor: '$barColor', barWidth: $barWidth, height: '$barHeight', chartRangeMin: 0 });\n";
// Check if we're looking at the home view and add the line for that dataset
if ( 'home' != $view && 'pp_home' != $view && 'updates' == $mode && 'yes' == $do_total ) {
$blog_data = false;
// We are not, so we need to load the home data as well to provide
// a comparison line on our chart
if ( !$blog_data = get_option( 'sparkplug_home_') )
$blog_data = false;
// Check if the data we have has "expired"
if ( isset( $blog_data['generated'] ) && $blog_data['generated'] < date( 'Y-m-d H:i:s', strtotime( 'yesterday' ) ) )
$blog_data = false;
// Make sure it contains the right number of days
if ( $days != ( count( $data ) - 1 ) )
$data = false;
// Now see if we need to load data
if ( false === $blog_data ) {
$blog_data = sparkplug_get_home_data( $days );
sparkplug_cleanup_data( $blog_data, $days );
update_option( 'sparkplug_home_', $blog_data );
}
unset( $blog_data['generated'] );
// Now generate the JS for the chart
ksort($blog_data);
$sparkline_js .= "\t\tjQuery('#sparkplug-chart').sparkline([" . implode( ',', $blog_data ) . "], { composite: true, type: 'line', minSpotColor: $minSpotColor, maxSpotColor: $maxSpotColor, defaultPixelsPerValue: $defaultPixelsPerValue, spotRadius: $spotRadius, fillColor: $fillColor, lineColor: '$lineColor' });\n";
}
// Write out the JS to generate the chart
echo $before_widget; ?>
'Display a sparkline chart showing post frequency for the current author/tag/category/blog.' ) );
wp_register_widget_control( 'sparkplug', 'Sparkplug', 'widget_sparkplug_control' );
}
add_action( 'init', 'widget_sparkplug_register' );
/**
* These are the options presented to the user for customizing the widget
**/
function widget_sparkplug_control() {
if ( !empty( $_POST['sidebar'] ) ) {
$sparkie = array(
'days' => $_POST['sparkplug_days'],
'barColor' => $_POST['sparkplug_barColor'],
'do_total' => !empty( $_POST['sparkplug_do_total'] ) ? 'yes' : 'no',
'lineColor' => $_POST['sparkplug_lineColor']
);
update_option( 'sparkplug_options', $sparkie );
}
$sparks = get_option( 'sparkplug_options' );
$days = !empty( $sparks['days'] ) ? $sparks['days'] : 30;
$barColor = !empty( $sparks['barColor'] ) ? $sparks['barColor'] : '#0066cc';
$lineColor = !empty( $sparks['lineColor'] ) ? $sparks['lineColor'] : '#bdbdbd';
$do_total = !empty( $sparks['do_total'] ) ? $sparks['do_total'] : 'yes';
?>