A message from PlayBook

Last night I received my free BlackBerry PlayBook.  I immediately downloaded the updates and began to familiarize myself with it.  The first thing I need to get used to is using my finger pad instead of my nails. Touch response is different than with my phone.  But, I am getting used to it.  After a little trail and error, I have figured out how to load music, photos and files from my desktop.  I have loaded a few free apps to help with the flight layovers I’ll have this weekend.  Later, I think I will give more of a review.

I can successfully report that Gigya sign in with WordPress works on the PlayBook browser. I typed this post using my PlayBook.

Updates to the site

Well, it has been some time since I’ve made any updates to the site.  I initially used a theme that gave a portfolio look to the site.  Unfortunately it was a little hokey, so I wanted to update it.  In the last year I also picked up a new hobby and a wife, in that order.  The wife-to-be had an interest in screen writing, so I decided that I would take over a Meetup group that would help me help her get a screen play published.  Since I took over The RTP “Let’s Make a Movie” Meetup Group, I have done two movie projects and am currently working on the third.  So, I decided that I would add movie work on this site as well.  With that in mind, a overhaul was needed.

The first thing I needed to do was fix the theme.  I found a couple I liked, but they broke the code highlighter.  I stumbled upon this theme by RedEvolution, which had a nice color and handled things well, until I installed it.  I figured since it was most of the way there, I would just alter it to get it all the way to my needs.  Initially there wasn’t anything in the theme to prevent Pages that were at the top level from showing, that I didn’t want to show.  I altered the header file that creates the Page navigation to only show the pages that I want. The change of code was inserting the include and sort order values to the page list call.

<div id="user3">
<ul><?php wp_list_pages('depth=1&include=1,2,3,4&sort_column=ID&sort_order=DESC&title_li='); ?></ul> 
</div>
<!--user3-->

Next on the agenda was adding my logo image to the top of the site. The theme display the Blog Title without an option to add a personalized logo. I’m sure there is a plugin to do this, but I found some code to easily alter the header information to add the logo on the WordPress Forum. The thread was two years old, but the code was still valid. The change to the header file adds a div class for the logo, while leaving the Blog Title available to be placed by the style sheet.

<div class="logo"><h1 class="offscreen"><a href="<?php bloginfo('url'); ?>/"><?php bloginfo('name'); ?></a></h1> 
	<div class="bubble"><p><?php bloginfo('description'); ?></p></div>
</div>

The style sheet changes help place the Blog Title off of the visible page and sets the background image of the div to the logo image file residing in the images directory. I also had to do some creative padding to get the logo and Blog Tagline to show in the right place.

.logo{
	position:relative; 
	z-index:1;
	background: url(images/header-logo.png) no-repeat top left;
	padding:12px 0 0 0;
}
.offscreen {
	font-size: 0;
	text-indent: -999em;
}
.bubble p{
color:#fff;
margin:0;
padding:0 0 0 30px;
}

Finally, I initially wanted my logo to change whenever the Category or Page changed. For example: if the Films Page was selected, then the logo would change to the DeanLogic Films logo. Unfortunately I haven’t gotten it all sorted out. I did manage to update the header to display the Category Description and Image for the selected Post. This code change to the header file can be found under the WordPress Codex. Simply just cycle through each of the categories for the post and get the Category Description and Category Slug. Then you will need to upload images to correspond with the image name created from the slug. For multiple categories, just append each description and image code together.

$catCount = 0;
	foreach((get_the_category()) as $category) { 
		if($catCount == 0){
			$catAbout .= $category->category_description;
			$catImage = '<img src="' . get_bloginfo('template_directory') . '/images/' . $category->category_nicename  . '.png" align="right" alt="' . $category->cat_name . '" />'; 
		} else {
			$catAbout = $catAbout . '&nbps;&nbps' . $category->category_description;
			$catImage .= '<img src="' . get_bloginfo('template_directory') . '/images/' . $category->category_nicename  . '.png" align="right" alt="' . $category->cat_name . '" />';
		}
		
		$catCount++;
	}

	$pageContent = $catAbout;

I will probably just create a image for each Page, since the Categories don’t match up exactly to each of the Pages. I initially wanted them to match up, but it didn’t work out logically for me. And while I was typing up this post the solution came to me. I did a search and found a way to get the Category information from the page slug parsed from the URI. It took me a couple of searches, but I finally found good example of using get_term_by. From this information, I updated the header file to include a step if the category was not found due to being a Page and not a Post.

if($catCount == 0){
		$catSlug = current( explode( '/', trim( $_SERVER['REQUEST_URI'], '/' ) ) );
		$tempCategory = get_term_by('slug', $catSlug , 'category');
		$catName = $tempCategory->name;
		$catImage = '<img src="' . get_bloginfo('template_directory') . '/images/category-' . $catSlug . '.png" align="right" alt="' . $catName . '" />';
		$catAbout = $tempCategory->description;
	}

I also had to make a small adjustment for when the site is on the home page. It tends to grab the first post category, so I have a check to see if there’s no URI length and then provide the text from the About Page. Here is the final code.

if(strlen($catSlug) < 1){ 
		$pageData = get_page_by_path('about');
		$catAbout = apply_filters('the_content', $pageData->post_content); 
	} else {
		foreach((get_the_category()) as $category) { 
		if($catCount == 0){
			$catAbout .= $category->category_description;
			$catImage = '<img src="' . get_bloginfo('template_directory') . '/images/category-' . $category->category_nicename  . '.png" align="right" alt="' . $category->cat_name . '" />'; 
		} else {
			$catAbout = $catAbout . '&nbps;&nbps' . $category->category_description;
			$catImage .= '<img src="' . get_bloginfo('template_directory') . '/images/category-' . $category->category_nicename  . '.png" align="right" alt="' . $category->cat_name . '" />';
		}
		
		$catCount++;
		}
	
		if($catCount == 0){
			$tempCategory = get_term_by('slug', $catSlug , 'category');
			$catName = $tempCategory->name;
			$catImage = '<img src="' . get_bloginfo('template_directory') . '/images/category-' . $catSlug . '.png" align="right" alt="' . $catName . '" />';
			$catAbout = $tempCategory->description;
		}
	}

Now all I have to do is make some better Category / Page images and start adding information from the films.

WP People

Tags: xfn, links, blogroll, people
Requires at least: 2.7.1
Tested up to: 2.8.4
Stable tag / Version: 3.4.0
Plugin Download
Support Forum : Hacks and Scripts / wp-people Thread

This hack plug-in will search a post and find names that match database records of people maked with the WP Category in the XFN Links. When it finds a match, it will replace the name with a link to the person. There is a administration screen for adding people and their bios to the database and viewing the current people marked for the filter. More than one person can be linked on a post. A individual name will only be linked once per post.

The original author of the hack stopped supporting it a while ago. I took his original idea and used another hack (acronymit) as a guide to make this work. The original worked with the my-hacks script used in WordPress 1.0.1, so this is beyond the functionality of the original.

If you were using the version 2 of Word Press People, then you will be able to see any current people in WP People. As of version 3.1.0, you can Copy records from the WP People table into the XFN Links databse.

Installation

  1. Upload `wp-people` to the `/wp-content/plugins/` directory
  2. Activate the plugin through the ‘Plugins’ menu in WordPress
  3. Go to the WP People administration page under the Tools section. The first time there will create the “WP People” Category
  4. Go to the Links administration page. Add new Links or add the WP Category to those people who you want to be included in WP People.
  5. Optionally Install the ‘WordPress Force Word Wrapping‘ plugin to prevent the description text from extednig past the popup window area.

Usage

The field on the Links form match up the following way:

  • Name is the Real Name (searched name) in WP People
  • Description is the Nick Name (displayed name) in WP People
  • Web Address is the Link in WP People
  • Advanced Notes is the Description/Bio in WP People
  • Advanced Image Link is the Photo in WP People

WP Mixed Tape

Tags: mixed tape, mp3
Requires at least: 2.7.1
Tested up to: 2.7.1
Stable tag / Version: 1.1.0
Plugin Download
Support Forum : Hacks and Scripts / WP Mixed Tape Thread

