WordCamp Philly 2010 Presentation Slides

Here are slides from my talk. I was told a video of the presentation will be up at WordPress.tv sometime soon. I’ll post a link when it is. And I’ll try to get these in SlideShare or something similar. I was having trouble with their site earlier.

Business Models for WordPress Plugin and Theme Distribution
What You Need to Know About the GPL

Download: ppt, pptx

On Ustream: WordPress Plugin to Capture Email Addresses in Exchange For a File Download

Update: No longer streaming. The plugin works great. Kim is going to make it pretty, then I’ll post to WordPress and do a right up.

I’m streaming live right now while I work on a WordPress plugin. Follow me at ustream.tv/channel/stranger-studios.

I’ll be working on a plugin for a feature that we’ve done a million different ways: asking for a user’s email address in exchange for a link to a file download. A solid plugin for this will save us a lot of time. If it turns out well, I’ll put it in the WP plugin directory and link to it from here.

Here are the requirements/specs I have for this plugin:

  • Should use a short code of the form [filedownload: /path/to/file.txt] to define where the form should go and what file.
  • The form will submit to the current page, log the email entered, add a session flag.
  • If an email is in $_POST, the post/page will show the download link instead of the email form.
  • Files will be hidden behind a script to obscure the path to the file. The script will check for the session flag before returning the file.
  • Should use a template to make it easy to change the HTML/CSS for the form.
  • Should be easy to adjust the addEmail code to work with Constant Contact/etc.

That’s it. Please join me. Post questions to the chat. Again, if this code comes out clean, I hope to share it.

Enhanced by Zemanta

WordPress get_the_content_after_more() Function

In WordPress, if you want to get just the excerpt of a post, you can use the built-in functions the_excerpt or get_the_excerpt. And if you want all of the content, you can of course use the the_content or get_the_content functions. But what if you want to get everything BUT the excerpt?

The Code

Here is a function that will do that: get_the_content_after_more()

function get_the_content_after_more()
{
	$content = get_the_content();
	$moretag = preg_match("/\<span id="\&quot;more-[0-9]*\&quot;\">\&lt;\/span\&gt;/", $content, $matches);
	if($moretag)
	{
		$morespan = $matches[0];
		$morespan_pos = strpos($content, $morespan);
		$newcontent = substr($content, $morespan_pos + strlen($morespan), strlen($content) - strlen($morespan));
		$newcontent = apply_filters('the_content', $newcontent);
		return $newcontent;
	}
	else
		return "";
}

How to Use This

Notice that this is a “get_” version of the function. So you will get to echo the results or save them to a variable for future use. Just plop an echo get_the_content_after_more() into your LOOP and you’re good to go.

How it Works

The function works by getting the post content through “get_the_content” (and so will give you the content for the current post in your loop) and looking (preg_match) for the span tag that WordPress puts at the more break. (If older versions of WordPress don’t add this tag, this function won’t work. But this works in 2.9-3.0 at least.)

If a more tag is found, I create a variable $newcontent holding everything after the more tag. Then I run it through apply_filters to all of your plugins/etc that work on the_content will work on this.

If a post doesn’t have an excerpt (no excerpt or more tag in the post), the function returns an empty string.

The function could easily be called get_the_inverse_excerpt or get_the_content_after_excerpt or get_the_content_minus_excerpt. Let me know if you have a better idea for this… or tell me what you searched for to figure this out.

Why Would I Use This?

Kim was updating our portfolio page, which is really a list of WordPress posts. She wanted to style the “excerpt” differently from the rest of the content. Notice how the top section (“WineLog is…”) is in a gradient box on the WineLog portfolio item… and then the rest is styled as written in the post content. We used code like this:

<div class="excerpt">
<?php    
    the_excerpt();
?>
</div>
<?php     	
    echo get_the_content_after_more();			
?>

