Oct

19

Drupal like templating for your wordpress plugin

By mike@mike-miles.com

As part of my job, from time to time I am responsible for building custom WordPress plugins for client sites. One of the biggest pains of building a plugin (besides the miss-mash of resources/tutorials on how to build one) is theming the output. Weather it be output to the front end user, or an admin screen or a form, I have never been able to find a great way to separate back end code and front end code (not having HTML within the plugin itself). That is until the idea came to me to mimic the method Drupal uses to template output (I do a lot more Drupal development, than I do wordpress). On the latest wordpress plugin I had to develop, I decided to implement my idea and it worked quite well. so I thought I’d share the methods used so that any other wordpress plugin developer out there might use it.

A quick basic overview of how Drupal templates content for any of you who maybe unfamiliar. When a Drupal module (module is to Drupal, what plugin is to WordPress) want to output content it calls a theming function, an example:

<?php
//... other module code
return theme('my-template-file',$arg1,$arg2);
?>

So basically, you call a function ( called ‘theme’) and pass it the name of the template file you want to use along with the arguments to pass to that template. (This is a very basic overview of how the Drupal theme function works).

What Drupal then does is start an output buffer, render the content of the template (passing the arguments as variables in the local scope) captures the output, clears the output buffer and return the rendered content. here is the function that does it (from ‘theme.inc’ in the Drupal directory ‘includes’).

<?php
function theme_render_template($template_file, $variables) {
	extract($variables, EXTR_SKIP);  // Extract the variables to a local namespace
	ob_start();                      // Start output buffering
	include "./$template_file";      // Include the template file
	$contents = ob_get_contents();   // Get the contents of the buffer
	ob_end_clean();                  // End buffering and discard
	return $contents;                // Return the contents
}
?>

So we want to replicate this function in our WordPress plugin, all be it with a few additions. First, we’ll need to be able to get the location of our plugin, and thus the full location of the template file (Drupal does something similar with themes and modules in other functions to get the full $template_file path). Second, we’ll want to extend the functionality of this to allow multiple templates to be used. This is again, modeled after Drupal. What the Drupal theme engine allows you to do is have different versions of a template file. For instance, there will be a basic one with the module, but any theme can also have the same template file which will be used instead (This allows Drupal sites to customize output without needing to alter the modules core code).

So our WordPress plugin function will look something like this:

<?php
//... other module code
function myplugin_theme($template,$variables){
        //empty content to start
	$contents = '';
	//no template path to start
	$template_file = false;
	//get the path or the current active theme
	$theme_path = get_stylesheet_directory();
	//get the path to our plugin template directory
	$plugin_path = dirname(__FILE__).'/templates/';
	//assume the template name we are given does not have the extension
	$template.='.tpl.php';
	//see which template file we want to use
	//doe the theme have the template file?
	if (file_exists($theme_path.'/'.$template)){
		//it does, so use this as the path
		$template_file = $theme_path.'/'.$template;
		//does the plugin have the template file?
	}else if (file_exists($plugin_path.$template)){
		//it does, use that instead
		$template_file = $plugin_path.$template;
	} // else, there is no template file found.
	//do we have a template file to output?
	if ($template_file){
		extract($variables, EXTR_SKIP);  // Extract the variables to a local namespace
		ob_start();                      // Start output buffering
		include "./$template_file";      // Include the template file
		$contents = ob_get_contents();   // Get the contents of the buffer
		ob_end_clean();                  // End buffering and discard
	}
	return $contents;                // Return the contents
}
?>

A quick break down of our function and how it work. The function retrieves the current active theme path, as well as the path to our plugin. It first checks to see if the theme has the template file (indicates the theme creator has specialized the output of this template), else it sees if the plugin has a base template file (as it should). If there is a template file, then the function uses the PHP extract function to move the variables passed into the local namespace. It then does the same thing as the Drupal function, renders the content and returns it. Not that, if the template file is not found our function returns an empty string.

So how might one use this? Well here is an example, say our module displays a list of restaurants near a users location. We don’t know how many results are being returned, but we want to template how the list looks. so we might do something like this:

<?php
function mymodule_display_restaurants(){
	//User submitted their zipcode via a form
	$zipcode = $_POST['zipcode'];
	//we retrieve a list of restaurants near the users zipcode
	$restaurants = mymodule_get_restaurants($zipcode);
	//display restaurants to user
	echo mymodule_theme('restaurant-list',array('restaurants'=>$restaurants));
}
?>

What is happening here is the function ‘mymodule_display_restaurants’ is sending an array of restaurants to the template file ‘restaurant-list.tpl.php’. Here is an example of what that template file might look like:

<div id="restaurant-list">
<?php if (sizeof($restaurants)>0){ ?>
	<p>Here are the restaurants in your area</p>
	<ul>
	<?php foreach ($restaurants as $restaurant){ ?>
		<li><?php echo $restaurant->title; ?> - <?php echo $restaurant->address; ?></li>
	<?php } ?>
	</ul>
<?php }else{ ?>
	<p>It looks like there are no restaurants in your area.</p>
<?php } ?>
</div>

There is still PHP in our template file, but what is most important is that the HTML to display this list is separated from our plugin code. This means that a theme developer could create a custom template for our plugin to display this data differently (say in a table).

