Switching WordPress Databases and Git Branches Simultaneously

I’ve recently switched to a rather rad workflow involving a local copy of our website run through MAMP using a git repository.

We’ve recently started working on a bigger set of changes that require a feature branch. But, these changes also will require WordPress to be upgraded to 3.3, requiring database changes.  Making it impossible to use the same database.

I was able to come up with this nifty technique for determining which DB to use.

$stringfromfile = file('.git/HEAD', FILE_USE_INCLUDE_PATH);
$stringfromfile = $stringfromfile[0]; //get the string from the array
$explodedstring = explode("/", $stringfromfile); //separate out by the "/" in the string
$branchname = trim($explodedstring[2]); //get the one that is always the branch name
 
// ** MySQL settings - You can get this info from your web host ** //
if($branchname == "master")
{
	/** The name of the database for WordPress */
	define('DB_NAME', 'wordpress_local');
}
else
{
	define('DB_NAME', 'wordpress_new');
}

That’s all there is to it. It works flawlessly.

Happy coding.

Adding Autocomplete Search To Shopp

This week I tackled another long term todo: autocomplete. It was a fun project with the right amount of difficulties and surprises along the way.

To make this possible, I used jQuery Marco Polo, a very nice autocomplete plugin available here: https://github.com/jstayton/jquery-marcopolo

I had to modify it to make it work exactly how I wanted, but it does the job.

Basically, jQuery Marco Polo takes input into a search field and runs it against a URL in realtime using AJAX. It expects a JSON response from this URL. It has many options on how to display items and what to do when items are clicked.

The first challenge was getting the results into JSON. To do this, I created a function to intercept requests that match a certain format: /q=?somesearchterm&ajax=true

Pretty basic. Here is the function:

add_action('wp', 'ajax_autocomplete_search', 1000);
 
function ajax_autocomplete_search() {
	if(isset($_GET['q']) && isset($_GET['ajax']) && $_GET['ajax'] == "true" && !empty($_GET['q']))
	{
		$q = $_GET['q'];
		shopp('catalog','search-products',"load=true&search=$q");
 
		if(shopp('category','has-products'))
		{
			global $Shopp;
			$filter_cat = $_GET['category'];
 
			$results = array();
			$count = 0;
			$used_products = array();
 
			while(shopp('category','products'))
			{
				if($count == 3) continue;
 
				$product_id = shopp('product','id','return=true');
 
				shopp('catalog','product',"id=$product_id&load=true");
				require(TEMPLATEPATH.'/name-vars.php');
 
				if(!in_array($first_name, $used_products))
				{
					$used_products[] = $first_name;
 
					ob_start();
					shopp('product', 'coverimage', 'width=60&height=60');
					$image_tag = ob_get_clean();
 
					$summary = shopp('product','summary','return=true');
					$slug = shopp('product','slug','return=true');
 
					if(strlen($summary) > 90)
					{
						$summary = substr($summary,0,strpos($summary,' ',90))."…";
					}
					else
					{
						$summary = $summary;
					}
 
					if(shopp('product','in-category',"name=$filter_cat") || (!isset($filter_cat) || $filter_cat == "Everything"))
					{
						$results[] = array('name' => $first_name, 'summary' => $summary, 'image' => $image_tag, 'slug' => $slug);
					}
					$count++;
				}
			}
 
			if(count($Shopp->Category->products) > 3 && count($results) == 3)
			{
				$results[] = array('name' => 'footer', 'summary' => 'See All Search Results', 'search' => $_GET['s'], 'category' => $_GET['category']);
			}
 
			if(count($results) > 0)
			{
				echo json_encode($results);
			}
		}
		die;
	}
}

Now, you’re going to notice right away that there are a lot of specific things going on here that cater to the specific way I implemented this. For instance, I only show the first 3 results with a “See All Results” footer. This also assumes that you, like me, have implemented a category filtered search as described here. There is also some custom stuff in there based on the way we name things. (that’s the name-vars.php and $first_name stuff…I won’t go into that)

No matter. You should be able to see what I’m doing and modify it as necessary.

So that, with some modification, should throw some nice JSON responses out.

Other than that, I just used the following JavaScript:

jQuery("#category-dropdown").change(function() {
				jQuery('input.search').marcoPolo({
				  url: '/?wp-minify-off=1&category=' + jQuery("#category-dropdown").val() + '&x=0&y=0&ajax=true',
				  param: 'q',
				  formatItem: function (data, $item) {
				  	if(data.name != "footer")
				  	{
				   		return '<h2>' + data.name + '</h2>' + data.image + '<p>' + data.summary + '</p>';
				   	}
				   	else
				   	{
				   		$item.addClass('footer');
				   		return data.summary;
				   	}
				  },
				  onSelect: function (data, $item) {
				  	if(data.name != "footer")
				  	{
				    	window.location = '/shop/' + data.slug;
				    }
				    else
				    {
				    	window.location = '/?s=' + data.search + '&category=' + data.category + '&catalog=true';
				    }
				  },
				  formatNoResults: function (q, $item) {
				  	$item.addClass("mp_selectable");
				  	$item.click(function() {
				  		window.location = '/?s=' + q + '&category=Everything&catalog=true';
				  	});
				  	return '<em>No results for <strong>' + q + '</strong> in ' + jQuery("#category-dropdown").val() + '.<br /><br /> Try your search again in Everything?</em>';
				  }
				});
			});
 
			jQuery("#category-dropdown").change();

Again, not the briefest example. It’s either show all or show nothing. You’re going to have to work around my particular usage of all of these things. I instantiate Marco Polo when the dropdown is changed because the search URL depends on the drop down selection.

I modified Marco Polo to get rid of it’s hijacking of the ENTER key, etc. I just removed all of the key binding because it didn’t support the way I wanted to use it.

You can see this fully working here: Moore and Giles, Inc.

Type in Brompton, for example.

That’s it. Feel free to ask questions.
Clif

Search by Category in Shopp

Today I saw a thread on the Shopp forums asking how to limit a search to a particular category. As this was on my big list, I decided to tackle it.

It turns out to not be very difficult, once you understand the way Shopp handles searches.

The trick is that the category template (i.e., theme/shopp/category.php) is used to display searches. This allows you to easily jump in and decide how it is displayed, based on your own criteria.

So, you will want to create a copy of your category.php file named something like category-default.php. Then create a separate file named category-search.php. The former file will handle the default category page functionality. The second will handle searches only.

In category.php, do something like this:

if(isset($_GET['s']) && isset($_GET['catalog']))
{
	include_once('category-search.php');
}
else
{
	include_once('category-default.php');
}

Basically, if the URL parameters for a search exist, use the search template, otherwise, stick to the normal game plan.

Then in category-search.php, you’re going to want to use your own loop to exclude every category but the one you’re interested in:

<?php
if(isset($_GET['category']))
{
	$category = $_GET['category'];
}
?>
<?php if (shopp('category','has-products')): ?>
	<?php while(shopp('category','products')): ?>
	 	<?php if(shopp('product','in-category',"name=$category") || (!isset($category) || $category == "Everything")): ?>
		// your code to display in your own html using the product tags (model it after the default category template, for example)
 
		<?php endif; ?>
	<?php endwhile; ?>

Lastly, you’ll want to render the search form yourself instead of using the default widget. For example:

			<form id="search" action="<?php bloginfo('url'); ?>" method="get" accept-charset="utf-8">
				<div class="search-perimeter">
					<input autocomplete="off" class="search" type="text" name="s" value="" id="find" />
					<select size=1 name="category" id="category-dropdown" title="Search in">
						<option value="Category 1 Name">Category 1 Name</option>
						<option value="Category 2 Name">Category 2 Name</option>
						<option value="Everything">Everything</option>
					</select>
					<input type="image" class="search-button" src="<?php bloginfo( 'template_url' ); ?>/images/search.png" />
				</div>
				<input type="hidden" name="catalog" value="true" id="catalog" />
			</form>

That’s about all there is to it. You’ll have to get in deep with the templates, but I think you’ll agree this is conceptually pretty simple.

Lastly, you should note: Category names seem to be case sensitive. “Bags” != “bags”

Good luck,
Clif

Improve HTML E-mails from WordPress, automagically

HTML e-mails are like wild stallions.  They are really hard to tame.