When we decided to do this, I first checked if WordPress was adding some tag around the excerpt that we could piggy back on. Nope. All that is added is a span like at the point where the more break is. Then I looked for a buit-in function or some parameter of the_content that could be used to do this. No luck. (Let me know if there is a built-in way to do this.)

If I Were Updating WordPress…

… and it’s open source. So maybe I will. One could add a parameter to the_content/get_the_content functions to exclude the excerpt. The code would be largely the same, though they could look for the more tag itself since they won’t have that stripped out yet. That feels clunky to me. Let me know if you have better ideas.

Make One Category Display Full Posts in Thesis Theme

Got a request from a client to have a specific category page on their WordPress blog running the Thesis theme show full posts (vs. excerpts) for just one category. So I’m going to figure it out and write the steps here for the benefit of humanity.

First, to be clear, I’m not talking about changing ALL CATEGORY PAGES from excerpts to full posts. That can be done easily through the Thesis Display Options. You would change the “Posts” display options to “display full content” and the “Archives” display options to “same as home page”.

What I want to do is show excerpts on every category page except one. You do this by settings up a custom category theme, and Thesis has its own way to do that. A custom category theme wasn’t optimal for this install, so I need to do something different. It’s good to have different ways of doing things. Maybe my method below works for you too.

Here’s what we’re going to do.

  1. Use the “thesis_hook_before_post_box” hook to check the current category and if it is the “Quick Takes” category, change the $thesis[‘display’][‘archives’][‘style’] value to “content”.
  2. Use the “thesis_hook_after_post_box” hook to change the $thesis[‘display’][‘archives’][‘style’] value back to what it was before we tweaked it.

Sounds complicated, but it’s only a few lines to add to the /custom/custom_functions.php file in your Thesis theme folder.

function quicktakes_fullpost_setup()
{
	$target_category = "Quick Takes";
 
	//saving the global archives display setting so we can revert later
	global $thesis, $saved_thesis_display_archives_style;
	$saved_thesis_display_archives_style = $thesis['display']['archives']['style'];
 
	//checking the category, if it's the target category, then set the display to full post
	$current_category = single_cat_title("", false);
	if($current_category == $target_category)
		$thesis['display']['archives']['style'] = "content";
 
}
function quicktakes_fullpost_cleanup()
{
	//we're setting the display archives style back to what it was before we tweaked it
	global $thesis, $saved_thesis_display_archives_style;
	$thesis['display']['archives']['style'] = $saved_thesis_display_archives_style;
}
add_action('thesis_hook_before_post_box', 'quicktakes_fullpost_setup');
add_action('thesis_hook_after_post_box', 'quicktakes_fullpost_cleanup');

Be sure to change the $target_category variable to the title of the category you want to change.

If you want to do the inverse of this, which would be to show the excerpt on just one category page and update show the full post on all others, you would:

  1. Change the Thesis Display Options for posts to “display full post content”
  2. Change the Thesis Display Options for archives to “same as your homepage”
  3. Then adjust line 12 in the code above to set the style to “excerpts” instead of “content.

I hope this helps. Let me know if you have any questions about this or similar customizations for Thesis.

SS-Downloads WordPress Plugin

Testing our new SS-Downloads plugin. It basically will require an email address before serving a specified file. Right now, I’m using a zip of the pre-release plugin for testing. I will update this to point to the latest version once it’s ready.

Should see a form or download link here.

Enter your email address to download ss-downloads.zip

Future plans:

  • Option to require account creation (instead of just an email address). Done
  • Option to email file as attachment instead of showing a link. Done
  • Icons for files in template.

Jason Speaking at WordCamp Philly October 30th at Temple University

I will be speaking on Business Models for Plugin/Theme Distribution. What You Need to Know About the GPL at WordCamp Philly October 30th, 2010 at Temple University in Philadelphia.

With lots of sweat and little luck, we’ll have our membership plugin available for some sort of release by then. But really I’m excited to share my thoughts on how to distribute, market, and make money off this plugin… and more interested in others ideas about the same.