This system also allows you to build complex themed output, theming individual parts with separate template files and theming all the output in a template. Example, we might change our previous function to something like this to make it more flexible:

<?php
function mymodule_display_resturants(){
	//User submitted their zipcode via a form
	$zipcode = $_POST['zipcode'];
	//we retrieve a list of resturnats near the usrs zipcode
	$resturants = mymodule_get_resturants($zipcode);
	if (sizeof($resturants)>0){
		foreach ($resturants as &$resturant){
			$resturant = mymodule_theme('resturant',array('resturant'=>$resturant));
		}
	}
	//display resturants to user
	echo mymodule_theme('resturant-list',array('resturants'=>$resturants));
}
?>

The ‘restaurant.tpl.php’, might look something like this:

<div>
        <strong>Name:</strong><?php echo $resturant->title; ?><br />
	<strong>Address:</strong><?php echo $resturant->address; ?><br />
</div>

And our ‘restaurant-list.tpl.php’ will now look something like this:

<div id="restaurant-list">
<?php if (sizeof($restaurants)>0){ ?>
	<p>Here are the restaurants in your area</p>
	<?php
		foreach ($restaurants as $restaurant){
			echo $restaurant;
		}
	?>
<?php }else{ ?>
        <p>It looks like there are no restaurants in your area.</p>
<?php } ?>
</div>

So you can see, this gives themers much more control over the output of your plugins data without needing to alter your plugin.

 

I hope this post proves useful and your able to build upon and use this theming convention to make your plugin development much more flexible.

 

 

 

Feb

23

Itunes like progress/capcity bar

By mike@mike-miles.com

Back in December I was working on a project that needed to show progress bars.  They progress bars had to show the progress of multiple items, and it was explained to me that they should resemble the percentage bars in iTunes that show how full your ipod / iphone is, like so;

Not a problem I thought to myself, all I needed was a few images and the ever useful PHP GD library .  If you’ve never used the PHP GD library before, a brief overview:  It’s a graphics library that allows you to create dynamic graphics.  (Told you it was brief).

Of course the great thing about PHP is that it is open source, so anything you need todo has already practaclly been done. After googling I found this percentage bar code created by netlobo.com, as well as this progress/percentage bar WebAppers (theirs was in javascript but it gave me a starting point). Both of these were close to what I wanted to do, I just needed to tweak and combine them.

I should note, that since I did use code from both those websites (which are covered under the Creative Commons license and GNU General Public License) this code covers the same licensing.

The Process

My thought process for creating this was pretty straight forward from the get-go.

  1. Get percents of the items that will be on the bar
  2. Create a “bucket” image
  3. for each percent, fill part of the bucket with a color
  4. Output the whole bar.

And that’s pretty much the route I followed.  It was pretty simple actually.

Sorry to say, this code has been removed for legal reasons

but, please check out the sources I used when developing my code (they are mentioned above)

Jun

13

fseek()

By mike@mike-miles.com

There is this nifty function in PHP called fseek().  It allows you to set a pointer to any where in a file (a text file for example).  It comes in handy when your dealing with very large files (like log files, or config files), and today I discoverd it can be very useful if you want to search throu a file backwards.

Which was exactly what I was trying to do.  I needed to traverse a file, starting at the end.  The only solutions I could find infolved reading the whole file into an array, reversing the array and thensearching that way, which completly nullified the point of going the end of the file (I had to read the whole file into memory to get the end).

The solution I came up with, involves using fseek() and fread() (which allows you to read a file byte by byte).  With this function you give it a file, and a string to search for, and it’ll go through the file backwards byte by byte, and return the pointer (byte location in the file) of where the search string occures. (if it doesnt find the string, it’ll return FALSE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?PHP
function searchFromEnd($filename,$search){
	//first make sure that the file is there, and we can read it
	if(file_exists($filename) && is_readable($filename)){ 
		//open the file for reading
		$fp = fopen($filename,'r');
		//get the length of the search string(minus the first character)
		$size=strlen($search)-1; 
		//get first element in string
		$first=$search[0]; 
		//size of the file
		$file_size=filesize($filename); 
		//set seek pointer to end of the file - size of the string
		$seek=$file_size-$size; 
		//set found to FALSE
		$found=false;
		//while not past the begining of the file
		while($seek>=0){
			//set file pointer to seek value
			fseek($fp,$seek); 
			//read in the first byte from pointer
            $test=fread($fp,1); 
            //if byte is same as the first string character, and  back far enough in file for search length
			if($test==$first && (($seek+$size) <= $file_size) ){ 
				//append enough characters to $test to make it same size as $search
				$test.=fread($fp,$size); 
				//if test value is what we're looking for
				if(strcmp($test,$search)==0){ 
					//return position;
					$found=$seek;
					//set pointer to -1 (exit loop);
					$seek=-1; 
				}else{
					//move back a byte
					$seek--; 
				}
			}else{
				//move back a byte
				$seek--; 
			}
		}
		//close the file
		fclose($fp); 
	}
	//return FALSE or position of search string
	return $found; 
}
?>

A much better method then reading the whole file.

You can download this file from here