This a plugin that will take shortcode for track listings and make a “Mixed Tape” by turning the mp3 file links into music players and creating a play list of the tracks. The play list is a second shortcode that can be placed anywhere in the post. The play list can also show the tracks as a single album by changing the type. This will only show one link to the artist name added to the play list. The play list icon can be changed to show the mixed tape, cd, cd box, head phones or any image. The image should be small enough to fit inside the play list.

There are no admin options for the plugin.

Usage information can be found below.

The plugin uses 1PixelOut‘s standalone WordPress MP3 player, but should not conflict with other plugins using the player.

The plugin creates a search link to MusicBrainz music database for the artist of the track.

Installation

  1. Upload `wp-mixedtape` to the `/wp-content/plugins/` directory
  2. Activate the plugin through the ‘Plugins’ menu in WordPress

Usage

Adding tracks

[ track title="{track title}" artist="{track artist}" url="{track url}" alt="{alt text}" ]

  • track title is the title for the song. This does not change what is displayed in the mp3 player as the title information is taken from the mp3 file.
  • track artist is the artist for the song. This does not change what is displayed in the mp3 player as the title information is taken from the mp3 file.
  • track url is the full url for the song.
  • alt text is the alternate text shown if the player is not displayed. If left empty, it will show the track title.

Adding the mixed tape play list

[ mixedtape title="{mixed tape title}" style="{override styles}" class="{override class}" type="{play list type}" icon="{override icon}" cover="{image url}" artist="{single album artist name}" ]

  • mixed tape title is the title for the mixed tape to be shown at the top of the play list
  • override styles is any extra style attributes to add to the overall play list
  • override class is class name given to override play list for class style
    The style class should be part of the div classes. (ie. div.myMixTapeClass )
  • play list type is the type of play list either MIXEDTAPE or SINGLEALBUM. MIXEDTAPE is the default and will list each artist with the song.
  • override icon is the option to change the icon, choices are TAPE, CD, CDBOX, HEADPHONES.
  • image url is the url for a custom image to override the icon, ALBUMCOVER must be used as the icon value.
  • single album artist name is the name of the artist for the SINGLEALBUM type. It is ingored on the MIXEDTAPE or default type.

Things to do

  • Playing one player shuts off the other one. This was fine with version 1 of the payer, but version 2 doesn’t seem to work.
  • Code clean up – just need to make sure everything is in it’s proper place and such
  • Language translation – not many words in the plugin, so hopefully this won’t take too much time

Change Log

Version 1.1.1

  • Didn’t have the SVN directory properly setup. The audio files were missing.

Version 1.1.0

  • Added the ability to change play list `type` (MIXEDTAPE, SINGLEABLUM)
  • Single album type allows for `artist` value to be added to play list, ignored for mixed tape type
  • Added the ability to change the play list `icon` (TAPE, CD, CDCASE, HEADPHONES, ALBUMCOVER)
  • Added the ability to add change the icon to an defined url image with the `cover` option

Version 1.0.0

  • Inital release.

AHP Sitewide Recent Posts – hack 2.2ish

I received a comment on the main AHP Sitewide Recent Posts hack about having an issue of just displaying the title for the post. The plugin uses a “option code” to determine which items will be displayed for each post. The idea is, you add the value for each option and that total value lets the plugin know which items to show.

	// DISPLAY OPTIONS BITMASK
	$grav_flag = 1;  // gravatar
	$date_flag = 2;  // date
	$auth_flag = 4;  // author name
	$com_flag  = 8;  // comment count
	$name_flag = 16; // blog name
	$post_flag = 32; // post name
	$exc_flag  = 64; // post excerpt
	$cap_flag  = 128; // excerpt capitalization</blockquote>

For example, if you only wanted to show the post name, then the option value would be 32.

<?php ahp_recent_posts(5, 90, 32); ?> 

If you wanted to show the post name and the blog name, then the option value would be 48 (32 + 16).

<?php ahp_recent_posts(5, 90, 48); ?> 

To show all items in the post, the option value is 225;

<?php ahp_recent_posts(5, 90, 225); ?> 

Unfortunately, the display part of the plugin was not set to only display those items, so I update the code to only show the post item if the flag is set to true.

					echo $begin_wrap;
					if($use_date) { echo '<div class="date">' . $thisdate . '</div>'; }
					if($use_grav) {	echo '<div id="avatar" style="margin-left:65px; margin-top:14px;">' . $thisgravatar . '</div>';	}
					if($use_post) { echo '<h3>' . $this_postname . '</h3>'; }
					if($use_date) { echo '<div class="postmetadata">&nbsp;@ ' . $thistime;	}
					if($use_auth) { echo '<br /><div id="author">' . $thisauthor . '</div>'; }
					if($use_name) { echo '<br /><div id="blogname">&nbsp;' . $this_blogname . '</div>'; }
					if($use_com) { echo '<div id="comment"> | ' . $thiscomment . '</div></div>'; }
					if($use_exc) { echo '<div id="excerpt">' . $thisexcerpt . '</div>'; }
					echo $end_wrap  . "\n";

Thanks to Kit at http://kitmobley.com/ for pointing out the issue to me.

The complete code

<?php

/*
Plugin Name: AHP Sitewide Recent Posts for WordPress MU
Plugin URI: http://www.metablog.us/blogging/ahp-recent-posts-plugin-for-wordpress-mu/
Description: Retrieves a highly customizable list of the most recent sitewide posts in a WordPress MU installation. Automatically excludes blog ID 1 (main blog), and post ID 1 (first "Hello World" posts of new blogs).
Author: Aziz Poonawalla
Author URI: http://metablog.us

FUNCTION ARGUMENTS

$how_many: how many recent posts are being displayed
$how_long: time frame to choose recent posts from (in days)
$optmask: bitmask for various display options (default: 255)
	DISPLAY OPTIONS BITMASK
	1;  // gravatar
	2;  // date
	4;  // author name
	8;  // comment count
	16; // blog name
	32; // post name
	64; // post excerpt
	128; // excerpt capitalization
$exc_size: size of excerpt in words (default: 30)
$begin_wrap: start html code (default: <li class="ahp_recent-posts">)
$end_wrap: end html code to adapt to different themes (default: </li>)

SAMPLE FUNCTION CALL

to show 5 posts from recent 30 days: <?php ahp_recent_posts(5, 30);  ?>

SAMPLE CSS

gravatar styling:  img.avatar-24 { float: left; padding: 0px; border: none; margin: 4px; clear: left; }
LI styling: li.ahp-recent-posts { list-style-type: none ;}
excerpt styling: .ahp-excerpt { margin-top: 2px }

TODO:

- link gravatar icon to Extended Profile in buddypress, if installed
- widgetize
- show more than one post per blog

CHANGELOG
Version 0.8
Update Author: Dean Logan
Update Author URI: http://www.dean-logan.com
- Altered the post query to be a count
- added a "per site" limit for the number of posts per site
- modified closing call to include the "per site" limit

Version 0.7
Update Author: Dean Logan
Update Author URI: http://www.dean-logan.com
- altered loops add an array to store comments
- sort comment array and then display
- altered display layout

Version 0.6
Update Author: Aziz Poonawalla
Update Author URI: http://metablog.us
- added comment count display option
- added enable/disable excerpt capitalization
- consolidated title/name of post display options into bitmask
- reduced number of required arguments
- added class name ahp-recent-posts to default start html LI tags
- added class ahp-excerpt to excerpt block

Version 0.5
Update Author: Aziz Poonawalla
Update Author URI: http://metablog.us
- changed gravatar link to point to all posts by author on main blog (ID = 1).
- added date string, author output
- implemented bitmask to control gravatar, date, author output
- consolidated numwords argument with display argument

Version 0.4.1
Update Author: Aziz Poonawalla
Update Author URI: http://metablog.us
- added gravatar support, icon size 24px
- gravatar can be styled by img.avatar-24 in your css file
- gravatar image links to author's blog
- capitalization of first five words of the excerpt

Version 0.4.0
Update Author: Aziz Poonawalla
Update Author URI: http://metablog.us
- added exclusions for first blog, first post, enabled post excerpt

Version: 0.32
Update Author: G. Morehouse
Update Author URI: http://wiki.evernex.com/index.php?title=Wordpress_MU_sitewide_recent_posts_plugin

Version: 0.31
Update Author: Sven Laqua
Update Author URI: http://www.sl-works.de/

Version: 0.3
Author: Ron Rennick
Author URI: http://atypicalhomeschool.net/
*/