If you want to hear me speak or are otherwise interested, get your ticket now.

Duplicate comment detected; it looks as though you’ve already said that!

Not sure how long WordPress has been doing this, but there is a check for duplicate comments. If the same user/email posts the same exact comment on the same post, the user will get a message like:

Duplicate comment detected; it looks as though you’ve already said that!

This is great actually, and keeps people from submitting the same comment twice if they get impatient waiting for moderation or otherwise click that submit button twice.

As a blog owner and WordPress developer, there may be situations where you want to allow people to post the same comment. If you want to enable duplicate comments on your blog for some reason, you can use this code here. Just add it to your theme’s functions.php or put this in a .php in your plugins folder and enable it.

function enable_duplicate_comments_preprocess_comment($comment_data)
{
	//add some random content to comment to keep dupe checker from finding it
	$random = md5(time());	
	$comment_data['comment_content'] .= "disabledupes{" . $random . "}disabledupes";	
 
	return $comment_data;
}
add_filter('preprocess_comment', 'enable_duplicate_comments_preprocess_comment');
 
function enable_duplicate_comments_comment_post($comment_id)
{
	global $wpdb;
 
	//remove the random content
	$comment_content = $wpdb->get_var("SELECT comment_content FROM $wpdb->comments WHERE comment_ID = '$comment_id' LIMIT 1");	
	$comment_content = preg_replace("/disabledupes\{.*\}disabledupes/", "", $comment_content);
	$wpdb->query("UPDATE $wpdb->comments SET comment_content = '" . $wpdb->escape($comment_content) . "' WHERE comment_ID = '$comment_id' LIMIT 1");
 
	/*
		add your own dupe checker here if you want
	*/
}
add_action('comment_post', 'enable_duplicate_comments_comment_post');

For reference, here is the dupe check code in wp-includes/comment.php. A newer version should probably have a hook above and/or below to allow people to override the dupe checker more directly.

// Simple duplicate check
	// expected_slashed ($comment_post_ID, $comment_author, $comment_author_email, $comment_content)
	$dupe = "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = '$comment_post_ID' AND comment_approved != 'trash' AND ( comment_author = '$comment_author' ";
	if ( $comment_author_email )
		$dupe .= "OR comment_author_email = '$comment_author_email' ";
	$dupe .= ") AND comment_content = '$comment_content' LIMIT 1";
	if ( $wpdb->get_var($dupe) ) {
		do_action( 'comment_duplicate_trigger', $commentdata );
		if ( defined('DOING_AJAX') )
			die( __('Duplicate comment detected; it looks as though you’ve already said that!') );
 
		wp_die( __('Duplicate comment detected; it looks as though you’ve already said that!') );
	}

Hidden 404 Errors with WordPress Plugin Pages

After a couple hours, I’ve tracked down and fixed a bug I was having with some of our WordPress plugins. I believe that there are a few people out there having the same problem. I think there may be another solution online, but it is one of those issues that is difficult to pare down to a good search query.

Anyway here is a solution for “404 issues with plugin pages” or “lynx shows a 404, but the page still loads”, or “Google Webtools says there is a 404, but I can get to the page”, or “setting status to 200:OK still results in 404″, or “I get a 404 in IE, but refreshing the page brings it up”, or “I get random 404 errors in IE”, or “I’m getting an HTTP/1.1 404 Not Found error but the page still loads”.

You may skip ahead to the Final Solution code, but it is probably a good idea to read everything below to make sure that you are indeed having the same issue I had… and that this will actually fix your problem.

The Context

I have some WordPress plguins (Stranger Products, Stranger Events) that generate pages outside of the core WordPress system (i.e. they are not “wordpress pages” in the WP DB, they are web pages generated by our plugin script). To serve these pages, I add a bunch of rules to the .htaccess file to redirect stuff like /products/1/ to a product info page.

