Vacation Learning – PHP and Smarty Templates

I’m on vacation this week and next week. Since I rarely have time to learn anything technical (or blog for that matter anymore), I thought I would take some time during my time off to learn something new around development.

We have a system at work that is essentially a small portal. The core of it was written by me to learn PHP about 8 years ago and has been augmented by me and one other guy at workKeith and I over the years. Over that time, as we added new functionality to it, I used it to experiment with other languages as I was learning it. Other pieces were written in Java out of convenience. In total, we have pieces written in PHP, Java, Python, and PERL.

As I usually use this system to learn new things, I figured it would be a good candidate to use to learn how to use the Smarty templating system for PHP. I became interested in this templating system after working with Eventum over the last few weeks and figured that if I am going to do further work with Eventum, it would be helpful to understand the templating framework it uses.

So I’ve started using the system to take our 8 year old PHP code base and separate some of the presentation logic out. Smarty is pretty flexible and easy to use at a high level (I haven’t gotten into any of the really advanced stuff yet).

Here’s an example of how nicely the use of a templating system simplifies your code. Take this example, which enumerated entries from our internal wiki via an RSS feed into a section on the home page:

 function getWikiEntries($url) {
$theHTML = "";

$rss = fetch_rss($url);

$theHTML .= "

"; $theHTML .= "";# foreach over each item in the array.
 # displaying simple links$rowCount = 0;
 $className = "modifications-evenrow";foreach ($rss->items as $item ) {if (($rowCount % 2) == 0) {
 $theHTML .= " "; } $theHTML .= "# truncate item title to 28 characters
 $myTitle = $item['title'];if (strlen($myTitle) > 28 ) {
 $myTitle = substr($myTitle, 0, 28) . " ...";
 }$theHTML .= $myTitle;
 if (($rowCount % 2) == 0) {
 $theHTML .= "  ";
 } else {
 $theHTML .= "";
 }
 $rowCount++;if ($rowCount == 20):
 break;
 endif;
 }$theHTML .= "

<table cellspacing="0" cellpadding="0" align="center">
 <tbody>
 <tr>
 <th class="header-title" colspan="2">"; # get the channel title and link properties off of the rss object # $title = "Recent Wiki Entries"; $link = $rss->channel['link']; #$theHTML .= "$title"; $theHTML .= "$title   <a href="$url"><img alt="" border="0" />"; $theHTML .= "</th>
 </tr>
 </tbody>
 </table>

<table cellspacing="0" cellpadding="0" align="center">
 <tbody>
 <tr>
 <td class="modifications-sectionheader" colspan="2"></td>
 <td class="modifications-data">"; $theHTML .= "<a title="" href="$item[link]">";</a></td>
 </tr>
 </tbody>
 </table>
 "; return($theHTML); }

I’m sure you can appreciate how hard this would be to maintain, and all of the cruft that has accumulated over the years …

Now take the simplified version (sans error checking), written today in about 10 minutes:

function getWikiEntries($url) {
$rss = fetch_rss($url);

$template = new TemplateEngine();

$firstColumn = array_slice($rss->items, 0, 10);
 $secondColumn= array_slice($rss->items, 10);

$template->assign("firstColumn", $firstColumn);
 $template->assign("secondColumn", $secondColumn);
 $template->assign("link", $rss->channel['link']);

return($template->renderString("wikiEntries.tpl"));
 }

… along with its corresponding Smarty template:

{section name="entries" loop="$firstColumn"}{/section}

<a title="{$secondColumn[entries].title}" href="{$secondColumn[entries].link}">{$secondColumn[entries].title}</a>
 <table width="80%" cellspacing="0" cellpadding="0" align="center">
 <tbody>
 <tr>
 <th class="header-title" colspan="2">Recent Wiki Entries <a href="{$link}"><img src="{$applicationURL}/images/rss.png" alt="" border="0" />

</tr>
 <tr>
 <td class="modifications-sectionheader" colspan="2"></td>
 <td class="modifications-data"><a title="{$firstColumn[entries].title}" href="{$firstColumn[entries].link}">{$firstColumn[entries].title|truncate:28:" ..."}</a></td>
 </tr>
 </tbody>
 </table>

I don’t know about you, but I think thats quite a difference in maintainability. I’d much rather modify the html in the template than in the original function. Not only that, but the code is actually code, not a bunch of code with a lot of simply horrid markup stuck in the middle of everything.

I’m pretty impressed with how much I’ve been able to use in a short amount of time this week. The libraries are obviously thought out and ramp up time for me was really minimal. I like libraries like that. It also addresses something that has annoyed me for a long time. Embedded HTML is a pain to maintain and I’ve dreaded going into this over the years just because of that.

At some point, I’ll investigate what it takes to write custom plugins, a functionality that the libraries also support.

I think I’ve been able to get a really good start at getting something maintainable. My goal over the next few of weeks is to templatize the whole system, then start taking the non-PHP pieces of the system and rewrite them in PHP. I’ll also add the ability to change configuration in one place, so that we can cut some of the pain that we have in keeping things maintained down – and perhaps be able to install the application in other places.

Should be fun. I’m definitely feeling productive over the past few days. I’ve always liked working in PHP over other languages. I definitely have to do work like this more often.

LDAP Enabling The Eventum Defect Tracking System

Due to a recent reorg, I have the opportunity to replace our defect tracking system, which has quite a bit of really wasteful process baked into the tool, with a new one. I’ve been looking at defect tracking software for a while, and chose Eventum, an open source project by MySQL AB for a number of reasons, some of them including:

  • Its open source
  • Its written in PHP, so I don’t have to worry about messing with fastcgi, mod_perl, or mod_python
  • It is extensible (you can add custom fields, etc)
  • It uses MySQL, rather than SQLLite or something like that, so we can integrate it into the rest of our home-grown build software
  • It supports email integration. While we won’t be using this right away, we’ll be implementing it in a later iteration
  • Its simple to use, with a very simple interface, once you get use to it. Everything is essentially on one screen.
  • It has time tracking, along with some basic reporting built in

One thing it doesn’t have built in is LDAP authentication. I wrote a previous article about all of the work we’ve done to integrate both our home grown applications and a few open source applications in with our LDAP store, to minimize the management of multiple passwords across systems, so this was very important to me. I started with many, many Google searches to see if someone else has done this, only to hit one dead end after another. At first I was being lazy and decided to just forget about it. One system not tied to the LDAP tree isn’t that big of a deal, but then my perfectionism set in. Why would I settle for that when LDAP authentication should be really easy to integrate into an Open Source package?

So I decided to spend a few hours to get it working. Since I had no success finding an implementation, I figured I could do my part and post what I have. There are a couple of caveats that I want to throw out before we actually get to the code though:

  1. It isn’t done “right”. This is all extra work for me, so I got enough done so that it would work. The right way to do this would to refactor the auth stuff out into a workflow like hierarchy that could be pluggable (see this post in the eventum mailing list). I’ll get to that someday, but right now this solution hacks the auth module to get authentication working.
  2. LDAP Settings are not configurable through the interface. I don’t have time for that, so a set of defines at the top of the LDAPAuthenticator class contains all of the configuration information for the LDAP server. Bummer, but like I said, I’m on a schedule.
  3. Users still have to be added to the Eventum database – they are not added automagically when they log in. I want control of who is in the system, so I’ve elected to leave this functionality out and just do authentication.

With these three caveats in place though, given my experience looking around for this stuff, at least this code works and will be able to be used by others. Its a starting point – which is more than is out there today. Anyone is free to use this and take the time to do it right. With that said, I’d love to receive updates if someone actually takes this up. For now though, this works for me.

So, now to the code. I wrote a small PHP class called “class.LDAPAuthenticator.php. There are two functions in it. Because Eventum uses email address as the login, we need a way to get the full user DN from the email address. This is what the email_to_dn function does. Given an email address, it returns the full distinguished name of the user. This is called by the main class function, ldap_authenticate. The ldap_authenticate function takes the same arguments as the class.auth.php function isCorrectPassword, which consist of the email address and the password. It binds to the LDAP authentication tree using the full DN of the user and the password supplied. If authentication is successful, it returns TRUE, otherwise it returns FALSE, just like the isCorrectPassword function used to validate the password from the Eventum database.

The code looks like this:

# Change these values to access another LDAP server.
define("LDAP_PORT", 636);
define("LDAP_HOST", 'ldaps://ldapserver.example.com:' . LDAP_PORT);
define("LDAP_BIND_DN", 'PUT THE BIND DN HERE');
define("LDAP_BIND_PASSWORD", 'PUT THE BIND PASSWORD HERE');
define("LDAP_SEARCH_DN", "PATH OF THE TREE TO SEARCH FOR USERS");

class LDAPAuthenticator {

# Look up a users full distinguised name from
# their email address, since Eventum uses
# email address as the login name.
function email_to_dn($emailAddress) {
$returnDN = "";

$server = ldap_connect(LDAP_HOST);

if ($server == FALSE) {
return($returnDN);
}

ldap_set_option($server, LDAP_OPT_PROTOCOL_VERSION, 3) ;

$ldapbind = ldap_bind($server, LDAP_BIND_DN, LDAP_BIND_PASSWORD);

# verify binding
if ($ldapbind) {
# find the user based on the entered email address.
$result = ldap_search($server,
LDAP_SEARCH_DN,
"(&(mail=$emailAddress))",
array("dn"));

$info = ldap_get_entries($server, $result);

# if we actually got a value back, return the users DN
if ($info["count"] > 0) {
$returnDN = $info[0]["dn"];
}

ldap_unbind($server);
}

return($returnDN);
}

# Authenticate with the LDAP server. Function returns true
# if authentication was successful, false otherwise.
function ldap_authenticate($email, $password) {
$returnValue = FALSE;
$userDN = LDAPAuthenticator::email_to_dn($email);

if ($userDN == "") {
return($returnValue);
}

$server = ldap_connect(LDAP_HOST);

if ($server == FALSE) {
return($returnValue);
}

ldap_set_option($server, LDAP_OPT_PROTOCOL_VERSION, 3) ;

$ldapbind = ldap_bind($server,
LDAPAuthenticator::email_to_dn($email),
$password);

if ($ldapbind) {
$returnValue = TRUE;
ldap_unbind($server);
}

return($returnValue);
}
}

Save this file as class.LDAPAuthenticator.php and put it in your Eventum includes directory. Modify the define statements at the top to contain your LDAP server information.

Now, to use it. Go to your Eventum includes directory and add the following line to the top of the class.auth.php file:

require_once(APP_INC_PATH . "class.LDAPAuthenticator.php");

I have this at the end of all of the rest of the require statements.

Now, replace the isCorrectPassword function in class.auth.php with the following function:

 /**
* Checks whether the provided password match against the email
* address provided.
*
* @access public
* @param string $email The email address to check for
* @param string $password The password of the user to check for
* @return boolean
*/
function isCorrectPassword($email, $password) {
return(LDAPAuthenticator::ldap_authenticate($email, $password));
}

… and VOILA. You can now authenticate off of your LDAP tree.

Now, I know it isn’t pretty, hacking the code directly – but it works, and its more of a starting point than I can find anywhere else. I hope its useful to others. Again, if anyone takes this further and does it “right”, I would be really happy to get a copy of the modifications.

One more thing – don’t forget to require SSL on the URL to your Eventum installation by using the SSLRequireSSL directive in your Apache server. You don’t want these passwords floating around in the clear across the network.

Download the Eventum LDAP hack here and happy authenticating.

Wordpress 2.1.3 released.

Version 2.1.3 of the WordPress blogging platform has been released and is available for download. According to the WordPress blog, this is a security release that “includes fixes for several publicly known minor XSS issues, one major XML-RPC issue, and a proactive full sweep of the WordPress codebase to protect against future problems”.

I’ve upgraded, and so should you. Take a couple of minutes to do this upgrade, as the possible consequences aren’t worth the humiliation. 😉

Mac Software I’m Finding Useful

I thought I’d take some time to sit down and document the tools I’ve been using lately as I continue my acclamation into the MacIntosh world. These are tools that I’ve found really useful over the last six months or so.

  • The Camino Browser – hands down the best browser I’ve found for the Mac so far. It’s my default browser.
  • Ecto – Mac Native application for writing blog entries and posting them to your blog. Supports Blogger, Blojsom, Drupal, MovableType, Nucleus, TypePad, and WordPress among others. Doug referred to MarsEdit as another alternative, but Ecto fits the bill for me perfectly. It includes spell checker, Amazon Web Services integration, templates, preview – really everything you would want in an offline authoring tool.
  • Vienna Newsreader – Vienna is an open source RSS reader for the Macintosh. It is quite comparable to FeedDemon, which I used on Windows, but I like it a lot better. This tool has become one of the things I use daily in order to keep up with things
  • Snap N Drag – Screen capture utility I mentioned in previous posts. I use this all the time as well. Excellent tool.
  • BBEdit 8.5 – BBEdit is an HTML editor for the MacIntosh platform. Its the only thing I’ve found comparable to HomeSite for the Macintosh. I’m using a trial version of this application right now, but there is a good chance that when the 30 day trial ends, I’ll be buying a copy. It makes HTML authoring a hell of a lot easier than Emacs.
  • UberCaster – This is podcasting software. I have a license for it, but I haven’t had the time to muck about with it. By far the easiest podcasting software I’ve seen so far for the Macintosh. The software is currently in beta.

Some additional software I’m looking at that looks useful, but I don’t have need for it yet:

  • Xyle Scope – CSS exploration tool. I’ve messed around with this a bit and it looks really interesting. I haven’t found another tool like it so far. Allows you to explore CSS and how the styles are resolved on your page.

I’m still looking for good image editing software that doesn’t cost a bajillion dollars (like Photoshop) and doesn’t require X-Windows to be installed. If anyone has any suggestions, I’d be happy to hear them.

Wordpress 2.0.5 Released

The folks on the WordPress team have released version 2.0.5 of the WordPress Blogging Application. This release includes around 50 bug fixes one of which was a missing index on the posts table. I just upgraded and the site performs much better now. I had always thought that the site ran a tad bit slower after the 2.0 upgrade, but for some reason I just figured 2.0 was doing so much more than the 1.x versions. Didn’t even think of looking at table indexes.

Mark Jaquith has also put together his list of change files and corresponding archives containing only the changes from 2.0.4 to 2.0.5, along with a patch file to upgrade your stuff directly. I opted to download from the WordPress site.

In any event, according to the release announcement, there are some security fixes in this release as well. As I do with every release of WordPress that contains security release, I am reminding you not to be lazy and get your site upgraded as soon as you can. The performance improvements alone are worth it.

Now … off to see if they fixed that “posting from Flickr mangling CSS thing” …

Value Stream Mapping Software

I’m looking for software that does Value Stream Mapping. As with mind mapping, this process is an extreme pain without a tool to do it. If you know of any good software (or component add ons for existing software like Visio), please leave a comment on the blog. Of course, the first preference is free, but I’d love to look at commercial software as well.