function ahp_recent_posts($how_many, $how_long, $optmask = 255, $exc_size = 30, $per_site = 5, $begin_wrap = '<li class="ahp_recent-posts">', $end_wrap = '</li>') {
	global $wpdb;
	$counter = 0;

	// EDIT THESE TO CUSTOMIZE THE OUTPUT
	$debug = 0;
	$blog_prefix = "Posted from ";
	$post_prefix = "";
	$auth_prefix = 'Posted by ';
	$com_prefix = ' &nbsp; currently ';
	$date_format = 'D M jS, Y';
	$grav_size = 60;

	// DISPLAY OPTIONS BITMASK
	$grav_flag = 1;  // gravatar
	$date_flag = 2;  // date
	$auth_flag = 4;  // author name
	$com_flag  = 8;  // comment count
	$name_flag = 16; // blog name
	$post_flag = 32; // post name
	$exc_flag  = 64; // post excerpt
	$cap_flag  = 128; // excerpt capitalization

	// set the various option flags
	if ($optmask & $grav_flag) { $use_grav = 1; } else { $use_grav = 0; }
	if ($optmask & $date_flag) { $use_date = 1; } else { $use_date = 0; }
	if ($optmask & $auth_flag) { $use_auth = 1; } else { $use_auth = 0; }
	if ($optmask & $com_flag)  { $use_com  = 1; } else { $use_com = 0;  }
	if ($optmask & $name_flag) { $use_name = 1; } else { $use_name = 0; }

	if ($optmask & $post_flag) { $use_post = 1; } else { $use_post = 0; }
	if ($optmask & $exc_flag)  { $use_exc  = 1; } else { $use_exc = 0;  }
	if ($optmask & $cap_flag)  { $use_cap  = 1; } else { $use_cap = 0;  }

	// debug block
	if ($debug) {
		echo '<hr>'.'opt = '.$optmask.': grav = '.$use_grav.', date = '.$use_date
		.', auth = '.$use_auth.', use_com = '.$use_com.', use_name = '.$use_name
		.', use_post = '.$use_post.', use_exc = '.$use_exc.', use_cap = '.$use_cap;
	}

	// get a list of blogs in order of most recent update. show only public and nonarchived/spam/mature/deleted
	$blogs = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs WHERE
		public = '1' AND archived = '0' AND mature = '0' AND spam = '0' AND deleted = '0' AND
		last_updated >= DATE_SUB(CURRENT_DATE(), INTERVAL $how_long DAY)
		ORDER BY last_updated DESC");

	$postArray = array();
	$i = 0;
	if ($blogs) {
		echo '<ul class="ahp_recent-posts">' . "\n";
		foreach ($blogs as $blog) {

			// we need _posts, _comments, and _options tables for this to work
		    $blogPostsTable = "wp_".$blog."_posts";

			// debug block
			if ($debug) {
				echo '<hr>processing blog '.$blog.' = <a href="'.
				$options[0]->option_value.'">'.$options[1]->option_value.'</a> <br>';
			}

			// fetch the ID, post title, post content, post date, and user's email for the latest post
			$sqlCount = "SELECT count($blogPostsTable.ID) ";
			$sqlSelect = "SELECT $blogPostsTable.post_date, $blog AS this_blog, $blogPostsTable.ID, $blogPostsTable.post_title,
				$blogPostsTable.post_content, wp_users.display_name,
				wp_users.user_email, wp_users.user_login ";
			$sqlFrom = "	FROM $blogPostsTable, wp_users
				WHERE wp_users.ID = $blogPostsTable.post_author
				AND post_status = 'publish' AND post_type = 'post'
				AND post_date >= DATE_SUB(CURRENT_DATE(), INTERVAL $how_long DAY)
				AND $blogPostsTable.id > 1 ";
			// build the count query
			$sql = $sqlCount . '' .  $sqlFrom;
			$postcount = $wpdb->get_var($sql);
			// if posts are found, cycle through the count and get the correct number of posts
			if($postcount > 0) {
				if ($postcount > $per_site) {
					$site_limit = $per_site;
				} else {
					$site_limit = $postcount;
				}
				for($x = 0; $x < $site_limit; $x++){
					$sqlOrder = "	ORDER BY $blogPostsTable.post_date DESC limit $x,1";
					$sql = $sqlSelect . '' .  $sqlFrom .''. $sqlOrder;
					$postArray[$i] = $wpdb->get_row($sql, ARRAY_A);
					$i++;
				}
			}
		}

		if(count($postArray) > 0){
			array_multisort($postArray, SORT_DESC);
			while ($counter < count($postArray)){

				// debug block
				if ($debugflag) {
					echo 'processing thispost ID = ' . $postArray[$counter]['ID'] . '<br>';
					echo 'post_title = ' . $postArray[$counter]['post_title'] . '<br>';
					echo 'post_content = ' . $postArray[$counter]['post_content'] . '<br>';
					echo 'post_date = ' . $postArray[$counter]['post_date'] . '<br>';
					echo 'display_name = ' . $postArray[$counter]['display_name'] . '<br>';
					echo 'user_email = ' . $postArray[$counter]['user_email'] . '<br>';
					echo 'user_login = ' . $postArray[$counter]['user_login'] . '<br>';
					echo 'site_url = ' . $postArray[$counter]['option_value'] . '<br>';
				}

				// get post ID
				$thispostID = $postArray[$counter]['ID'];

				// get permalink by ID.  check wp-includes/wpmu-functions.php
				$thispermalink = get_blog_permalink( $postArray[$counter]['this_blog'], $thispostID);

				// set blog tables
				$blogOptionsTable = "wp_" . $postArray[$counter]['this_blog'] . "_options";
				$blogCommentsTable = "wp_" . $postArray[$counter]['this_blog'] . "_comments";

				// get blog name,  URL
				if ($use_name) {

					$options = $wpdb->get_results("SELECT option_value FROM
					$blogOptionsTable WHERE option_name IN ('siteurl','blogname')
					ORDER BY option_name DESC");

					$blog_link = $options[0]->option_value;
					$blog_name = $options[1]->option_value;
					$this_blogname = $blog_prefix.'<a href="' . $blog_link . '">' . $blog_name . '</a>';

				} else { $this_blogname = ''; }

				// get comments
				if ($use_com) {

					// sql query for all comments on the current post
					$thesecomments = $wpdb->get_results("SELECT comment_ID
					FROM $blogCommentsTable
					WHERE comment_post_ID = $thispostID");

					// count total number of comments
					$num_comments = sizeof($thesecomments);

					// pretty text
					if ($num_comments == 0) { $thiscomment = $com_prefix.'no comments'; }
					elseif ($num_comments == 1) { $thiscomment = $com_prefix.'one comment'; }
					else { $thiscomment = $com_prefix.$num_comments.' comments'; }

				} else { $thiscomment = ''; }

				// get author
				if ($use_auth) {
					$thisauthor = $auth_prefix.$postArray[$counter]['display_name'];
				} else { $thisauthor = ''; }

				// get author's posts link
				$thisuser = $thispost[0]->user_login;
				$thisuser_url = $thisbloglink."author/".$thisuser;

				// get gravatar
				if ($use_grav) {
					$grav_img = get_avatar( $postArray[$counter]['user_email'] , $grav_size );
					$thisgravatar = '<a href="'.$thisuser_url.'">'.$grav_img.'</a>';
				} else { $thisgravatar = ''; }

				// get post date (nicely formatted)
				if ($use_date) {
					//$thisdate = date($date_format, strtotime( $thispost[0]->post_date )) ;
					$thisdate = '<span class="dateMonth">' . date('M', strtotime( $postArray[$counter]['post_date'] )) . '</span>';
					$thisdate .= '<span class="dateDay">' . date('d', strtotime( $postArray[$counter]['post_date'] )) . '</span>';
					$thisdate .= '<span class="dateYear">' . date('Y', strtotime( $postArray[$counter]['post_date'] )) . '</span>';
					$thistime = date('g:i a', strtotime( $postArray[$counter]['post_date'] ));
				} else { $thisdate = ''; }

				// get post name
				if ($use_post) {
					$this_postname = $post_prefix . '<a href="' . $thispermalink . '">'  .$postArray[$counter]['post_title'] . '</a><br>';
				} else {
					$this_postname = ''; }

				if ($use_exc) {
					if ($exc_size == 0) {
						$thisexcerpt = '';
					} else {
						// get post content and truncate to (numwords) words
						$thiscontent = strip_tags( $postArray[$counter]['post_content'] );
						preg_match("/([\S]+\s*){0,$exc_size}/", $thiscontent, $regs);

						if ($use_cap) {
							// build the excerpt html block, capitalize first five words
							$trunc_content = explode( ' ', trim($regs[0]) , 6 );
							$exc_str = strtoupper($trunc_content[0]).' '
							.strtoupper($trunc_content[1]).' '
							.strtoupper($trunc_content[2]).' '
							.strtoupper($trunc_content[3]).' '
							.strtoupper($trunc_content[4]).' '
							.$trunc_content[5].'... ';
						} else { $exc_str = trim($regs[0]); }

						$thisexcerpt = '<span style="ahp-excerpt">'.$exc_str
						.'<a href="'.$thispermalink.'">'
						.'&raquo;&raquo;MORE'.'</a></span>';
					}
				} else { $thisexcerpt = ''; }

					echo $begin_wrap;
					if($use_date) { echo '<div class="date">' . $thisdate . '</div>'; }
					if($use_grav) {	echo '<div id="avatar" style="margin-left:65px; margin-top:14px;">' . $thisgravatar . '</div>';	}
					if($use_post) { echo '<h3>' . $this_postname . '</h3>'; }
					if($use_date) { echo '<div class="postmetadata">&nbsp;@ ' . $thistime;	}
					if($use_auth) { echo '<br /><div id="author">' . $thisauthor . '</div>'; }
					if($use_name) { echo '<br /><div id="blogname">&nbsp;' . $this_blogname . '</div>'; }
					if($use_com) { echo '<div id="comment"> | ' . $thiscomment . '</div></div>'; }
					if($use_exc) { echo '<div id="excerpt">' . $thisexcerpt . '</div>'; }
					echo $end_wrap  . "\n";

					$counter++;

				// don't go over the limit
				if($counter >= ($how_many)) {
					break;
				}
			}
		}
	}
	else {
		//echo "no recent posts meet the criteria...<br>";
	}
}
?>

AHP Sitewide Recent Posts – hack 2.1ish

It’s funny how things happen to fall into place. Jacob over at 912Candidates.org was lamenting over the issue of not keeping the main page up-to-date. A few of the state sites have been posting updates, but Jacob has been a little busy and didn’t have anything to put on his front page. If only, he thought, there was a way to show the posts from the other sites. Well, what do you know, I was just playing around with a plug-in that did that. After pointing Jacob to the example on NC Freedom, he came back with some changes. The biggest change that he wanted was to use the flag icon I was using for each site instead of the avatar for the user.

When I created the one off Gold List plug-in for the 912Candidate site, I used avatar images that depict state flags. I decided to be a little creative and spice up the list with these flags and it turned out really well. Initially I had some issues getting the flags to work, because I needed an easy way to get the file name based off of the site. Each state site used the state abbreviation and the file names used the full name of the state. I didn’t want to rename all the image files, so I created an array to hold the values.

// state list
$stateList = array( 'AL' => 'Alabama', 'AK' => 'Alaska', 'AZ' => 'Arizona', 'AR' => 'Arkansas', 'CA' => 'California',
'CO' => 'Colorado', 'CT' => 'Connecticut', 'DE' => 'Delaware', 'DC' => 'District of Columbia', 'FL' => 'Florida',
'GA' => 'Georgia', 'HI' => 'Hawaii', 'ID' => 'Idaho', 'IL' => 'Illinois', 'IN' => 'Indiana', 'IA' => 'Iowa',
'KS' => 'Kansas', 'KY' => 'Kentucky', 'LA' => 'Louisiana', 'ME' => 'Maine', 'MD' => 'Maryland', 'MA' => 'Massachusetts',
'MI' => 'Michigan', 'MN' => 'Minnesota', 'MS' => 'Mississippi', 'MO' => 'Missouri', 'MT' => 'Montana', 'NE' => 'Nebraska',
'NV' => 'Nevada', 'NH' => 'New Hampshire', 'NJ' => 'New Jersey', 'NM' => 'New Mexico', 'NY' => 'New York', 'NC' => 'North Carolina',
'ND' => 'North Dakota', 'OH' => 'Ohio', 'OK' => 'Oklahoma', 'OR' => 'Oregon', 'PA' => 'Pennsylvania', 'RI' => 'Rhode Island',
'SC' => 'South Carolina', 'SD' => 'South Dakota', 'TN' => 'Tennessee', 'TX' => 'Texas', 'UT' => 'Utah', 'VT' => 'Vermont',
'VA' => 'Virginia', 'WA' => 'Washington', 'WV' => 'West Virginia', 'WI' => 'Wisconsin',  'WY' => 'Wyoming'	);

By having the array, I could always add more values or use either the initial to get the name or the name to get the initial.
I updated the code to search through the array and match up a substring of the blog link to the state abbreviations. If an abbreviation matched, then the code for adding the image is added to the variable. I replaced the avatar variable value with this new variable. It makes for a nice effect on the main page of 912Candidates.

foreach ($stateList as $abbr => $stateName){
if($abbr == strtoupper(substr($blog_link,strpos($blog_link,'org') + 4, 2))){
$this_blog_image = '<img src="/resources/flags/' .
$stateName .
'.gif" height="55" width="55" border="0" align="left" style="padding-right: 5pt;" />';
}
}

AHP Sitewide Recent Posts – hack 2.0

After completing the initial hack, I went back to my production site and noticed that only one post from each site was being displayed. I believe this was the original intent of the plug-in, but I think that needs to be changed, so I made a little improvement. So now, the plug-in lets you set a max limit on the number of post per site. If you have a lot of sites, you’ll probably want to up the number of posts to display in general. Here is what I did.

The first thing I did was to split up the sql code into different parts.
The first part was changed to a simple count of the number of posts available.

$sqlCount = "SELECT count($blogPostsTable.ID) ";

The second part is the actual full select statement to get the post information.

$sqlSelect = "SELECT $blogPostsTable.post_date, $blog AS this_blog, $blogPostsTable.ID, $blogPostsTable.post_title,
			$blogPostsTable.post_content, wp_users.display_name,
			wp_users.user_email, wp_users.user_login ";

The third part is the rest of the select statement, except the order by, which will be used by both select statements.

$sqlFrom = "	FROM $blogPostsTable, wp_users
			WHERE wp_users.ID = $blogPostsTable.post_author
			AND post_status = 'publish' AND post_type = 'post'
			AND post_date >= DATE_SUB(CURRENT_DATE(), INTERVAL $how_long DAY)
			AND $blogPostsTable.id > 1 ";

The final part is the order by and limit statement that will be dynamically created based on the number of posts found.

$sqlOrder = "ORDER BY $blogPostsTable.post_date DESC limit $x,1";

As before, there is an initial query to get the number of posts available. But, I changed it to a var query, which will reduce the call to the database.

// build the count query
$sql = $sqlCount . '' .  $sqlFrom;
$postcount = $wpdb->get_var($sql);

Once I get that value back, if there are more than 0 posts, then I loop through the call getting each post and inserting it into the array.
I have to first make sure that the number of posts is not less than the per site post limit. If it is less, I use that number, but if it is more, then I just use the per site limit. The “x” counter lets me start the limit at the proper post and only go 1 row from that point. For example, when $x is 4, it goes to row 4 and gets only that one row. I rebuild the query and then get the post data to insert into the array, with the $i counting the array rows.

if($postcount > 0) {
	if ($postcount > $per_site) {
		$site_limit = $per_site;
	} else {
		$site_limit = $postcount;
	}
	for($x = 0; $x < $site_limit; $x++){
		$sqlOrder = "	ORDER BY $blogPostsTable.post_date DESC limit $x,1";
		$sql = $sqlSelect . '' .  $sqlFrom .''. $sqlOrder;
		$postArray[$i] = $wpdb->get_row($sql, ARRAY_A);
		$i++;
	}
}

To make sure the page formatting doesn’t break, I added the “per site” limit to the “how many” limit to make sure that the counter doesn’t go to high.
I changed out the $a counter with the $counter variable. And made sure that the “break” call was inside the while loop.

Finally I added a parameter to the function and defaulted the value at 2 per site.

function ahp_recent_posts($how_many, $how_long, $optmask = 255, $exc_size = 30, $per_site = 2, $begin_wrap = '<li class="ahp_recent-posts">', $end_wrap = '</li>')

Here is the complete code

<?php

/*
Plugin Name: AHP Sitewide Recent Posts for WordPress MU
Plugin URI: http://www.metablog.us/blogging/ahp-recent-posts-plugin-for-wordpress-mu/
Description: Retrieves a highly customizable list of the most recent sitewide posts in a WordPress MU installation. Automatically excludes blog ID 1 (main blog), and post ID 1 (first "Hello World" posts of new blogs).
Author: Aziz Poonawalla
Author URI: http://metablog.us

FUNCTION ARGUMENTS

$how_many: how many recent posts are being displayed
$how_long: time frame to choose recent posts from (in days)
$optmask: bitmask for various display options (default: 255)
	DISPLAY OPTIONS BITMASK
	1;  // gravatar
	2;  // date
	4;  // author name
	8;  // comment count
	16; // blog name
	32; // post name
	64; // post excerpt
	128; // excerpt capitalization
$exc_size: size of excerpt in words (default: 30)
$begin_wrap: start html code (default: <li class="ahp_recent-posts">)
$end_wrap: end html code to adapt to different themes (default: </li>)

SAMPLE FUNCTION CALL

to show 5 posts from recent 30 days: <?php ahp_recent_posts(5, 30);  ?>

SAMPLE CSS

gravatar styling:  img.avatar-24 { float: left; padding: 0px; border: none; margin: 4px; clear: left; }
LI styling: li.ahp-recent-posts { list-style-type: none ;}
excerpt styling: .ahp-excerpt { margin-top: 2px }

TODO:

- link gravatar icon to Extended Profile in buddypress, if installed
- widgetize
- show more than one post per blog

CHANGELOG
Version 0.8
Update Author: Dean Logan
Update Author URI: http://www.dean-logan.com
- Altered the post query to be a count
- added a "per site" limit for the number of posts per site
- modified closing call to include the "per site" limit

Version 0.7
Update Author: Dean Logan
Update Author URI: http://www.dean-logan.com
- altered loops add an array to store comments
- sort comment array and then display
- altered display layout

Version 0.6
Update Author: Aziz Poonawalla
Update Author URI: http://metablog.us
- added comment count display option
- added enable/disable excerpt capitalization
- consolidated title/name of post display options into bitmask
- reduced number of required arguments
- added class name ahp-recent-posts to default start html LI tags
- added class ahp-excerpt to excerpt block

Version 0.5
Update Author: Aziz Poonawalla
Update Author URI: http://metablog.us
- changed gravatar link to point to all posts by author on main blog (ID = 1).
- added date string, author output
- implemented bitmask to control gravatar, date, author output
- consolidated numwords argument with display argument

Version 0.4.1
Update Author: Aziz Poonawalla
Update Author URI: http://metablog.us
- added gravatar support, icon size 24px
- gravatar can be styled by img.avatar-24 in your css file
- gravatar image links to author's blog
- capitalization of first five words of the excerpt

Version 0.4.0
Update Author: Aziz Poonawalla
Update Author URI: http://metablog.us
- added exclusions for first blog, first post, enabled post excerpt

Version: 0.32
Update Author: G. Morehouse
Update Author URI: http://wiki.evernex.com/index.php?title=Wordpress_MU_sitewide_recent_posts_plugin

Version: 0.31
Update Author: Sven Laqua
Update Author URI: http://www.sl-works.de/

Version: 0.3
Author: Ron Rennick
Author URI: http://atypicalhomeschool.net/
*/

function ahp_recent_posts($how_many, $how_long, $optmask = 255, $exc_size = 30, $per_site = 5, $begin_wrap = '<li class="ahp_recent-posts">', $end_wrap = '</li>') {
	global $wpdb;
	$counter = 0;

	// EDIT THESE TO CUSTOMIZE THE OUTPUT
	$debug = 0;
	$blog_prefix = "Posted from ";
	$post_prefix = "";
	$auth_prefix = 'Posted by ';
	$com_prefix = ' &nbsp; currently ';
	$date_format = 'D M jS, Y';
	$grav_size = 60;

	// DISPLAY OPTIONS BITMASK
	$grav_flag = 1;  // gravatar
	$date_flag = 2;  // date
	$auth_flag = 4;  // author name
	$com_flag  = 8;  // comment count
	$name_flag = 16; // blog name
	$post_flag = 32; // post name
	$exc_flag  = 64; // post excerpt
	$cap_flag  = 128; // excerpt capitalization

	// set the various option flags
	if ($optmask & $grav_flag) { $use_grav = 1; } else { $use_grav = 0; }
	if ($optmask & $date_flag) { $use_date = 1; } else { $use_date = 0; }
	if ($optmask & $auth_flag) { $use_auth = 1; } else { $use_auth = 0; }
	if ($optmask & $com_flag)  { $use_com  = 1; } else { $use_com = 0;  }
	if ($optmask & $name_flag) { $use_name = 1; } else { $use_name = 0; }

	if ($optmask & $post_flag) { $use_post = 1; } else { $use_post = 0; }
	if ($optmask & $exc_flag)  { $use_exc  = 1; } else { $use_exc = 0;  }
	if ($optmask & $cap_flag)  { $use_cap  = 1; } else { $use_cap = 0;  }

	// debug block
	if ($debug) {
		echo '<hr>'.'opt = '.$optmask.': grav = '.$use_grav.', date = '.$use_date
		.', auth = '.$use_auth.', use_com = '.$use_com.', use_name = '.$use_name
		.', use_post = '.$use_post.', use_exc = '.$use_exc.', use_cap = '.$use_cap;
	}

	// get a list of blogs in order of most recent update. show only public and nonarchived/spam/mature/deleted
	$blogs = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs WHERE
		public = '1' AND archived = '0' AND mature = '0' AND spam = '0' AND deleted = '0' AND
		last_updated >= DATE_SUB(CURRENT_DATE(), INTERVAL $how_long DAY)
		ORDER BY last_updated DESC");

	$postArray = array();
	$i = 0;
	if ($blogs) {
		echo '<ul class="ahp_recent-posts">' . "\n";
		foreach ($blogs as $blog) {

			// we need _posts, _comments, and _options tables for this to work
		    $blogPostsTable = "wp_".$blog."_posts";

			// debug block
			if ($debug) {
				echo '<hr>processing blog '.$blog.' = <a href="'.
				$options[0]->option_value.'">'.$options[1]->option_value.'</a> <br>';
			}

			// fetch the ID, post title, post content, post date, and user's email for the latest post
			$sqlCount = "SELECT count($blogPostsTable.ID) ";
			$sqlSelect = "SELECT $blogPostsTable.post_date, $blog AS this_blog, $blogPostsTable.ID, $blogPostsTable.post_title,
				$blogPostsTable.post_content, wp_users.display_name,
				wp_users.user_email, wp_users.user_login ";
			$sqlFrom = "	FROM $blogPostsTable, wp_users
				WHERE wp_users.ID = $blogPostsTable.post_author
				AND post_status = 'publish' AND post_type = 'post'
				AND post_date >= DATE_SUB(CURRENT_DATE(), INTERVAL $how_long DAY)
				AND $blogPostsTable.id > 1 ";
			// build the count query
			$sql = $sqlCount . '' .  $sqlFrom;
			$postcount = $wpdb->get_var($sql);
			// if posts are found, cycle through the count and get the correct number of posts
			if($postcount > 0) {
				if ($postcount > $per_site) {
					$site_limit = $per_site;
				} else {
					$site_limit = $postcount;
				}
				for($x = 0; $x < $site_limit; $x++){
					$sqlOrder = "	ORDER BY $blogPostsTable.post_date DESC limit $x,1";
					$sql = $sqlSelect . '' .  $sqlFrom .''. $sqlOrder;
					$postArray[$i] = $wpdb->get_row($sql, ARRAY_A);
					$i++;
				}
			}
		}

		if(count($postArray) > 0){
			array_multisort($postArray, SORT_DESC);
			while ($counter < count($postArray)){

				// debug block
				if ($debugflag) {
					echo 'processing thispost ID = ' . $postArray[$counter]['ID'] . '<br>';
					echo 'post_title = ' . $postArray[$counter]['post_title'] . '<br>';
					echo 'post_content = ' . $postArray[$counter]['post_content'] . '<br>';
					echo 'post_date = ' . $postArray[$counter]['post_date'] . '<br>';
					echo 'display_name = ' . $postArray[$counter]['display_name'] . '<br>';
					echo 'user_email = ' . $postArray[$counter]['user_email'] . '<br>';
					echo 'user_login = ' . $postArray[$counter]['user_login'] . '<br>';
					echo 'site_url = ' . $postArray[$counter]['option_value'] . '<br>';
				}

				// get post ID
				$thispostID = $postArray[$counter]['ID'];

				// get permalink by ID.  check wp-includes/wpmu-functions.php
				$thispermalink = get_blog_permalink( $postArray[$counter]['this_blog'], $thispostID);

				// set blog tables
				$blogOptionsTable = "wp_" . $postArray[$counter]['this_blog'] . "_options";
				$blogCommentsTable = "wp_" . $postArray[$counter]['this_blog'] . "_comments";

				// get blog name,  URL
				if ($use_name) {

					$options = $wpdb->get_results("SELECT option_value FROM
					$blogOptionsTable WHERE option_name IN ('siteurl','blogname')
					ORDER BY option_name DESC");

					$blog_link = $options[0]->option_value;
					$blog_name = $options[1]->option_value;
					$this_blogname = $blog_prefix.'<a href="' . $blog_link . '">' . $blog_name . '</a>';

				} else { $this_blogname = ''; }

				// get comments
				if ($use_com) {

					// sql query for all comments on the current post
					$thesecomments = $wpdb->get_results("SELECT comment_ID
					FROM $blogCommentsTable
					WHERE comment_post_ID = $thispostID");

					// count total number of comments
					$num_comments = sizeof($thesecomments);

					// pretty text
					if ($num_comments == 0) { $thiscomment = $com_prefix.'no comments'; }
					elseif ($num_comments == 1) { $thiscomment = $com_prefix.'one comment'; }
					else { $thiscomment = $com_prefix.$num_comments.' comments'; }

				} else { $thiscomment = ''; }

				// get author
				if ($use_auth) {
					$thisauthor = $auth_prefix.$postArray[$counter]['display_name'];
				} else { $thisauthor = ''; }

				// get author's posts link
				$thisuser = $thispost[0]->user_login;
				$thisuser_url = $thisbloglink."author/".$thisuser;

				// get gravatar
				if ($use_grav) {
					$grav_img = get_avatar( $postArray[$counter]['user_email'] , $grav_size );
					$thisgravatar = '<a href="'.$thisuser_url.'">'.$grav_img.'</a>';
				} else { $thisgravatar = ''; }

				// get post date (nicely formatted)
				if ($use_date) {
					//$thisdate = date($date_format, strtotime( $thispost[0]->post_date )) ;
					$thisdate = '<span class="dateMonth">' . date('M', strtotime( $postArray[$counter]['post_date'] )) . '</span>';
					$thisdate .= '<span class="dateDay">' . date('d', strtotime( $postArray[$counter]['post_date'] )) . '</span>';
					$thisdate .= '<span class="dateYear">' . date('Y', strtotime( $postArray[$counter]['post_date'] )) . '</span>';
					$thistime = date('g:i a', strtotime( $postArray[$counter]['post_date'] ));
				} else { $thisdate = ''; }

				// get post name
				if ($use_post) {
					$this_postname = $post_prefix . '<a href="' . $thispermalink . '">'  .$postArray[$counter]['post_title'] . '</a><br>';
				} else {
					$this_postname = ''; }

				if ($use_exc) {
					if ($exc_size == 0) {
						$thisexcerpt = '';
					} else {
						// get post content and truncate to (numwords) words
						$thiscontent = strip_tags( $postArray[$counter]['post_content'] );
						preg_match("/([\S]+\s*){0,$exc_size}/", $thiscontent, $regs);

						if ($use_cap) {
							// build the excerpt html block, capitalize first five words
							$trunc_content = explode( ' ', trim($regs[0]) , 6 );
							$exc_str = strtoupper($trunc_content[0]).' '
							.strtoupper($trunc_content[1]).' '
							.strtoupper($trunc_content[2]).' '
							.strtoupper($trunc_content[3]).' '
							.strtoupper($trunc_content[4]).' '
							.$trunc_content[5].'... ';
						} else { $exc_str = trim($regs[0]); }

						$thisexcerpt = '<span style="ahp-excerpt">'.$exc_str
						.'<a href="'.$thispermalink.'">'
						.'&raquo;&raquo;MORE'.'</a></span>';
					}
				} else { $thisexcerpt = ''; }

					echo $begin_wrap
					. '<div class="date">' . $thisdate . '</div>'
					. '<div id="avatar" style="margin-left:65px; margin-top:14px;">' . $thisgravatar . '</div>'
					. '<h3>' . $this_postname . '</h3>'
					. '<div class="postmetadata">&nbsp;@ ' . $thistime . '<br /><div id="author">' . $thisauthor . '</div>'
					. '<br /><div id="blogname">&nbsp;' . $this_blogname . '</div><div id="comment"> | ' . $thiscomment . '</div></div>'
					. '<div id="excerpt">' . $thisexcerpt . '</div>'
					. $end_wrap  . "\n";

					$counter++;

				// don't go over the limit
				if($counter >= ($how_many)) {
					break;
				}
			}
		}
	}
	else {
		//echo "no recent posts meet the criteria...<br>";
	}
}
?>

Sitewide Recent Posts – hack

I started using WordPress Mu for a couple of sites that I am helping out with. The key to WordPress Mu is the ability to have multiple WordPress sites under the control of a main WordPress site. For example, if you have a common topic, you can have multiple sub-sites under the main site. Examples of these are the two sites I am helping out with 9/12 Candidates and NC Freedom. While 912 Candidates has sub-sites based on each state, NC Freedom uses sub-sites based on regions within the state.

Another part of WordPress Mu is the ability to have a sitewide plug-in. A sitewide plugin can pull information throughout the site. On 912 Candidates I create a sitewide plug-in to handle the Gold List of candidates. It allows each sub-site to enter in their candidate and have the Gold Lists update across the site accordingly.

For the NC Freedom site, I needed a way to show the most recent post from across the site. Luckily, WordPress Mu has a site with plug-ins created for sitewide use. On this site I found AHP Sitewide Recent Posts. Unfortunately there was a slight issue with how the recent posts were returned.

The 0.6.1 version would get each blog based on the update date value and then get each post for those blogs, posting the excerpts as it went through each blog. This meant, that if a blog was updated for any reason, it might have an early update date and it’s posts would show as being newer than other posts that were in reality posted more recently. The best option would be to put all of the posts into an array, then sort the array on the post date and then finally display them in the appropriate order.

The first change I made was to add an array variable and a counter.

	$postArray = array();
	$i = 0;

Next I shortened the loop area to only include the blog and post queries and not display any output. This also included separating out the sql statement for the comment query, since I would be using this twice. First to loop through each comment and then second to insert that comment into the array. The “$i” counter allows each post to be inserted at the next array spot. I also modified the query to put the post date as the first column and the second column as the blog id ($blog AS this_blog). These changes will help with the sorting and permalink structure.

foreach ($blogs as $blog) {

			// we need _posts, _comments, and _options tables for this to work
		    $blogPostsTable = "wp_".$blog."_posts";...

			// fetch the ID, post title, post content, post date, and user's email for the latest post
			$sql = "SELECT $blogPostsTable.post_date, $blog AS this_blog, $blogPostsTable.ID, $blogPostsTable.post_title,
				$blogPostsTable.post_content, wp_users.display_name,
				wp_users.user_email, wp_users.user_login
				FROM $blogPostsTable, wp_users
				WHERE wp_users.ID = $blogPostsTable.post_author
				AND post_status = 'publish' AND post_type = 'post'
				AND post_date >= DATE_SUB(CURRENT_DATE(), INTERVAL $how_long DAY)
				AND $blogPostsTable.id > 1
				ORDER BY $blogPostsTable.post_date DESC limit 0,1";
			$thispost = $wpdb->get_results($sql);


			// if it is found put it into the Array
			if($thispost) {

				$postArray[$i] = $wpdb->get_row($sql, ARRAY_A);
				$i++;
			}
		}

Finally, after the array is done, there is a check to see if there are actual rows in the array. If there are rows, then the array is sorted by the first column, which is the post date. Then the script loops through the array to display each posts.

if(count($postArray) > 0){
	array_multisort($postArray, SORT_DESC);
	for ($a = 0; $a < count($postArray); $a++){

When each row is called, a little different syntax is required. There has to be a reference to the array item, then the column.

$postArray[$a]['post_date']

Here is the complete modified script. The displaying of the post has been modified to fit better with the layout of site I am using it on. Look here for the working example.

<?php

/*
Plugin Name: AHP Sitewide Recent Posts for WordPress MU
Plugin URI: http://www.metablog.us/blogging/ahp-recent-posts-plugin-for-wordpress-mu/
Description: Retrieves a highly customizable list of the most recent sitewide posts in a WordPress MU installation. Automatically excludes blog ID 1 (main blog), and post ID 1 (first "Hello World" posts of new blogs).
Author: Aziz Poonawalla
Author URI: http://metablog.us

FUNCTION ARGUMENTS

$how_many: how many recent posts are being displayed
$how_long: time frame to choose recent posts from (in days)
$optmask: bitmask for various display options (default: 255)
	DISPLAY OPTIONS BITMASK
	1;  // gravatar
	2;  // date
	4;  // author name
	8;  // comment count
	16; // blog name
	32; // post name
	64; // post excerpt
	128; // excerpt capitalization
$exc_size: size of excerpt in words (default: 30)
$begin_wrap: start html code (default: <li class="ahp_recent-posts">)
$end_wrap: end html code to adapt to different themes (default: </li>)

SAMPLE FUNCTION CALL

to show 5 posts from recent 30 days: <?php ahp_recent_posts(5, 30);  ?>

SAMPLE CSS

gravatar styling:  img.avatar-24 { float: left; padding: 0px; border: none; margin: 4px; clear: left; }
LI styling: li.ahp-recent-posts { list-style-type: none ;}
excerpt styling: .ahp-excerpt { margin-top: 2px }

TODO:

- link gravatar icon to Extended Profile in buddypress, if installed
- widgetize
- show more than one post per blog

CHANGELOG
Version 0.7
Update Author: Dean Logan
Update Author URI: http://www.dean-logan.com
- altered loops add an array to store comments
- sort comment array and then display
- altered display layout

Version 0.6
Update Author: Aziz Poonawalla
Update Author URI: http://metablog.us
- added comment count display option
- added enable/disable excerpt capitalization
- consolidated title/name of post display options into bitmask
- reduced number of required arguments
- added class name ahp-recent-posts to default start html LI tags
- added class ahp-excerpt to excerpt block

Version 0.5
Update Author: Aziz Poonawalla
Update Author URI: http://metablog.us
- changed gravatar link to point to all posts by author on main blog (ID = 1).
- added date string, author output
- implemented bitmask to control gravatar, date, author output
- consolidated numwords argument with display argument

Version 0.4.1
Update Author: Aziz Poonawalla
Update Author URI: http://metablog.us
- added gravatar support, icon size 24px
- gravatar can be styled by img.avatar-24 in your css file
- gravatar image links to author's blog
- capitalization of first five words of the excerpt

Version 0.4.0
Update Author: Aziz Poonawalla
Update Author URI: http://metablog.us
- added exclusions for first blog, first post, enabled post excerpt

Version: 0.32
Update Author: G. Morehouse
Update Author URI: http://wiki.evernex.com/index.php?title=Wordpress_MU_sitewide_recent_posts_plugin

Version: 0.31
Update Author: Sven Laqua
Update Author URI: http://www.sl-works.de/

Version: 0.3
Author: Ron Rennick
Author URI: http://atypicalhomeschool.net/
*/

function ahp_recent_posts($how_many, $how_long, $optmask = 255, $exc_size = 30, $begin_wrap = '<li class="ahp_recent-posts">', $end_wrap = '</li>') {
	global $wpdb;
	$counter = 0;

	// EDIT THESE TO CUSTOMIZE THE OUTPUT
	$debug = 0;
	$blog_prefix = "Posted from ";
	$post_prefix = "";
	$auth_prefix = 'Posted by ';
	$com_prefix = ' &nbsp; currently ';
	$date_format = 'D M jS, Y';
	$grav_size = 60;

	// DISPLAY OPTIONS BITMASK
	$grav_flag = 1;  // gravatar
	$date_flag = 2;  // date
	$auth_flag = 4;  // author name
	$com_flag  = 8;  // comment count
	$name_flag = 16; // blog name
	$post_flag = 32; // post name
	$exc_flag  = 64; // post excerpt
	$cap_flag  = 128; // excerpt capitalization

	// set the various option flags
	if ($optmask & $grav_flag) { $use_grav = 1; } else { $use_grav = 0; }
	if ($optmask & $date_flag) { $use_date = 1; } else { $use_date = 0; }
	if ($optmask & $auth_flag) { $use_auth = 1; } else { $use_auth = 0; }
	if ($optmask & $com_flag)  { $use_com  = 1; } else { $use_com = 0;  }
	if ($optmask & $name_flag) { $use_name = 1; } else { $use_name = 0; }

	if ($optmask & $post_flag) { $use_post = 1; } else { $use_post = 0; }
	if ($optmask & $exc_flag)  { $use_exc  = 1; } else { $use_exc = 0;  }
	if ($optmask & $cap_flag)  { $use_cap  = 1; } else { $use_cap = 0;  }

	// debug block
	if ($debug) {
		echo '<hr>'.'opt = '.$optmask.': grav = '.$use_grav.', date = '.$use_date
		.', auth = '.$use_auth.', use_com = '.$use_com.', use_name = '.$use_name
		.', use_post = '.$use_post.', use_exc = '.$use_exc.', use_cap = '.$use_cap;
	}

	// get a list of blogs in order of most recent update. show only public and nonarchived/spam/mature/deleted
	$blogs = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs WHERE
		public = '1' AND archived = '0' AND mature = '0' AND spam = '0' AND deleted = '0' AND
		last_updated >= DATE_SUB(CURRENT_DATE(), INTERVAL $how_long DAY)
		ORDER BY last_updated DESC");

	$postArray = array();
	$i = 0;
	if ($blogs) {
		echo '<ul class="ahp_recent-posts">' . "\n";
		foreach ($blogs as $blog) {

			// we need _posts, _comments, and _options tables for this to work
		    $blogPostsTable = "wp_".$blog."_posts";

			// debug block
			if ($debug) {
				echo '<hr>processing blog '.$blog.' = <a href="'.
				$options[0]->option_value.'">'.$options[1]->option_value.'</a> <br>';
			}

			// fetch the ID, post title, post content, post date, and user's email for the latest post
			$sql = "SELECT $blogPostsTable.post_date, $blog AS this_blog, $blogPostsTable.ID, $blogPostsTable.post_title,
				$blogPostsTable.post_content, wp_users.display_name,
				wp_users.user_email, wp_users.user_login
				FROM $blogPostsTable, wp_users
				WHERE wp_users.ID = $blogPostsTable.post_author
				AND post_status = 'publish' AND post_type = 'post'
				AND post_date >= DATE_SUB(CURRENT_DATE(), INTERVAL $how_long DAY)
				AND $blogPostsTable.id > 1
				ORDER BY $blogPostsTable.post_date DESC limit 0,1";
			$thispost = $wpdb->get_results($sql);


			// if it is found put it into the array
			if($thispost) {

				$postArray[$i] = $wpdb->get_row($sql, ARRAY_A);
				$i++;
			}
		}

		if(count($postArray) > 0){
			array_multisort($postArray, SORT_DESC);
			for ($a = 0; $a < count($postArray); $a++){
				// debug block
				if ($debugflag) {
					echo 'processing thispost ID = ' . $postArray[$a]['ID'] . '<br>';
					echo 'post_title = ' . $postArray[$a]['post_title'] . '<br>';
					echo 'post_content = ' . $postArray[$a]['post_content'] . '<br>';
					echo 'post_date = ' . $postArray[$a]['post_date'] . '<br>';
					echo 'display_name = ' . $postArray[$a]['display_name'] . '<br>';
					echo 'user_email = ' . $postArray[$a]['user_email'] . '<br>';
					echo 'user_login = ' . $postArray[$a]['user_login'] . '<br>';
					echo 'site_url = ' . $postArray[$a]['option_value'] . '<br>';
				}

				// get post ID
				$thispostID = $postArray[$a]['ID'];

				// get permalink by ID.  check wp-includes/wpmu-functions.php
				$thispermalink = get_blog_permalink( $postArray[$a]['this_blog'], $thispostID);

				// set blog tables
				$blogOptionsTable = "wp_" . $postArray[$a]['this_blog'] . "_options";
				$blogCommentsTable = "wp_" . $postArray[$a]['this_blog'] . "_comments";

				// get blog name,  URL
				if ($use_name) {

					$options = $wpdb->get_results("SELECT option_value FROM
					$blogOptionsTable WHERE option_name IN ('siteurl','blogname')
					ORDER BY option_name DESC");

					$blog_link = $options[0]->option_value;
					$blog_name = $options[1]->option_value;
					$this_blogname = $blog_prefix.'<a href="' . $blog_link . '">' . $blog_name . '</a>';

				} else { $this_blogname = ''; }

				// get comments
				if ($use_com) {

					// sql query for all comments on the current post
					$thesecomments = $wpdb->get_results("SELECT comment_ID
					FROM $blogCommentsTable
					WHERE comment_post_ID = $thispostID");

					// count total number of comments
					$num_comments = sizeof($thesecomments);

					// pretty text
					if ($num_comments == 0) { $thiscomment = $com_prefix.'no comments'; }
					elseif ($num_comments == 1) { $thiscomment = $com_prefix.'one comment'; }
					else { $thiscomment = $com_prefix.$num_comments.' comments'; }

				} else { $thiscomment = ''; }

				// get author
				if ($use_auth) {
					$thisauthor = $auth_prefix.$postArray[$a]['display_name'];
				} else { $thisauthor = ''; }

				// get author's posts link
				$thisuser = $thispost[0]->user_login;
				$thisuser_url = $thisbloglink."author/".$thisuser;

				// get gravatar
				if ($use_grav) {
					$grav_img = get_avatar( $postArray[$a]['user_email'] , $grav_size );
					$thisgravatar = '<a href="'.$thisuser_url.'">'.$grav_img.'</a>';
				} else { $thisgravatar = ''; }

				// get post date (nicely formatted)
				if ($use_date) {
					//$thisdate = date($date_format, strtotime( $thispost[0]->post_date )) ;
					$thisdate = '<span class="dateMonth">' . date('M', strtotime( $postArray[$a]['post_date'] )) . '</span>';
					$thisdate .= '<span class="dateDay">' . date('d', strtotime( $postArray[$a]['post_date'] )) . '</span>';
					$thisdate .= '<span class="dateYear">' . date('Y', strtotime( $postArray[$a]['post_date'] )) . '</span>';
					$thistime = date('g:i a', strtotime( $postArray[$a]['post_date'] ));
				} else { $thisdate = ''; }

				// get post name
				if ($use_post) {
					$this_postname = $post_prefix . '<a href="' . $thispermalink . '">'  .$postArray[$a]['post_title'] . '</a><br>';
				} else { $this_postname = ''; }

				if ($use_exc) {

					if ($exc_size == 0) { $thisexcerpt = ''; }
					else {
						// get post content and truncate to (numwords) words
						$thiscontent = strip_tags( $postArray[$a]['post_content'] );
						preg_match("/([\S]+\s*){0,$exc_size}/", $thiscontent, $regs);

						if ($use_cap) {
							// build the excerpt html block, capitalize first five words
							$trunc_content = explode( ' ', trim($regs[0]) , 6 );
							$exc_str = strtoupper($trunc_content[0]).' '
							.strtoupper($trunc_content[1]).' '
							.strtoupper($trunc_content[2]).' '
							.strtoupper($trunc_content[3]).' '
							.strtoupper($trunc_content[4]).' '
							.$trunc_content[5].'... ';
						} else { $exc_str = trim($regs[0]); }

						$thisexcerpt = '<span style="ahp-excerpt">'.$exc_str
						.'<a href="'.$thispermalink.'">'
						.'&raquo;&raquo;MORE'.'</a></span>';
					}
				} else { $thisexcerpt = ''; }

					echo $begin_wrap
					. '<div class="date">' . $thisdate . '</div>'
					. '<div id="avatar" style="margin-left:65px; margin-top:14px;">' . $thisgravatar . '</div>'
					. '<h3>' . $this_postname . '</h3>'
					. '<div class="postmetadata">&nbsp;@ ' . $thistime . '<br /><div id="author">' . $thisauthor . '</div>'
					. '<br /><div id="blogname">&nbsp;' . $this_blogname . '</div><div id="comment"> | ' . $thiscomment . '</div></div>'
					. '<div id="excerpt">' . $thisexcerpt . '</div>'
					. $end_wrap  . "\n";

					$counter++;
			}

			// don't go over the limit
			if($counter >= $how_many) {
				break;
			}
		}
	}
	else {
		//echo "no recent posts meet the criteria...<br>";
	}
}
?>

svn causing me heartburn

Arrrghh!!

So, the Plugin Directory finally pushed the 1.1.0 version of my Mixed Tape plugin to the repository to download. After I upgraded the plugin through the admin Dashboard, I noticed that the players were not showing in the test page. Well, apparently the svn check in blew up when I tried to add the 1.1.0 version and never put the audio files up to the site. I copied the audio directory from the trunk, which is apparently a no no. So, from now on I will have to remember to copy it from my working directory, which happens to be my local directory of my site.

I know some people have demo sites of their blogs. I really never thought I would have to worry about that, until now. I really don’t want to go through the hassle of setting up a duplicate blog just for testing, but it might come to that. My blog doesn’t get much traffic, but who knows, with the plugins and the Meetup Organizer Tool, it might just start.

Anyway, I’ve released 1.1.1 of the Mixed Tape and I see that the audio directory is under that version. I guess this will also be a good test to see how the tags/revisions work. Since the audio folder wasn’t in 1.1.0, then there will be a definite change between the two version. Of course, I will have to wait until it gets pushed through the repository, however long that will take.

An ah ha! moment

So, I apparently didn’t get enough sleep last night and my brain isn’t working on all cylinders. Anyway, it hasn’t stopped me from some side brain storming. Since I finally got my Mixed Tape plugin to show up on the plugin directory, I guess I was thinking about the type of people who might use it. I shared it with one of the guys over at Music Brainz, who plans to do a little write-up about the plugin. I guess that was in the back of my head, because it came to me that people who review music could use the plugin. Of course, that might have nothing to do with a music database, but it might. I bet there are plenty of people who write-up reviews about the latest album from their favorite artist, before they submit it to Music Brainz.

Since the plugin basically just takes the tracks and makes a playlist a reviewer could discuss each of the tracks on the album and link to the track as the review goes along. So, when they are done, someone could listen to the track and see if they have the same impression as the reviewer.

So, this means I probably need to make some small enhancements. I can probably add an option to the playlist to show either the mixed tape or a cd or maybe just an album. Or maybe I could show do a thumbnail of the album art, if the reviewer had that. Then I guess there would be an option as to whether or not the page contained a mixed tape or single album.

Welp, guess I better add that to the “to do” list.