I have been frustrated by the vastly different ways e-mails from my company’s site (http://www.mooreandgiles.com) render in different clients.  After some research, I decided the trick to fix this would be inlining all CSS styles, as various clients will haphazardly abort style blocks you include in your e-mails.

Being lazy and wanting to the greatest flexibility for modifying templates in the future, I came up with a way to automatically process e-mails and inline their styles as they leave.   As this hooks into wp_mail, it should work with most plugins that send HTML e-mails.  Some may go around WordPress and use PHP mail…I can’t help you there.

In your theme functions file, add this:

add_filter('wp_mail', 'html_email_prep');
 
function html_email_prep($email) {
	if(strpos($email['headers'], "html") != false )
	{
		$message = $email['message'];
 
		//Grab CSS block
		require_once TEMPLATEPATH.'/css_to_inline_styles.php';
 
		$cssConverter = new CSSToInlineStyles();
		$cssConverter->setCleanup(true);
		$cssConverter->setHTML($message);
		$cssConverter->setUseInlineStylesBlock(true);
 
		$new_html = $cssConverter->convert();
 
		$email['message'] = $new_html;
	}
 
	return $email;
}

And, simply dropp css_to_inline_styles.php in your theme folder. You may download it here: http://blog.verkoyen.eu/blog/p/detail/convert-css-to-inline-styles-with-php

With this in place, it will automatically parse outgoing e-mail messages. It will find the style block and inline each style, automagically. This instantly fixed most of the rendering problems I was having in various clients.

It automatically excludes e-mails that don’t have HTML. I have seen the class bork up one HTML template, but making a minor change to the markup avoided the problem easily. Make sure you test.

Twitch for Blinksale

Twitch for Blinksale - An iPhone app for BlinksaleAfter many late nights, long weekends, and a couple of months I am finally finished with version 1.0 of Twitch for Blinksale.

Twitch for Blinksale is the first native iPhone/iOS client for the popular invoicing service Blinksale. With Twitch, you can manage all of your invoicing needs right from your iPhone.

Its clean, professional design highlights the information you need. Updating invoices on-the-go has never been easier.

Invoices Features:

  • View, add, update invoices.
  • Send invoices to clients with or without payment link.
  • Receive payment for invoices.

Client Features:

  • View and add clients.
  • Add people to clients.
  • Contact clients directly from phone (using the iPhone’s phone and mail apps)

More:

  • Convenient, “Pull To Refresh” design on invoice and client views makes reloading data a cinch.
  • Tested on multiple iPhone devices and OS versions.
  • Very stable.
  • Secure credential storage in keychain.

Screenshots:


 

Twitch for Blinksale is available for purchase in the App Store.

Available in the iPhone app store

Bulk Updating Image Sizes in @ShopPlugin

This will be two helper scripts I’ve written in a row for Shopp.

We found a situation recently where we had images with different sizes in Shopp, making uniform usage difficult at times. To compensate for this, we exported the images again. Since we use a consistent naming scheme, we didn’t have to worry about names.

We then uploaded these new images over the existing images in the /wp-content/uploads/shopp folder.

In a perfect world, this would be all there was to it, but unfortunately Shopp stores width and height information about an image in the database. This meant each table entry had to be updated. Enter this script.

It automatically scans your uploads directory and looks for images that have a different size than is stored in the database. It then updates the size.

WARNING: This script is DESTRUCTIVE. It can potentially screw up your database so PLEASE make sure you have a full backup. This is provided without any warranty whatsoever. I have only tested this on Shopp 1.1.17 and have no idea how it will work/not work on other versions.

<?php
//Adjust to point to root from where this script is located
require '../wp-load.php';
 
//Edit this path
$path = '/var/www/vhosts/domain/httpdocs/wp-content/uploads/shopp';
 
// END EDITS. You shouldn't have to edit below this line.
 
header("Content-Type: text/plain");
ini_set("auto_detect_line_endings", 1);
$dir_handle = opendir($path);
$row = 1;
$csv_data = array();
while (false !== ($file = readdir($dir_handle)))
{
	if ($file != "." && $file != "..")
	{
		$width_regex = '/((?<=width\";i:)[0-9]{1,4})|((?<=width=\")[0-9]{1,4})/';
		$height_regex = '/((?<=height\";i:)[0-9]{1,4})|((?<=height=\")[0-9]{1,4})/';
		$query = "select value from wp_shopp_meta where value LIKE '%"".$file.""%'";
		$info = getimagesize($path."/".$file);
 
		$existing_properties = $wpdb->get_var($query);
 
		if(strlen($existing_properties) > 10)
		{
			$new_width = $info[0];
			$new_height = $info[1];
 
			preg_match($width_regex, $existing_properties, $old_width);
			preg_match($height_regex, $existing_properties, $old_height);
 
 
			if(($new_width != $old_width[0] || $new_height != $old_height[0]) && !(empty($old_width[0]) || empty($old_height[0])))
			{
				echo "Updating $file. Old width/height: ".$old_width[0]."/".$old_height[0]." New width/height: $new_width/$new_height";
 
				$new_properties = preg_replace($width_regex, $new_width, $existing_properties);
				$new_properties = preg_replace($height_regex, $new_height, $new_properties);
 
				if(strlen($new_properties) > 10)
				{
					$query_update = "update wp_shopp_meta set value = '".mysql_real_escape_string($new_properties)."' where value LIKE '%"".$file.""%'";
 
					//echo "Query update: ".$query_update."n";
 
					$update = $wpdb->query($query_update);
 
					if($update == 0)
					{
						echo " --- FAILED. No rows changed. Didn't update.n";
					}
					else if($update > 1)
					{
						echo " --- POSSIBLE FAILURE. Updated too many rows.($update)n";
					}
					else
					{
						echo " --- SUCCESSn";
					}
				}
				else
				{
					echo "--- FAILED. New properties string suspiciously short.";
				}
			}
		}
	}
}
?>

Hope it helps. Let me know if there are questions.

Converting Shopp's Image Storage From Database to File System

I have been searching for a little while for an easy way to convert a Shopp install from using the database for image storage to using the file system.

The advantages for this are obvious, especially when you have 3000 images and your database has bloomed to several hundred MB.

Failing to find one, I decided to write my own. Shopp’s image store rules (at least in 1.0.14) seem to follow this model:

  • Check wp_shopp_asset table to see if an image has been stored in the data column.
  • If it isn’t there, assume it’s in the filesystem path.

After about an hour, I came up with this solution (WARNING: This script is DESTRUCTIVE. It will delete all of your images from the database so PLEASE make sure you have a full backup. This is provided without any warranty whatsoever. I have only tested this on Shopp 1.0.14 and have no idea how it will work/not work on other versions.):

<?php
// Copyright 2011, Clifton H. Griffin II
// This script is DESTRUCTIVE.  It will delete all images in your database!
// Run at your own risk. It is provided with NO WARRANTY WHATSOEVER.
// Please read carefully and do a full backup before using!
// Test with Shopp 1.0.14
 
//EDIT THIS LINE (this directory must be writeable)
$path = "/var/www/vhosts/domain.com/httpdocs/wp-content/uploads/shopp";
 
//Load WP data objects
require 'wp-load.php';
 
//Get list of rows from shopp
$sql    = "SELECT id FROM wp_shopp_asset";
$results = $wpdb->get_results($sql, "ARRAY_A");
 
//Loop through them, grabbing images
foreach ($results as $row)
{
	$id = $row['id'];
	$therow = $wpdb->get_row("SELECT name, data FROM wp_shopp_asset WHERE id = $id", "ARRAY_A");
	$image = $therow["data"];
	$name = $therow["name"];
 
	echo "File name: ".$path."$name <br />";
	$file = fopen($path."$name","w");
	fwrite($file, $image);
	fclose($file);
 
	//Clear image from DB
	$wpdb->query("UPDATE wp_shopp_asset SET data = null WHERE id = $id");
}
//Optimize the table to clear unused space
$wpdb->query("OPTIMIZE TABLE wp_shopp_asset");
?>

It should be fairly self-explanatory. Use at your own risk. I create a public github repository to host the file, on the off chance others want to add to it: https://github.com/clifgriffin/Shopp-Image-Export

Let me know if it helps you out.

Disable RSS Plugin for WordPress

Smashing Magazing did a post today including several RSS related WordPress hacks.   One of them featured a method to disable all RSS feeds. As they say:

“Let’s say you’re using WordPress as a CMS to manage your online portfolio or your company’s website. In such cases, the RSS feed isn’t that useful, and some people would probably want to remove it.”

Notably, they did not encapsulate the functionality in a plugin. So I will be converting the ones without a associated plugin into plugins over the coming days. This is the first one.

Simply put, all you have to do is upload the plugin, activate it, and viola! No more RSS feeds. You can edit the error message users receive by editing the actual plugin file. (disable-rss.php)

The code:

add_action('do_feed', 'fb_disable_feed', 1);
add_action('do_feed_rdf', 'fb_disable_feed', 1);
add_action('do_feed_rss', 'fb_disable_feed', 1);
add_action('do_feed_rss2', 'fb_disable_feed', 1);
add_action('do_feed_atom', 'fb_disable_feed', 1);
 
function fb_disable_feed() {
	wp_die( __('No feed available,please visit our <a href="'. get_bloginfo('url') .'">homepage</a>!') );
}

Download Disable RSS 1.0 for WordPress 2.7-RC1 Now…
Disable RSS 1.0 for WordPress 2.7-RC1

Converting HTML to a C# String

Today I was working on a project where it became useful to convert an HTML file into two C# strings.  As I only intended to do this one time, I opted to not do all of the string replacements necessary and decided to look for someone else who had already done the heavy lifting. 

In short time I found a singularly useful tool to do exactly what I needed.   The tool is named HTML To C# String and is published as an open source project under the GPL (or MIT) license. (it isn’t extremely clear which)

It's simple.

It's simple.

The only limitation I found was that this project does not preserve whitespace or line breaks. That’s not a problem if you’re dealing with simple html, but if you have anything fancy–like javascript, you might run into troubles. For example this:

var something = "This is such a cool var!"; //A comment about this var
var somethingElse = "blah";

becomes this:

var something = "This is such a cool var!"; //A comment about this var var somethingElse = "blah";

This will obviously not work.  I converted his project (which is only a few lines of code) into a Visual Studio 2008 project and tweaked it slightly.  You can download the result below.  I did not compile it with a setup as this utility is too simple to warrant an installation in my humble opinion.  Just run the executable in bin/Release. 

Download it now…
HTML To C# String-Mod