Some gallery plugins or other plugins that generate new pages may have a similar setup/issue.

The Problem

While the mod rewrite works fine, and the page loads fine, WordPress doesn’t find a WP page or post for the query string and so sends a “HTTP/1.1 404 File Not Found” status in the header. Most web browsers will ignore this and show the content that comes after the header. It seems that IE will sometimes choke on this status, and other times show the page. Funny IE!

Google’s crawler however will not crawl that page and will let you know in a web toolkit report. Also, I noticed that the lynx command line browser for Linux would show the 404 error and then load the page.

The big issue here is that Google is not going to crawl our page.

The Fix

I spent a lot of time tracking down where in the WordPress code the 404 status is set. Ideally, there would be a plugin “hook” near this that we could use to prevent the 404 status from reaching the browser.

The function that makes the 404 decision is handle_404(), which can be found in the /wp-includes/classes.php file. Here is the code (for WordPress 2.8.4, similar for previous versions I looked at too):

459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
function handle_404() {
	global $wp_query;
	if ( (0 == count($wp_query->posts)) && !is_404() && !is_search() && ( $this->did_permalink || (!empty($_SERVER['QUERY_STRING']) && (false === strpos($_SERVER['REQUEST_URI'], '?'))) ) ) {
		// Don't 404 for these queries if they matched an object.
		if ( ( is_tag() || is_category() || is_author() ) && $wp_query->get_queried_object() ) {
			if ( !is_404() )
				status_header( 200 );
			return;
		}
		$wp_query->set_404();
		status_header( 404 );
		nocache_headers();
	} elseif ( !is_404() ) {
		status_header( 200 );
	}
}

This would be the ideal place to say, “Hey, don’t 404 this page”, but there is no hook in here. I tried setting the $wp->did_permalink flag to FALSE, which worked sometimes, but sometimes WordPress would write that back to TRUE after I reset it. And I’m not even sure what that flag is doing; so playing with it might cause some bugs elsewhere.

The next place to check is the status_header() function called by handle_404. This function is found in the /wp-includes/functions.php file. Here is the code:

1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
function status_header( $header ) {
	$text = get_status_header_desc( $header );
 
	if ( empty( $text ) )
		return false;
 
	$protocol = $_SERVER["SERVER_PROTOCOL"];
	if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol )
		$protocol = 'HTTP/1.0';
	$status_header = "$protocol $header $text";
	if ( function_exists( 'apply_filters' ) )
		$status_header = apply_filters( 'status_header', $status_header, $header, $text, $protocol );
 
	return @header( $status_header, true, $header );
}

Tada! This function uses the “status_header” hook/filter before updating the header. So we can create a function in our plugin to check the status for a 404 and then return false/NULL if we know that there really is a page to load. Here’s how I did it.

The Final Solution

In PHP code for my pages, I created a global variable called $isapage and set it to true. So at the very top of any page that is giving the 404 errors, add this code:

global $isapage;
$isapage = true;

Now I add the following function and filter to my plugin code:

//this function checks if we have set the $isapage variable, and if so prevents WP from sending a 404
function ssp_status_filter($s)
{
	global $isapage;
	if($isapage && strpos($s, "404"))
		return false;	//don't send the 404
	else
		return $s;
}
add_filter('status_header', 'ssp_status_filter');

I hope this helps some people out there.

Feel free to critique this solution. Let me know if I missed something or if there are better ways to do this.

Feel free to post related issues. I may have found solutions to those along the way… or maybe a commenter can help you out.

Unit of Measure PHP Class

For a recent project, we developed a php class that made it easier to work with units of measure. I was surprised that there wasn’t anything like this available out there, so we decided to open source the code once it was in a good state. That day has come.

We will be updating this code (and this blog post) over the next few days and likely down the road as well. But in the meantime, here is a link to the GitHub repository: phpUOM on GitHub.

(If this goes well, we’ll hopefully be open sourcing a bit more of our code in the future.)