<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
<channel>
	<title>New Media Campaigns</title>
	<link>http://www.newmediacampaigns.com</link>
	<description>New Media Campaigns is a full service web design, development and marketing firm specializing in high end web sites created at an affordable price.</description>
	<language>en-us</language>
	<copyright>Copyright 2005-2009, New Media Technology Group</copyright>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/newmediacampaigns/development" type="application/rss+xml" /><feedburner:emailServiceId>newmediacampaigns/development</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
		<title>Following content management's core concepts with your CMS</title>
		<link>http://feedproxy.google.com/~r/newmediacampaigns/development/~3/3UIkS3dBnAI/content-managment-core-concepts</link>
		<description>&lt;a style="float: left; margin: 0 18px 18px 0;" href="http://api.tweetmeme.com/share?url=http://www.newmediacampaigns.com/page/content-managment-core-concepts"&gt;&lt;img src="http://api.tweetmeme.com/imagebutton.gif?url=http://www.newmediacampaigns.com/page/content-managment-core-concepts" height="61" width="51" /&gt;&lt;/a&gt;
			&lt;p&gt;Content Management is nothing new, but it does have its fair share of growing pains as it relates to the web. With the number of options readily available, it is easy for designers, developers and clients to lose sight of content management's core principals:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sorting 'Stuff' &amp;mdash; content defined by an end-user &amp;mdash; into a content repository.&lt;/li&gt;
&lt;li&gt;Support the workflow of the end-user.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The web community has the first principal down thanks to relational databases, but we really struggle with the second. Since the end-user can vary from an individual with varying skill sets to a large corporation with endless resources, demands on a Content Management Systems and its specific workflow have create countless options in the marketplace.&lt;/p&gt;
&lt;p&gt;With each options comes a list of pros and cons, fanboys and detractors which ends up creating some misconceptions. Here are some misconceptions I've read repeatedly in the last few weeks that neglect to take these core principals into account:&lt;/p&gt;
&lt;h2&gt;One type of system is better than other.&lt;/h2&gt;
&lt;p&gt;Is it better to go open source, third-party or proprietary system? Truthfully, there is no right answer because each option can meet your goal of publishing content to the web but could create a potential workflow nightmare.&lt;/p&gt;
&lt;p&gt;I use to advocate for using third-party software. I preferred working with a system that offered continuing support thanks to its customer base. If I was unable to continue to help client with maintenance there was always someone within the community. Now a client would never feel like they've wasted their investment on piece of technology.&lt;/p&gt;
&lt;p&gt;That philosophy helped me in large part but I'm not sure it was in my clients best interest. When I started using other technology that better suited their workflow needs, I found myself getting more projects in the long run. Just because a company can afford an expensive system doesn't mean Wordpress isn't the best solution for their needs.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;With the volume of clients and sites we work on, our proprietary system has proven the best option over time and I realized that within my first few days here.&lt;/p&gt;
&lt;h2&gt;CMSs should be more open to customization.&lt;/h2&gt;
&lt;p&gt;I think there is a false perceptions in the title 'Content Management System.' People seem to infer that they should have more control over the system itself and this can lead to people neglecting the content itself. Not all systems are created equal, so you can't be under the assumption any system will meet your needs.&lt;/p&gt;
&lt;p&gt;If you feel like your system can't be customized, you have either chosen a system that doesn't fit into your workflow or you didn't define your content before development began on your system (I'll discuss defining your content in a future post). By ignoring your content from the start, you won't have a clear understanding of the work needed to publish your content and you will feel limited by any system.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Buying into a system means that you are committing to a set of standards, like a fixed design, so that you can produce and manage content quickly be streamlining your workflow. If you can't conform to any set of standards, moving to a CMS isn't in your best option.&lt;/p&gt;
&lt;h2&gt;I don't need to learn HTML now that I have a CMS.&lt;/h2&gt;
&lt;p&gt;The point of moving to a CMS is to avoid coding and/or reduce the reliance of a developer to speed up your publishing process, creating a better workflow. So why would I have to learn HTML when I have a WYSWYG editor to do the work for me?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;You don't.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;While there is no need to learn the syntax of a web language like HTML still you need to have an general understanding of how it works. You need to realize that pasting source code info your CMS from an e-mail newsletter is going to break your page in all likelihood. You need to realize when something is outside of the workflow of publishing content.&lt;/p&gt;
&lt;h2&gt;CMSs are bad for SEO&lt;/h2&gt;
&lt;p&gt;This goes back to the same ideas with customization: If you don't make SEO part of your workflow and create/use a system with it in mind early, it's going to make it harder to have good SEO. If that's the case, is it the systems' fault or yours?&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The success of any CMS is tied to its ability to conform to your workflow. Most complaints with specific systems can be drawn to the fact they weren't developed with your particular workflow in mind. After all, the end goal of using a system is to publish and management content easily and quickly. Most systems can accomplish that, which is align with the first principal.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Having only worked with our in-house system for a few days, I can tell the team put careful thought into the workflow and it seems to be paying dividends. &amp;nbsp;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=3UIkS3dBnAI:IApbg9KDDL0:IuXuQPol3sE"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=3UIkS3dBnAI:IApbg9KDDL0:IuXuQPol3sE" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=3UIkS3dBnAI:IApbg9KDDL0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=3UIkS3dBnAI:IApbg9KDDL0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=3UIkS3dBnAI:IApbg9KDDL0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=3UIkS3dBnAI:IApbg9KDDL0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=3UIkS3dBnAI:IApbg9KDDL0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/newmediacampaigns/development/~4/3UIkS3dBnAI" height="1" width="1"/&gt;</description>
		<guid isPermaLink="false">http://www.newmediacampaigns.com/page/content-managment-core-concepts</guid>
		<pubDate>Tue, 10 Nov 2009 10:00:00 EST</pubDate>
	<feedburner:origLink>http://www.newmediacampaigns.com/page/content-managment-core-concepts</feedburner:origLink></item>
		<item>
		<title>Three Ways to Target Mobile Devices</title>
		<link>http://feedproxy.google.com/~r/newmediacampaigns/development/~3/sEvKrW8zMJU/three-ways-to-target-mobile-devices</link>
		<description>&lt;a style="float: left; margin: 0 18px 18px 0;" href="http://api.tweetmeme.com/share?url=http://www.newmediacampaigns.com/page/three-ways-to-target-mobile-devices"&gt;&lt;img src="http://api.tweetmeme.com/imagebutton.gif?url=http://www.newmediacampaigns.com/page/three-ways-to-target-mobile-devices" height="61" width="51" /&gt;&lt;/a&gt;
			&lt;p&gt;&lt;img style="float: right; margin: 0 0 10px 10px;" src="/files/posts/mobile_site.jpg" alt="Webkit Mobile Site" width="200" height="320" /&gt;&lt;/p&gt;
&lt;p&gt;We recently launched a beta version of a New Media Campaigns mobile Webkit site.&amp;nbsp; If you're reading this post from an iPhone, iTouch, Palm Pre or Android based phone you're seeing it right now.&amp;nbsp; Josh Lockhart did the design and frontend code for the site, a topic he will be covering in a post later this week.&lt;/p&gt;
&lt;p&gt;This post discusses how to target mobile devices and show them either different content, different stylesheets or even redirect to a mobile URL. Ideally, the check would be done for ALL mobile webkit platforms, not just the iPhone.&amp;nbsp; It would be a shame to leave those Android/Palm out after developing a pretty mobile site. So without further ado, here are the methods:&lt;/p&gt;
&lt;h2&gt;1. Checking User Agent Serverside&lt;/h2&gt;
&lt;p&gt;When a browser visits a site, it sends a string describing who it is called the user-agent string.&amp;nbsp; It varies depending on the browser and platform.&amp;nbsp; The user-agent string for key mobile webkit browsers are:&lt;/p&gt;
&lt;pre&gt;iPhone - Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3
iTouch - Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/3A100a Safari/419.3 
Android - Mozilla/5.0 (Linux; U; Android 0.5; en-us) AppleWebKit/522+ (KHTML, like Gecko) Safari/419.3
Palm Pre - Mozilla/5.0 (webOS/1.0; U; en-US) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/1.0 Safari/525.27.1 Pre/1.0
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Unfortunately there is not a good way to check for mobile webkit in general.&amp;nbsp; So each of these needs to be checked for in a case by case manner.&amp;nbsp; Here is some rough php to do that:&lt;/p&gt;
&lt;pre class="brush:php"&gt;if( strstr($_SERVER['HTTP_USER_AGENT'],'Android') ||
	strstr($_SERVER['HTTP_USER_AGENT'],'webOS') ||
	strstr($_SERVER['HTTP_USER_AGENT'],'iPhone') ||
	strstr($_SERVER['HTTP_USER_AGENT'],'iPod')
	){
	// Send Mobile Site
}
&lt;/pre&gt;
&lt;p&gt;We did this type detection since we wanted to send completely new templates for pages at the same URL.&amp;nbsp; This worked nicely with our CMS since both the normal frontend and the mobile fronend were updated from the same database.&amp;nbsp; We could also use this technique to redirect to a mobile URL if that is how we wanted to present our mobile site.&lt;/p&gt;
&lt;h2&gt;2. Checking User Agent Clientside&lt;/h2&gt;
&lt;p&gt;This method simply uses javascript to check the User Agent after the page has loaded.&amp;nbsp; The obvious downside is that it first requires mobile visitors to load your standard site which might be fairly heavy.&amp;nbsp; It is fine to use in a pinch where you are not able to modify server-side code.&lt;/p&gt;
&lt;p&gt;Here is some javascript to detect the mobile webkit browsers:&lt;/p&gt;
&lt;pre class="brush:js"&gt;if( navigator.userAgent.match(/Android/i) ||
	navigator.userAgent.match(/webOS/i) ||
	navigator.userAgent.match(/iPhone/i) ||
	navigator.userAgent.match(/iPod/i) ||
	){
 // Send Mobile Site
}
&lt;/pre&gt;
&lt;p&gt;You can use this method to either redirect to a different site, send a different stylesheet or whatever else you may need to do for a mobile site.&lt;/p&gt;
&lt;h2&gt;3. Use CSS Media Type&lt;/h2&gt;
&lt;p&gt;If your HTML doesn't need to change between your mobile site and standard site, it may make more sense to send a different stylesheet just to mobile browsers.&amp;nbsp; There are a couple of ways to do this, but using the media type capabilities of CSS may be the way to go.&lt;/p&gt;
&lt;p&gt;To load an entirely different stylesheet, use the media attribute when loading the stylesheet:&lt;/p&gt;
&lt;pre class="brush:html"&gt;&amp;lt;link href="mobile.css" rel="stylesheet" type="text/css" media="only screen and (max-device-width: 480px)" /&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Alternatively you can just add some declarations into your existing stylesheet:&lt;/p&gt;
&lt;pre class="brush:css"&gt;@media screen and (max-device-width: 480px) {
    /* mobile declarations */
}
&lt;/pre&gt;
&lt;p&gt;This method is a little dangerous since it depends on the screen resolution to send the stylesheet. A mobile site is generally not different from a typical site because of resolution alone; it is also different because of the physical screen size of the device.&amp;nbsp; For instance, the new Verizon Droid has roughly the same screen dimensions of the iPhone but a resolution of 854px by 440px!&amp;nbsp; Serving a mobile site to this device makes sense even though it has such high resolution.&lt;/p&gt;
&lt;p&gt;If you've done a mobile site and have a perferred method be sure to share the site and the method in the comments.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=sEvKrW8zMJU:W8DV-wRPxN8:IuXuQPol3sE"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=sEvKrW8zMJU:W8DV-wRPxN8:IuXuQPol3sE" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=sEvKrW8zMJU:W8DV-wRPxN8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=sEvKrW8zMJU:W8DV-wRPxN8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=sEvKrW8zMJU:W8DV-wRPxN8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=sEvKrW8zMJU:W8DV-wRPxN8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=sEvKrW8zMJU:W8DV-wRPxN8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/newmediacampaigns/development/~4/sEvKrW8zMJU" height="1" width="1"/&gt;</description>
		<guid isPermaLink="false">http://www.newmediacampaigns.com/page/three-ways-to-target-mobile-devices</guid>
		<pubDate>Mon, 09 Nov 2009 11:05:00 EST</pubDate>
	<feedburner:origLink>http://www.newmediacampaigns.com/page/three-ways-to-target-mobile-devices</feedburner:origLink></item>
		<item>
		<title>Easy menus with nmcDropDown</title>
		<link>http://feedproxy.google.com/~r/newmediacampaigns/development/~3/P8PNBbYOwvI/easy-menus-with-nmcdropdown</link>
		<description>&lt;a style="float: left; margin: 0 18px 18px 0;" href="http://api.tweetmeme.com/share?url=http://www.newmediacampaigns.com/page/easy-menus-with-nmcdropdown"&gt;&lt;img src="http://api.tweetmeme.com/imagebutton.gif?url=http://www.newmediacampaigns.com/page/easy-menus-with-nmcdropdown" height="61" width="51" /&gt;&lt;/a&gt;
			&lt;p&gt;&lt;img style="float:right;margin:0 0 1em 1em;" src="../files/images/nmcdropdown/nmcbowd.jpg" alt="nmcDropDown in use on ncbowd.com" /&gt;Since I released my &lt;a href="http://www.newmediacampaigns.com/page/nmcdropdown"&gt;nmcDropDown plugin for jQuery&lt;/a&gt; two weeks ago, several people have been asking for a simple example of how to use it. Althought the plugin takes care of all the behavior automatically, you still need to style and format the menu using CSS. In this post I will demonstrate &lt;a href="http://www.newmediacampaigns.com/files/nmcdropdown_example/"&gt;two simple menu styles&lt;/a&gt; that use nmcDropDown.&lt;/p&gt;
&lt;h2&gt;The bare minimum&lt;/h2&gt;
&lt;p&gt;The examples below have styling and effects to make them look nice, but first I will show you the minimum you need to get your menus running. First off, you need a set of nested lists and link to create the structure of your menus:&lt;/p&gt;
&lt;pre class="brush:html"&gt;&amp;lt;ul id="nav"&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Item One&amp;lt;/a&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Sub-menu Item 1&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Sub-menu Item 2&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Sub-menu Item 3&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Item Two&amp;lt;/a&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Sub-menu Item 1&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Sub-menu Item 2&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Sub-menu Item 3&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Item Three&amp;lt;/a&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Sub-menu Item 1&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Sub-menu Item 2&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Sub-menu Item 3&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now you need a bit of CSS to put everything in its correct place. This menu isn't going to look nice, but it will give you a starting point from which you can match your site's overall aesthetic.&lt;/p&gt;
&lt;pre class="brush:css"&gt;#nav { float: right; height: 30px; }
#nav li { float: left; position: relative; }
#nav li a { display: block; padding: 5px 10px; line-height: 20px; }
#nav li ul { display: none; position: absolute; top: 30px; left: 0; width: 120px; background: #fff; }
#nav li:hover ul { display: block; }
#nav li ul li { float: none; }
#nav li ul li a { display: inline-block; }
#nav li ul li a { display: block; }
&lt;/pre&gt;
&lt;p&gt;The most important thing here is to set &lt;code&gt;#nav li&lt;/code&gt; to &lt;code&gt;position: relative;&lt;/code&gt; and &lt;code&gt;#nav li ul&lt;/code&gt; to &lt;code&gt;position: absolute;&lt;/code&gt; to the submenus are aligned with their parent item. In the interest of accessibility, we are also hiding the submenus in the CSS (&lt;code&gt;#nav li ul { display: none; }&lt;/code&gt;) and showing them with their parent &lt;code&gt;li&lt;/code&gt; is hovered over (&lt;code&gt;#nav li:hover ul { display: block; }&lt;/code&gt;. That way, if JavaScript is disabled, the menus will still work in every browser but Internet Explorer 6. JavaScript is always required for drop-downs to work in IE6, unfortunately. Speaking of which, did you notice the oddity at the end where we declare the links as &lt;code&gt;display: inline-block&lt;/code&gt; and then re-declare them at &lt;code&gt;display: block&lt;/code&gt;? That is the only concession we need to make to Internet Explorer bugs&amp;mdash;it removes the extra space IE6 inserts between list items, as discovered by &lt;a href="http://www.456bereastreet.com/archive/200610/closing_the_gap_between_list_items_in_ie/"&gt;Roger Johansson&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This CSS will create a horizontal menu with drop-downs, but by changing a few lines, you could just as easily make it a vertical menu with fly-outs.&lt;/p&gt;
&lt;p&gt;Finally, the JavaScript to hook up the nmcDropDown script. Make sure you also paste the &lt;a href="http://www.newmediacampaigns.com/files/media/nmcdropdown/jquery.nmcDropDown.min.js"&gt;nmcDropDown script&lt;/a&gt; itself (and the &lt;a href="http://cherne.net/brian/resources/jquery.hoverIntent.html"&gt;HoverIntent plugin&lt;/a&gt;, if you wish to use it) into the JavaScript file.&lt;/p&gt;
&lt;pre class="brush:js"&gt;$('#nav').nmcDropDown();
&lt;/pre&gt;
&lt;h2&gt;Makin' it Pretty&lt;/h2&gt;
&lt;p&gt;&lt;img style="float:right" src="/files/images/nmcdropdown/menuscreenshot.png" alt="Screenshot of sample page" /&gt;Now that we have a functioning menu, we can start styling it. I have created an &lt;a href="http://www.newmediacampaigns.com/files/nmcdropdown_example/"&gt;sample page&lt;/a&gt; with two different examples using nmcDropDown.&lt;/p&gt;
&lt;p&gt;The first&amp;mdash;in the top-right of the page&amp;mdash;is based on the simplified example above, just with additional CSS to style the menu bar and drop-downs. I also added an additional parameter to the call in JavaScript to make the menus slide down instead of fading in: &lt;code&gt;$('#nav').nmcDropDown({show: {height: 'show', opacity: 'show'}});&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The second example is a little more interesting, as it's actually not a menu at all. In the right-hand sidebar, I am using nmcDropDown to create an informational panel with four heading that, which clicked on, each reveal a bit of text. To do this, I replaced the second level of &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;s in my HTML with paragraphs. I then used the following CSS to line arrange everything vertically:&lt;/p&gt;
&lt;pre class="brush:css"&gt;#sidebarNav { padding: 10px 0; background: #ccc; border: 1px solid #bbb; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; }
#sidebarNav li { border-top: 0 solid #ccc; }
#sidebarNav li:hover, #sidebarNav li.open { background: #bbb; }
#sidebarNav li a { display: block; padding: 5px 10px; line-height: 20px; color: #444; font-weight: bold; text-decoration: none; }
#sidebarNav li p { display: none; padding: 5px 10px 10px; color: #444; border-top: 1px dashed #aaa; }
#sidebarNav li:hover p { display: block; }
&lt;/pre&gt;
&lt;p&gt;Finally, in the JavaScript I told nmcDropDown to activate on click rather than hover and to look for my paragraph instead of the unordered-list it usually expects:&lt;/p&gt;
&lt;pre class="brush:js"&gt;$('#sidebarNav').nmcDropDown({
  trigger: 'click',
  submenu_selector: 'p',
  show: {height: 'show'},
  hide: {height: 'hide'}
});
&lt;/pre&gt;
&lt;p&gt;Please look at the &lt;a href="http://www.newmediacampaigns.com/files/nmcdropdown_example/"&gt;sample page&lt;/a&gt; to try these out, and view source to examine the code in more detail. If you find another creative way to use nmcDropDown, please link to it in the comments.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=P8PNBbYOwvI:ZXFu3FRUI-w:IuXuQPol3sE"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=P8PNBbYOwvI:ZXFu3FRUI-w:IuXuQPol3sE" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=P8PNBbYOwvI:ZXFu3FRUI-w:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=P8PNBbYOwvI:ZXFu3FRUI-w:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=P8PNBbYOwvI:ZXFu3FRUI-w:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=P8PNBbYOwvI:ZXFu3FRUI-w:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=P8PNBbYOwvI:ZXFu3FRUI-w:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/newmediacampaigns/development/~4/P8PNBbYOwvI" height="1" width="1"/&gt;</description>
		<guid isPermaLink="false">http://www.newmediacampaigns.com/page/easy-menus-with-nmcdropdown</guid>
		<pubDate>Mon, 02 Nov 2009 10:55:00 EST</pubDate>
	<feedburner:origLink>http://www.newmediacampaigns.com/page/easy-menus-with-nmcdropdown</feedburner:origLink></item>
		<item>
		<title>How to create a blog with the Recess PHP Framework, Part 3</title>
		<link>http://feedproxy.google.com/~r/newmediacampaigns/development/~3/D9JDRRdD5PU/how-to-create-a-blog-with-recess-php-framework-part3</link>
		<description>&lt;a style="float: left; margin: 0 18px 18px 0;" href="http://api.tweetmeme.com/share?url=http://www.newmediacampaigns.com/page/how-to-create-a-blog-with-recess-php-framework-part3"&gt;&lt;img src="http://api.tweetmeme.com/imagebutton.gif?url=http://www.newmediacampaigns.com/page/how-to-create-a-blog-with-recess-php-framework-part3" height="61" width="51" /&gt;&lt;/a&gt;
			&lt;p&gt;This is the third article in my &lt;a href="http://www.recessframework.org"&gt;Recess Framework&lt;/a&gt; blog tutorial that will demonstrate how to create model relationships with the Recess PHP Framework. Previously in this tutorial, we learned &lt;a href="http://www.newmediacampaigns.com/page/how-to-create-a-blog-with-recess-php-framework-part1"&gt;how to list and display individual blog posts&lt;/a&gt;, and more recently, &lt;a href="http://www.newmediacampaigns.com/page/how-to-create-a-blog-with-recess-php-framework-part2"&gt;how to create, update and delete blog posts using Recess&amp;rsquo; &lt;span class="caps"&gt;ORM&lt;/span&gt; (object relational mapper)&lt;/a&gt;. This tutorial assumes you have completed the previous tutorials. If you have not completed the previous tutorials, you should &lt;a href="http://www.newmediacampaigns.com/files/posts/recess-blog-tutorial2/blog.zip"&gt;download the most current version of our blog application&lt;/a&gt; before reading further.&lt;/p&gt;
&lt;p&gt;As I explained in my previous tutorial, &lt;strong&gt;[BLOG_ROOT]&lt;/strong&gt; references the filesystem directory where you have installed the Recess &lt;span class="caps"&gt;PHP&lt;/span&gt; Framework; on my machine, &lt;strong&gt;[BLOG_ROOT]&lt;/strong&gt; is &lt;code&gt;~/Sites/recess/&lt;/code&gt;. &lt;strong&gt;[BLOG_URL]&lt;/strong&gt; references the &lt;span class="caps"&gt;URL&lt;/span&gt; with which you can access the blog application in a web browser; on my machine &lt;strong&gt;[BLOG_URL]&lt;/strong&gt; is &lt;code&gt;http://localhost/~joshlockhart/recess/&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Authors&lt;/h2&gt;
&lt;p&gt;We will create a one-to-many model relationship between authors and blog posts. One author writes many blog posts, and each blog post belongs to only one author. If we were using a user authentication system, we could determine the author based on the logged-in user writing the post. We will explore user authentication with Recess later in this tutorial series. For now, we will display a combo-box on the Write Post and Edit Post forms that will display a list of available authors. Let&amp;rsquo;s get started. We first need to create the controller, models, and views for authors. Since this process was discussed in detail in the previous two two tutorials, I will move quickly through this process assuming you are familiar with these steps.&lt;/p&gt;
&lt;h3&gt;Creating the Author database table&lt;/h3&gt;
&lt;p&gt;Create the &lt;em&gt;authors&lt;/em&gt; table in your SQLite database with the following &lt;span class="caps"&gt;SQL&lt;/span&gt; commands:&lt;/p&gt;
&lt;pre class="brush:sql"&gt;CREATE TABLE `authors` (
    `id` INTEGER PRIMARY KEY,
    `name_first` TEXT,
    `name_last` TEXT
);
INSERT INTO `authors` VALUES (1,'John','Doe');
INSERT INTO `authors` VALUES (2,'Jane','Doe');
&lt;/pre&gt;
&lt;p&gt;Next, update the &lt;em&gt;posts&lt;/em&gt; database table and add an &amp;ldquo;author_id&amp;rdquo; foreign key:&lt;/p&gt;
&lt;pre class="brush:sql"&gt;ALTER TABLE `posts` ADD `author_id` INTEGER;    
&lt;/pre&gt;
&lt;p&gt;Last, let&amp;rsquo;s make sure all existing posts are assigned to the first author.&lt;/p&gt;
&lt;pre class="brush:sql"&gt;UPDATE `posts` SET `author_id` = 1;
&lt;/pre&gt;
&lt;h3&gt;Creating the Author model&lt;/h3&gt;
&lt;p&gt;Next, create the Author model at &lt;code&gt;[BLOG_ROOT]apps/blog/models/Author.class.php&lt;/code&gt; and insert the following code:&lt;/p&gt;
&lt;pre class="brush:php"&gt;&amp;lt;?php
/** 
 * !Table authors
 */
class Author extends Model {}
?&amp;gt;
&lt;/pre&gt;
&lt;h3&gt;Creating the Author controller&lt;/h3&gt;
&lt;p&gt;The Author controller is responsible for listing, creating, updating, and deleting blog authors. Create &lt;code&gt;[BLOG_ROOT]apps/blog/controllers/AuthorsController.class.php&lt;/code&gt; and insert the following code:&lt;/p&gt;
&lt;pre class="brush:php"&gt;&amp;lt;?php
Library::import('recess.framework.controllers.Controller');
Library::import('blog.models.Author');

/**
 * !RespondsWith Layouts
 * !Prefix Routes: /, Views: authors/
 */
class AuthorsController extends Controller {

    /** !Route GET, authors */
    public function listAuthors(){
        $author = new Author();
        $this-&amp;gt;authors = $author-&amp;gt;all();
    }

    /** !Route GET, authors/$id */
    public function show($id){
        $author = new Author($id);
        $this-&amp;gt;author = $author-&amp;gt;find()-&amp;gt;first();
    }

    /** !Route GET, authors/new */
    public function newAuthor(){
        $this-&amp;gt;author = new Author();
    }

    /** !Route POST, authors */
    public function create(){
        $author = new Author($this-&amp;gt;request-&amp;gt;post['author']);

        if( $author-&amp;gt;save() ){
            return $this-&amp;gt;redirect($this-&amp;gt;urlTo('listAuthors'));
        } else {
            return $this-&amp;gt;ok('newAuthor');
        }
    }

    /** !Route GET, authors/$id/edit */
    public function edit($id){
        $author = new Author($id);

        if( $author-&amp;gt;exists() ){
            $this-&amp;gt;author = $author-&amp;gt;find()-&amp;gt;first();
        } else {
            return $this-&amp;gt;redirect($this-&amp;gt;urlTo('listAuthors'));
        }
    }

    /** !Route PUT, authors/$id */
    public function update($id){
        $author = new Author($id);

        if( $author-&amp;gt;exists() ){
            $this-&amp;gt;author = $author-&amp;gt;find()-&amp;gt;first()-&amp;gt;copy($this-&amp;gt;request-&amp;gt;put['author']);
            if( $this-&amp;gt;author-&amp;gt;save() ){
                //set a success message if necessary
            }
            return $this-&amp;gt;redirect($this-&amp;gt;urlTo('edit',$id));
        } else {
            return $this-&amp;gt;redirect($this-&amp;gt;urlTo('listAuthors'));
        }
    }

    /**
     * !Route DELETE, authors/$id
     * !Route GET, authors/$id/delete
     */
    public function delete($id){
        $author = new Author($id);

        if( $author-&amp;gt;exists() ) $author-&amp;gt;delete();
        return $this-&amp;gt;redirect($this-&amp;gt;urlTo('listAuthors'));
    }

}

?&amp;gt;
&lt;/pre&gt;
&lt;h3&gt;Creating the Author view templates&lt;/h3&gt;
&lt;p&gt;Now, let&amp;rsquo;s quickly create the templates for the Author &lt;span class="caps"&gt;CRUD&lt;/span&gt; actions. Create &lt;code&gt;[BLOG_ROOT]apps/blog/views/authors/listAuthors.html.php&lt;/code&gt; and insert the following markup:&lt;/p&gt;
&lt;pre class="brush:html"&gt;&amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Authors&amp;lt;/h1&amp;gt;
        &amp;lt;ul&amp;gt;
            &amp;lt;?php foreach( $authors as $author ): ?&amp;gt;
            &amp;lt;li&amp;gt;
                &amp;lt;h2&amp;gt;&amp;lt;a href="&amp;lt;?php echo Url::action('AuthorsController::show',$author-&amp;gt;id); ?&amp;gt;"&amp;gt;&amp;lt;?php echo $author-&amp;gt;name_first . ' ' . $author-&amp;gt;name_last; ?&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h2&amp;gt;
                &amp;lt;p&amp;gt;
                    &amp;lt;a href="&amp;lt;?php echo Url::action('AuthorsController::edit',$author-&amp;gt;id); ?&amp;gt;"&amp;gt;Edit&amp;lt;/a&amp;gt;
                    &amp;lt;a href="&amp;lt;?php echo Url::action('AuthorsController::delete',$author-&amp;gt;id); ?&amp;gt;" onclick="return confirm('Are you sure?');"&amp;gt;Delete&amp;lt;/a&amp;gt;
                &amp;lt;/p&amp;gt;
            &amp;lt;/li&amp;gt;
            &amp;lt;?php endforeach; ?&amp;gt;
        &amp;lt;/ul&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;You can view the author list at &lt;code&gt;[BLOG_URL]index.php/blog/authors/&lt;/code&gt;. Next, create &lt;code&gt;[BLOG_ROOT]apps/blog/views/authors/show.html.php&lt;/code&gt; and insert the following markup:&lt;/p&gt;
&lt;pre class="brush:html"&gt;&amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;&amp;lt;?php echo $author-&amp;gt;name_first . ' ' . $author-&amp;gt;name_last; ?&amp;gt;&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;    
&lt;/pre&gt;
&lt;p&gt;You can view each author&amp;rsquo;s page at &lt;code&gt;[BLOG_URL]index.php/blog/authors/1&lt;/code&gt; (where &amp;lsquo;1&amp;rsquo; is the ID of a given author). Next, create &lt;code&gt;[BLOG_ROOT]apps/blog/views/authors/newAuthor.html.php&lt;/code&gt; and insert the following markup:&lt;/p&gt;
&lt;pre class="brush:html"&gt;&amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Add Author&amp;lt;/h1&amp;gt;
        &amp;lt;form action="&amp;lt;?php echo Url::action('AuthorsController::create'); ?&amp;gt;" method="POST"&amp;gt;
            &amp;lt;fieldset&amp;gt;
                &amp;lt;label for="firstName"&amp;gt;First Name&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;input type="text" id="firstName" name="author[name_first]" value=""/&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;label for="lastName"&amp;gt;Last Name&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;input type="text" id="lastName" name="author[name_last]" value=""/&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;input type="submit" value="Add Author"/&amp;gt;
            &amp;lt;/fieldset&amp;gt;
        &amp;lt;/form&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;    
&lt;/pre&gt;
&lt;p&gt;You can view the Add Author form at &lt;code&gt;[BLOG_URL]index.php/blog/authors/new&lt;/code&gt;. Next, create &lt;code&gt;[BLOG_ROOT]apps/blog/views/authors/edit.html.php&lt;/code&gt; and insert the following markup:&lt;/p&gt;
&lt;pre class="brush:html"&gt;&amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Edit Author&amp;lt;/h1&amp;gt;
        &amp;lt;form action="&amp;lt;?php echo Url::action('AuthorsController::update',$author-&amp;gt;id); ?&amp;gt;" method="POST"&amp;gt;
            &amp;lt;fieldset&amp;gt;
                &amp;lt;label for="firstName"&amp;gt;First Name&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;input type="text" id="firstName" name="author[name_first]" value="&amp;lt;?php echo $author-&amp;gt;name_first; ?&amp;gt;"/&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;label for="lastName"&amp;gt;Last Name&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;input type="text" id="lastName" name="author[name_last]" value="&amp;lt;?php echo $author-&amp;gt;name_last; ?&amp;gt;"/&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;input type="hidden" name="_METHOD" value="PUT"/&amp;gt;
                &amp;lt;input type="submit" value="Update Author"/&amp;gt;
            &amp;lt;/fieldset&amp;gt;
        &amp;lt;/form&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;    
&lt;/pre&gt;
&lt;p&gt;You can view the Edit Author form at &lt;code&gt;[BLOG_URL]index.php/blog/authors/1/edit&lt;/code&gt; (where &amp;lsquo;1&amp;rsquo; is the ID of a given author). Next, create blank template files at &lt;code&gt;[BLOG_ROOT]apps/blog/views/authors/create.html.php&lt;/code&gt; and &lt;code&gt;[BLOG_ROOT]apps/blog/views/authors/update.html.php&lt;/code&gt; and &lt;code&gt;[BLOG_ROOT]apps/blog/views/authors/delete.html.php&lt;/code&gt;. Although these templates are not necessary for our application, Recess requires a template for each public controller action to avoid throwing errors. The creators of Recess will remedy this behavior in a future framework update.&lt;/p&gt;
&lt;p&gt;We are now able to list, view, add, update, and delete blog authors. Now let&amp;rsquo;s relate authors to posts.&lt;/p&gt;
&lt;h2&gt;Associating Authors to Posts&lt;/h2&gt;
&lt;h3&gt;Updating the models&lt;/h3&gt;
&lt;p&gt;We need to tell Recess that each post belongs to one author. Open &lt;code&gt;[BLOG_ROOT]apps/blog/models/Author.class.php&lt;/code&gt; and update it&amp;rsquo;s contents to look like this:&lt;/p&gt;
&lt;pre class="brush:php"&gt;&amp;lt;?php
/** 
 * !Table authors
 * !HasMany posts, Key: author_id
 */
class Author extends Model {}
?&amp;gt;
&lt;/pre&gt;
&lt;p&gt;We use a Recess &lt;em&gt;declarative annotation&lt;/em&gt; to describe our Author model. In this example, we say that an Author &lt;em&gt;!HasMany&lt;/em&gt; posts. We also specify the foreign key column used by the related model if the foreign key column name is different from convention; by default, Recess expects a foreign key column name of &amp;ldquo;authorId&amp;rdquo;. I like to suffix my foreign keys with "_id", so I needed to clarify this in the &lt;em&gt;!HasMany&lt;/em&gt; annotation. Let&amp;rsquo;s now update &lt;code&gt;[BLOG_ROOT]apps/blog/models/Post.class.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="brush:php"&gt;&amp;lt;?php
/**
 * !Table posts
 * !BelongsTo author, Key: author_id
 */
class Post extends Model {}
?&amp;gt;
&lt;/pre&gt;
&lt;p&gt;We use a Recess declarative annotation to describe our Post model; a Post &lt;em&gt;!BelongsTo&lt;/em&gt; one author. We also specify the foreign key column used by this relationship. The annotation on the Post model assumes the &lt;em&gt;posts&lt;/em&gt; database table has an &amp;ldquo;author_id&amp;rdquo; foreign key column that references an existing Author database record. We added this foreign key earlier in this tutorial.&lt;/p&gt;
&lt;h3&gt;Updating the views&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s now update our Write Post and Edit Post templates so that we may select an author when we write or edit a post. Before we edit the templates, we need to make sure we pass an array of available authors from the &lt;code&gt;PostsController::write()&lt;/code&gt; and &lt;code&gt;PostsController::edit()&lt;/code&gt; actions. Open &lt;code&gt;[BLOG_ROOT]apps/blog/controllers/PostsController.class.php&lt;/code&gt;. Edit the &lt;code&gt;PostsController::write()&lt;/code&gt; action to look like this:&lt;/p&gt;
&lt;pre class="brush:php"&gt;/** !Route GET, posts/write */
public function write(){
    $this-&amp;gt;post = new Post();
    $author = new Author();
    $this-&amp;gt;authors = $author-&amp;gt;all();
}
&lt;/pre&gt;
&lt;p&gt;And update the &lt;code&gt;PostsController::edit()&lt;/code&gt; action to look like this:&lt;/p&gt;
&lt;pre class="brush:php"&gt;/** !Route GET, posts/$id/edit */
public function edit($id){
    $post = new Post($id);

    if( $post-&amp;gt;exists() ){
        $this-&amp;gt;post = $post-&amp;gt;find()-&amp;gt;first();
        $author = new Author();
        $this-&amp;gt;authors = $author-&amp;gt;all();
    } else {
        return $this-&amp;gt;redirect($this-&amp;gt;urlTo('listPosts'));
    }
}
&lt;/pre&gt;
&lt;p&gt;Since we reference the Author model in the Posts controller, we need to import the Author model into our controller. Add this line to the existing import statements at the top of &lt;code&gt;PostsController.class.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="brush:php"&gt;Library::import('blog.models.Author');    
&lt;/pre&gt;
&lt;p&gt;Now we can update our templates. Open &lt;code&gt;[BLOG_ROOT]apps/blog/views/home/write.html.php&lt;/code&gt; and update it to look like this. I have highlighted the changes.&lt;/p&gt;
&lt;pre class="brush:html;highlight:[10,11,12,13,14,15]"&gt;&amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Write Post&amp;lt;/h1&amp;gt;
        &amp;lt;form action="&amp;lt;?php echo Url::action('PostsController::create'); ?&amp;gt;" method="POST"&amp;gt;
            &amp;lt;fieldset&amp;gt;
                &amp;lt;label for="title"&amp;gt;Title&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;input type="text" id="title" name="post[title]" value=""/&amp;gt;&amp;lt;br/&amp;gt;    
                &amp;lt;label for="content"&amp;gt;Content&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;textarea id="content" name="post[content]" rows="20" cols="50"&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;label for="author"&amp;gt;Select an author:&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;select id="author" name="post[author_id]"&amp;gt;
                    &amp;lt;?php foreach( $authors as $author ): ?&amp;gt;
                    &amp;lt;option value="&amp;lt;?php echo $author-&amp;gt;id; ?&amp;gt;"&amp;gt;&amp;lt;?php echo $author-&amp;gt;name_first .' '.$author-&amp;gt;name_last; ?&amp;gt;&amp;lt;/option&amp;gt;
                    &amp;lt;?php endforeach; ?&amp;gt;
                &amp;lt;/select&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;input type="submit" value="Create Post"/&amp;gt;
            &amp;lt;/fieldset&amp;gt;
        &amp;lt;/form&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Next, open &lt;code&gt;[BLOG_ROOT]apps/blog/views/home/edit.html.php&lt;/code&gt; and update the markup so it looks like this. I have highlighted the changes.&lt;/p&gt;
&lt;pre class="brush:html;highlight:[10,11,12,13,14,15]"&gt;&amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Update Post&amp;lt;/h1&amp;gt;
        &amp;lt;form action="&amp;lt;?php echo Url::action('PostsController::update',$post-&amp;gt;id); ?&amp;gt;" method="POST"&amp;gt;
            &amp;lt;fieldset&amp;gt;
                &amp;lt;label for="title"&amp;gt;Title&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;input type="text" id="title" name="post[title]" value="&amp;lt;?php echo $post-&amp;gt;title; ?&amp;gt;"/&amp;gt;&amp;lt;br/&amp;gt;    
                &amp;lt;label for="content"&amp;gt;Content&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;textarea id="content" name="post[content]" rows="20" cols="50"&amp;gt;&amp;lt;?php echo $post-&amp;gt;content; ?&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;label for="author"&amp;gt;Select an author:&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;select id="author" name="post[author_id]"&amp;gt;
                    &amp;lt;?php foreach( $authors as $author ): ?&amp;gt;
                    &amp;lt;option value="&amp;lt;?php echo $author-&amp;gt;id; ?&amp;gt;" &amp;lt;?php if($author-&amp;gt;id == $post-&amp;gt;author_id): ?&amp;gt;selected="selected"&amp;lt;?php endif; ?&amp;gt;&amp;gt;&amp;lt;?php echo $author-&amp;gt;name_first .' '.$author-&amp;gt;name_last; ?&amp;gt;&amp;lt;/option&amp;gt;
                    &amp;lt;?php endforeach; ?&amp;gt;
                &amp;lt;/select&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;
                &amp;lt;input type="hidden" name="_METHOD" value="PUT"/&amp;gt;
                &amp;lt;input type="submit" value="Update Post"/&amp;gt;
            &amp;lt;/fieldset&amp;gt;
        &amp;lt;/form&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;When we edit an existing post, the correct author will be pre-selected.&lt;/p&gt;
&lt;h2&gt;Relationships in Action&lt;/h2&gt;
&lt;p&gt;We have successfully related Authors to Posts. Let&amp;rsquo;s see an example of this relationship in action by displaying all posts written by an author on the Show Author page. Open &lt;code&gt;[BLOG_ROOT]apps/blog/views/authors/show.html.php&lt;/code&gt; and update its markup like this:&lt;/p&gt;
&lt;pre class="brush:html"&gt;&amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;&amp;lt;?php echo $author-&amp;gt;name_first . ' ' . $author-&amp;gt;name_last; ?&amp;gt;&amp;lt;/h1&amp;gt;
        &amp;lt;ul&amp;gt;
            &amp;lt;?php foreach( $author-&amp;gt;posts() as $post ): ?&amp;gt;
            &amp;lt;li&amp;gt;&amp;lt;a href="&amp;lt;?php echo Url::action('PostsController::show',$post-&amp;gt;id); ?&amp;gt;"&amp;gt;&amp;lt;?php echo $post-&amp;gt;title; ?&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
            &amp;lt;?php endforeach; ?&amp;gt;
        &amp;lt;/ul&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Last, let&amp;rsquo;s place a link to the author&amp;rsquo;s page beneath each blog post&amp;rsquo;s title. Open &lt;code&gt;[BLOG_ROOT]apps/blog/views/home/listPosts.html.php&lt;/code&gt; and update the markup as shown below. The changes are highlighted.&lt;/p&gt;
&lt;pre class="brush:html;highlight:[8]"&gt;&amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Posts&amp;lt;/h1&amp;gt;
        &amp;lt;ul&amp;gt;
            &amp;lt;?php foreach( $posts as $post ): ?&amp;gt;
            &amp;lt;li&amp;gt;
                &amp;lt;h2&amp;gt;&amp;lt;a href="&amp;lt;?php echo Url::action('PostsController::show',$post-&amp;gt;id); ?&amp;gt;"&amp;gt;&amp;lt;?php echo $post-&amp;gt;title; ?&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h2&amp;gt;
                &amp;lt;p&amp;gt;By &amp;lt;a href="&amp;lt;?php echo Url::action('AuthorsController::show',$post-&amp;gt;author()-&amp;gt;id); ?&amp;gt;"&amp;gt;&amp;lt;?php echo $post-&amp;gt;author()-&amp;gt;name_first . ' ' . $post-&amp;gt;author()-&amp;gt;name_last; ?&amp;gt;&amp;lt;/a&amp;gt; | Posted on &amp;lt;?php echo strftime("%B %e, %Y", $post-&amp;gt;created); ?&amp;gt;&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;&amp;lt;?php echo $post-&amp;gt;content; ?&amp;gt;&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;
                    &amp;lt;a href="&amp;lt;?php echo Url::action('PostsController::edit',$post-&amp;gt;id); ?&amp;gt;"&amp;gt;Edit&amp;lt;/a&amp;gt;
                    &amp;lt;a href="&amp;lt;?php echo Url::action('PostsController::delete',$post-&amp;gt;id); ?&amp;gt;" onclick="return confirm('Are you sure?');"&amp;gt;Delete&amp;lt;/a&amp;gt;
                &amp;lt;/p&amp;gt;
            &amp;lt;/li&amp;gt;
            &amp;lt;?php endforeach; ?&amp;gt;
        &amp;lt;/ul&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Also open &lt;code&gt;[BLOG_ROOT]apps/blog/views/home/show.html.php&lt;/code&gt; and update the markup as shown below. The changes are highlighted:&lt;/p&gt;
&lt;pre class="brush:html;highlight:[4]"&gt;&amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;&amp;lt;?php echo $post-&amp;gt;title; ?&amp;gt;&amp;lt;/h1&amp;gt;
        &amp;lt;p&amp;gt;By &amp;lt;a href="&amp;lt;?php echo Url::action('AuthorsController::show',$post-&amp;gt;author()-&amp;gt;id); ?&amp;gt;"&amp;gt;&amp;lt;?php echo $post-&amp;gt;author()-&amp;gt;name_first . ' ' . $post-&amp;gt;author()-&amp;gt;name_last; ?&amp;gt;&amp;lt;/a&amp;gt; | Posted on &amp;lt;?php echo strftime("%B %e, %Y", $post-&amp;gt;created); ?&amp;gt;&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;&amp;lt;?php echo $post-&amp;gt;content; ?&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;This tutorial demonstrated how to relate the Author and Post models using &lt;em&gt;declarative annotations&lt;/em&gt;. We also learned how to declare a custom foreign key on model relationships if the foreign key column name does not adhere to the Recess Framework naming convention. And finally, we learned how to take advantage of model relationships with the Recess Framework &lt;span class="caps"&gt;ORM&lt;/span&gt; in our templates. The next tutorial in this series will explore RESTful routing in the Recess Framework. Stay tuned!&lt;/p&gt;
&lt;h2&gt;Download the final project&lt;/h2&gt;
&lt;p&gt;This file contains the Recess Framework and all controllers, models, and views for this tutorial.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.newmediacampaigns.com/files/posts/recess-blog-tutorial3/recess.zip"&gt;Download&lt;/a&gt; (ZIP, 356 KB)&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=D9JDRRdD5PU:qqPlXZNcIzE:IuXuQPol3sE"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=D9JDRRdD5PU:qqPlXZNcIzE:IuXuQPol3sE" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=D9JDRRdD5PU:qqPlXZNcIzE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=D9JDRRdD5PU:qqPlXZNcIzE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=D9JDRRdD5PU:qqPlXZNcIzE:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=D9JDRRdD5PU:qqPlXZNcIzE:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=D9JDRRdD5PU:qqPlXZNcIzE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/newmediacampaigns/development/~4/D9JDRRdD5PU" height="1" width="1"/&gt;</description>
		<guid isPermaLink="false">http://www.newmediacampaigns.com/page/how-to-create-a-blog-with-recess-php-framework-part3</guid>
		<pubDate>Wed, 28 Oct 2009 11:55:00 EDT</pubDate>
	<feedburner:origLink>http://www.newmediacampaigns.com/page/how-to-create-a-blog-with-recess-php-framework-part3</feedburner:origLink></item>
		<item>
		<title>AutoSprites - A jQuery Menu Plugin</title>
		<link>http://feedproxy.google.com/~r/newmediacampaigns/development/~3/xL_riXj6dXg/autosprites-jquery-menu-plugin</link>
		<description>&lt;a style="float: left; margin: 0 18px 18px 0;" href="http://api.tweetmeme.com/share?url=http://www.newmediacampaigns.com/page/autosprites-jquery-menu-plugin"&gt;&lt;img src="http://api.tweetmeme.com/imagebutton.gif?url=http://www.newmediacampaigns.com/page/autosprites-jquery-menu-plugin" height="61" width="51" /&gt;&lt;/a&gt;
			&lt;p&gt;&lt;img src="/files/posts/autosprites/menu-sprites.jpg" alt="AutoSprites jQuery Menu Plugin" width="560" height="67" /&gt;&lt;/p&gt;
&lt;p style="text-align: center;"&gt;&lt;a href="/files/posts/autosprites/autosprites.zip"&gt;Download the Files (Zip)&lt;/a&gt; | &lt;a href="/files/posts/autosprites/index.html"&gt;View a Demo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We have written and released a &lt;a href="/page/nmcdropdown"&gt;jquery dropdown menu plugin&lt;/a&gt; as well as a &lt;a href="/page/css-sprites2-refactored-building-an-unobtrusive-jquery-plugin"&gt;CSS Sprites2 Plugin&lt;/a&gt; -- this post is along the same lines. &amp;nbsp;Its purpose is to allow you to build an image-based menu with animated hover states as easily as possible and by using the most concise descriptions possible. &amp;nbsp;To see the results on both a horizontal and vertical menu, check out the demo.&lt;/p&gt;
&lt;h2&gt;Setting up the jQuery Menu Plugin&lt;/h2&gt;
&lt;p&gt;The first component when doing something with sprites is a combined image that contains all menu states. &amp;nbsp;For the menu above, this is the image below was used. &amp;nbsp;(This was designed by &lt;a href="http://www.liaisondesigngroup.com"&gt;Liaison Design Group&lt;/a&gt;, one of our Partners)&lt;/p&gt;
&lt;p&gt;&lt;img src="/files/posts/autosprites/horiz_sprites_post.jpg" alt="sprites" width="560" height="131" /&gt;&lt;/p&gt;
&lt;p&gt;The image contains both the normal state and the hover state. &amp;nbsp;The value to doing things this way is that it allows your site to load faster. &amp;nbsp;Rather than downloading an image for each nav item and its hoverstate, only a single image needs to be downloaded. &amp;nbsp;This minimizes the overhead of many http requests.&lt;/p&gt;
&lt;p&gt;The next thing to do, is set up the HTML for the nav bar:&lt;/p&gt;
&lt;pre class="brush:html"&gt;&amp;lt;ul id="hnav"&amp;gt; 
	&amp;lt;li id="hnavhome"&amp;gt;&amp;lt;a href="#"&amp;gt;Home&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt; 
	&amp;lt;li id="hnavlocal"&amp;gt;&amp;lt;a href="#"&amp;gt;Local Industry&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt; 
	&amp;lt;li id="hnavhigher"&amp;gt;&amp;lt;a href="#"&amp;gt;Higher Education&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt; 
	&amp;lt;li id="hnavcomm"&amp;gt;&amp;lt;a href="#"&amp;gt;Our Community&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt; 
	&amp;lt;li id="hnavnews"&amp;gt;&amp;lt;a href="#"&amp;gt;News&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt; 
&amp;lt;/ul&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Then we need to set up the CSS. There are a couple of things to note here. We are applying the background image to the containing element so that we don't need to respecify background positioning. This also makes the menu usable if javascript is disabled. Each element needs to have its size defined specifically as well.&lt;/p&gt;
&lt;pre class="brush:css"&gt;#hnav { position: absolute; top: 0; left: 0; width: 615px; height: 72px; background: url('horiz_sprites.gif') no-repeat; }
#hnav li { position: absolute; left: 0; height: 72px; }
	#hnav #hnavhome { width: 82px; left: 0px; }
	#hnav #hnavlocal { width: 146px; left: 82px; }
	#hnav #hnavhigher{ width: 162px; left: 228px; }
	#hnav #hnavcomm { width: 143px; left: 390px; }
	#hnav #hnavnews { width: 82px; left: 533px; }
#hnav li a { display: block; position: absolute; top: 0; left: 0; width: 100%; height: 72px; text-indent: -9999em; }
&lt;/pre&gt;
&lt;p&gt;Notice how much less complicated the CSS is than what is typical with sprites.  There is no need to define background positioning for each element and its hover state.  The last piece you'll need to do is enable the autosprites plugin:&lt;/p&gt;
&lt;pre class="brush:js"&gt;$(document).ready(function(){
	$('#hnav').autosprites();
});
&lt;/pre&gt;
&lt;p&gt;There are no required options.  The plugin defaults to a horizontal menu and fading for the animation.  It infers everything else from the CSS.  If you would like to customize things, here are the options that are available:&lt;/p&gt;
&lt;pre class="brush:js"&gt;settings = $.extend({
	offset: '100%', //Can be specified in px as well. This tells the plugin the offset between the two sprites images
	orientation: 'horizontal', //'vertical' is the other option
	over: { opacity: 'show' },
	overSpeed: 500,
	out: { opacity: 'hide' },
	outSpeed: 500
}, settings);
&lt;/pre&gt;
&lt;p&gt;So that's it! You can specify the minimum amount of information about your menu and the sprites will be built automatically. Be sure to check out the &lt;a href="/files/posts/autosprites/index.html"&gt;demo&lt;/a&gt; and &lt;a href="/files/posts/autosprites/autosprites.zip"&gt;download the zip&lt;/a&gt; for your own projects.  As always leave a comment if you have any complements, insults, suggestions or questions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A Special Note on Compiling Javascript&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The purpose of using sprites is to minimize HTTP requests. &amp;nbsp;It would be foolish to use this plugin by including jQuery, the minimized plugin, and a setup script. &amp;nbsp;Instead, it is best practice on a production site to bring all of your scripts together into one file, just as sprites bring your images into one file.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=xL_riXj6dXg:rGxNwwozXmI:IuXuQPol3sE"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=xL_riXj6dXg:rGxNwwozXmI:IuXuQPol3sE" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=xL_riXj6dXg:rGxNwwozXmI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=xL_riXj6dXg:rGxNwwozXmI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=xL_riXj6dXg:rGxNwwozXmI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=xL_riXj6dXg:rGxNwwozXmI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=xL_riXj6dXg:rGxNwwozXmI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/newmediacampaigns/development/~4/xL_riXj6dXg" height="1" width="1"/&gt;</description>
		<guid isPermaLink="false">http://www.newmediacampaigns.com/page/autosprites-jquery-menu-plugin</guid>
		<pubDate>Wed, 28 Oct 2009 11:15:00 EDT</pubDate>
	<feedburner:origLink>http://www.newmediacampaigns.com/page/autosprites-jquery-menu-plugin</feedburner:origLink></item>
		<item>
		<title>Introductory Regular Expression Tutorial</title>
		<link>http://feedproxy.google.com/~r/newmediacampaigns/development/~3/UCtfc2a7E9s/regular-expression-tutorial</link>
		<description>&lt;a style="float: left; margin: 0 18px 18px 0;" href="http://api.tweetmeme.com/share?url=http://www.newmediacampaigns.com/page/regular-expression-tutorial"&gt;&lt;img src="http://api.tweetmeme.com/imagebutton.gif?url=http://www.newmediacampaigns.com/page/regular-expression-tutorial" height="61" width="51" /&gt;&lt;/a&gt;
			&lt;p&gt;Regular expressions are a handy tool for developers to use. They're a concise, domain-specific language for pattern matching text. Regular expressions have many applications: input validation, data extraction, and advanced search and replace are a few good examples. In this introductory regular expression tutorial we'll take a high-level tour of primary concepts. We'll avoid details for now and revisit them in follow-up tutorials.&lt;/p&gt;
&lt;p&gt;Use our &lt;strong&gt;&lt;a href="http://gethifi.com/regexp/"&gt;free regex tester&lt;/a&gt;&lt;/strong&gt; to follow along with this tutorial. It is web-based and written in JavaScript so no download is necessary. We'll be using JavaScript style syntax, which is closely related to its &lt;a href="http://perldoc.perl.org/perlre.html"&gt;Perl regex&lt;/a&gt; inspiration. Most modern language regex implementations use a syntax like Perl/JavaScript's but the details for executing match and replace are language specific.&lt;/p&gt;
&lt;h2&gt;Simple Text Finding&lt;/h2&gt;
&lt;p&gt;If you've ever used 'Find' in a program like your web browser or text editor you've used a tool akin to the most basic regular expressions. For example, the regular expression &lt;code&gt;/hifi/g&lt;/code&gt; will match the string 'hifi'. It will not match 'HiFi' or 'high-fidelity'.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/regex-101/find.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;In order to match these three variations we can use regex choices, just like an OR.&lt;/p&gt;
&lt;h2&gt;This or That, Regex Choices&lt;/h2&gt;
&lt;p&gt;Choices are the 'OR' of regular expressions. The vertical bar character | denotes the "OR" between options. For example, &lt;code&gt;/hifi|HiFi|high-fidelity/g&lt;/code&gt;, translates to 'hifi', or 'HiFi', or 'high-fidelity'.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/regex-101/choice.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;What if we wanted to choose between two options in the middle of a regex match string? For example, what if we wanted to match the words ending in an 'ood' or 'ould' sound in the classic tongue-twister 'How much wood would a woodchuck chuck'? We could repeat a lot of information or'ing each of the words like &lt;code&gt;/wood|would|could/g&lt;/code&gt; or we could use matching groups and use a choice in the middle of the string.&lt;/p&gt;
&lt;h2&gt;Regex Matching Groups&lt;/h2&gt;
&lt;p&gt;Create matching groups by pairing parenthesis around a part of the regular expression. They allow you to 'pick out' important parts of a large match with sub-expressions. That sounds more complicated than it is. Let's look at a simple example: &lt;code&gt;/(w|c)ould/g&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/regex-101/group-simple.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Notice how there's a special match on the first letter. We have essentially picked out the small part of the string we care about: the first letter of an 'ould' sounding word.&lt;/p&gt;
&lt;p&gt;We can use a second group to match 'wood', too, with an or in the middle of the regex: &lt;code&gt;/(w|c)(oul|oo)d/g&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/regex-101/group-multi.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;What else would this regex match that is not in this string? I'll leave this as an exercise to the reader.&lt;/p&gt;
&lt;h2&gt;How Many Times? Regex Quantifiers&lt;/h2&gt;
&lt;p&gt;If we care how many times a regex factor occurs, meaning characters, groups, and classes (up next), we can use a quantifier. Quantifiers come in two formats: ranges between curly braces and the special characters *, +, and ?.&lt;/p&gt;
&lt;p&gt;Let's talk about ranges first. Google owns a number of variations on the domain 'google.com' with varying numbers of 'o's. The following domains will all take you to Google: gogle.com, google.com, gooogle.com. We can capture any of these variations using a range quantifier: &lt;code&gt;/go{1,3}gle/g&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It turns out, they also own the domain with 5 o's, but a squatter owns the domain with 4. So, we can put together the three big concepts we've learned so far: choices, groups, and quantifiers, to "match g followed by 1 to 3 o's or 5 o's followed by 'gle'" with this regex: &lt;code&gt;/g(o{1,3}|o{5})gle/g&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/regex-101/quantifier-simple.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Ranges in the form of &lt;code&gt;{N,}&lt;/code&gt; that do not have a ceiling translate to "at least N times". We can use quantifiers on groups, too, so we can find all instances of the google domain with an even number of o's like this: &lt;code&gt;/g(oo){1,}gle.com/g&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/regex-101/quantifier-atleast.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;Now we're ready to start talking about the special quantifier characters. They're simple shortcuts for commonly used ranges:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;*&lt;/code&gt; is &lt;code&gt;{0,}&lt;/code&gt; - 0 or more matches&lt;/li&gt;
&lt;li&gt;&lt;code&gt;?&lt;/code&gt; is &lt;code&gt;{0,1}&lt;/code&gt; - 0 or 1 match&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+&lt;/code&gt; is &lt;code&gt;{1,}&lt;/code&gt; - 1 or more matches&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thus, the previous example could have also been written as &lt;code&gt;/g(oo)+gle.com/g&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Regex Character Classes&lt;/h2&gt;
&lt;p&gt;We saw how we could match a 'w' or a 'c' using a group and a choice with &lt;code&gt;(w|c)&lt;/code&gt;. What if we wanted to match all vowels? &lt;code&gt;/(a|e|i|o|u)/g&lt;/code&gt;. That's starting to get verbose!&lt;/p&gt;
&lt;p&gt;There is a special construct in regular expressions called character classes, they are placed within square brackets []. For example, &lt;code&gt;/[aeiou]/g&lt;/code&gt; and &lt;code&gt;/(a|e|i|o|u)/g&lt;/code&gt; mean roughly the same thing. Ranges can be used, too, so [A-Z] is all uppercase English letters, [a-z] is all lowercase letters and [A-Za-z] all english letters. Finally, classes can be complemented with the ^ character to say "not any of these characters" so consonants could be described as the opposite of vowels: &lt;code&gt;/[^aeiou]/g&lt;/code&gt;. &lt;em&gt;(Note: Complements include all ASCII characters so &lt;code&gt;[^aeiou]&lt;/code&gt; includes numbers, punctuation, etc.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We can match words using a class of all English characters and a 1 or more quantifier, &lt;code&gt;/[A-Za-z]+/g&lt;/code&gt;, such as:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/regex-101/class.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;Pulling it All Together&lt;/h2&gt;
&lt;p&gt;As a last example let's put all the concepts we've covered in regular expressions together to pick out tags from an HTML file. HTML tags open like &lt;code&gt;&amp;lt;div id="container"&amp;gt;&lt;/code&gt; and close like &lt;code&gt;&amp;lt;/h1&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Let's focus on finding opening tags, for now. They start with a less than sign and are followed by a letter, followed 0 or more other letters or numbers. We'll use a capturing group to pick out the important part: &lt;code&gt;/&amp;lt;([A-Za-z][A-Za-z0-9]*)/g&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/regex-101/html-open.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;What if we wanted to pick out the attributes of a tag, too? We could match everything except the greater than character, using a complemented character class: &lt;code&gt;([^&amp;gt;]*)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/regex-101/html-attributes.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;As a final touch we can also match all closing tags, too. The difference between a closing tag and an opening tag is forward slash after the greater than sign like &lt;code&gt;&amp;lt;/&lt;/code&gt; that. The slash can occur 0 or 1 times, 0 for opening, 1 for closing. The forward slash is a special regex character so it must be escaped with a backslash. We'll cover escaping special characters in a future post, for now just take my word that if you use &lt;code&gt;\\/&lt;/code&gt; it means simply a forward slash. So, this last little piece looks like &lt;code&gt;(\\/?)&lt;/code&gt; and our product is &lt;code&gt;/&amp;lt;(\\/?)([A-Za-z][A-Za-z0-9]*)/g&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/regex-101/html-all.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;That's all for now, folks. We've seen how &lt;strong&gt;regular expressions&lt;/strong&gt; can be used in the most basic sense as similar to your text editor's "find", covered &lt;strong&gt;choices&lt;/strong&gt; which allow for ORs in our matching, used &lt;strong&gt;groups&lt;/strong&gt; to pick out the important parts of a match, looked at &lt;strong&gt;sequences&lt;/strong&gt; giving us the ability to specify how many times a match occurs, and finally poked around with &lt;strong&gt;character classes&lt;/strong&gt; for concise lists of characters that match or do not match. Armed with just these tools you've got 80% of the power of regular expressions under your belt.&lt;/p&gt;
&lt;p&gt;Unfortunately the other 20% of regular expression syntax is more detail oriented. These topics include special characters &amp;amp; escape codes, anchors, non-capturing groups, and lazy vs. greedy matching. We'll be covering these topics in future posts in this series. If you're eager to learn regular expressions you should subscribe to our &lt;a href="http://feeds.feedburner.com/newmediacampaigns/development"&gt;development blog&lt;/a&gt; and continue playing around with our &lt;a href="http://gethifi.com/regexp/"&gt;free regex tester&lt;/a&gt;.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=UCtfc2a7E9s:66y-qFykbZ0:IuXuQPol3sE"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=UCtfc2a7E9s:66y-qFykbZ0:IuXuQPol3sE" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=UCtfc2a7E9s:66y-qFykbZ0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=UCtfc2a7E9s:66y-qFykbZ0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=UCtfc2a7E9s:66y-qFykbZ0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=UCtfc2a7E9s:66y-qFykbZ0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=UCtfc2a7E9s:66y-qFykbZ0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/newmediacampaigns/development/~4/UCtfc2a7E9s" height="1" width="1"/&gt;</description>
		<guid isPermaLink="false">http://www.newmediacampaigns.com/page/regular-expression-tutorial</guid>
		<pubDate>Mon, 26 Oct 2009 17:00:00 EDT</pubDate>
	<feedburner:origLink>http://www.newmediacampaigns.com/page/regular-expression-tutorial</feedburner:origLink></item>
		<item>
		<title>Getting content from a remote server using Smarty</title>
		<link>http://feedproxy.google.com/~r/newmediacampaigns/development/~3/jSrPCwSendg/get-remote</link>
		<description>&lt;a style="float: left; margin: 0 18px 18px 0;" href="http://api.tweetmeme.com/share?url=http://www.newmediacampaigns.com/page/get-remote"&gt;&lt;img src="http://api.tweetmeme.com/imagebutton.gif?url=http://www.newmediacampaigns.com/page/get-remote" height="61" width="51" /&gt;&lt;/a&gt;
			&lt;p&gt;Although our &lt;a href="http://www.newmediacampaigns.com/page/content-management-system"&gt;CMS&lt;/a&gt; is designed to handle most of the requirements of our clients, occasionally we find ourselves needing to include information that is stored in another system. One such project (the details of which I can't reveal yet) was just such a case. The client has a legacy e-commerce and CRM system running on a Windows server, which they are tied to for various reasons.&lt;/p&gt;
&lt;p&gt;Ideally, we would interact with the remote app by using public APIs to fetch data and do any processing and formatting on our server before outputting it in a &lt;a href="http://www.smarty.net/"&gt;Smarty&lt;/a&gt; template. This particular system, however, does not provide an API.&lt;/p&gt;
&lt;p&gt;Fortunately, PHP provides several ways to get the text of a remote file and manipulate it as you would any other content. The easiest of these methods, and the one we decided to use, is &lt;a href="http://php.net/manual/en/function.file-get-contents.php"&gt;file_get_contents&lt;/a&gt;. Given the name of a file, this function returns its entire contents as a string, easy as that. Now we can just output that string into the content area of our template, surrounded by the same header, footer, and navigation as the other pages on the site.&lt;/p&gt;
&lt;h3&gt;Is It Really That Easy?&lt;/h3&gt;
&lt;p&gt;Unfortunately, there are a few caveats. The first is that you either need to have access to the templating system of the app you are pulling from&amp;mdash;so you can remove everything but the actual content&amp;mdash;or you will need an &lt;a href="http://simplehtmldom.sourceforge.net/"&gt;HTML parser&lt;/a&gt; to pull out the section you want to use. Dropping an entire html page into your template will give you a huge mess, as you will have duplicate &amp;lt;html&amp;gt;, &amp;lt;head&amp;gt;, and &amp;lt;body&amp;gt; tags, which is most definitely not allowed.&lt;/p&gt;
&lt;p&gt;The second issue is that any relative urls in image or link tags will now be broken. We are using a regular expression (created using our handy new &lt;a href="http://gethifi.com/regexp/"&gt;RegEx Tester&lt;/a&gt;!) to find all the relative urls and replace them with absolute urls pointing back to the original server.&lt;/p&gt;
&lt;h2&gt;Smarty Plugin&lt;/h2&gt;
&lt;p&gt;I have wrapped this whole process up in a Smarty plugin. Just &lt;a href="/files/media/get_remote/function.get_remote.php.zip"&gt;download the plugin file&lt;/a&gt; and drop it into the plugins folder inside your copy of Smarty. There is no configuration or installation: you're ready to go.&lt;/p&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;pre class="brush:php"&gt;{get_remote url="http://www.test.com/page.html" [default="Text to show if the file isn't found"] [fix="href|src"]}&lt;/pre&gt;
&lt;h4&gt;Parameters&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;url&lt;/strong&gt;: The remote url to fetch. This is the only required parameter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;default&lt;/strong&gt;: Text to be shown if the remote file couldn't be found or if no url is passed in. Defaults to "Not found".&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fix&lt;/strong&gt;: Pipe-delimited list of html attributes that will have their contents checked for relative urls. Defaults to "href|src|action".&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Warnings!&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Smarty evaluates PHP code that is included this way!&lt;/strong&gt; Generally, this shouldn't be a problem, since the remote server will have already processed the php code and given you the results. However there is the potential for serious security issues here if, for instance, the remote server doesn't have PHP enabled and gives you the raw source code. If you don't trust the remote server, you probably shouldn't be using this plugin.&lt;/p&gt;
&lt;p&gt;This is a beta release. There are likely still bugs in it. If you find any, please report them in the comments.&lt;/p&gt;
&lt;h4&gt;What is still to do?&lt;/h4&gt;
&lt;p&gt;Caching is the main thing. I plan to add that capability within the next few weeks. I would also like to implement more flexible url replacements at some point (i.e. not just in html attributes), but that is a fairly complex problem.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=jSrPCwSendg:gL9J5avXVSg:IuXuQPol3sE"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=jSrPCwSendg:gL9J5avXVSg:IuXuQPol3sE" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=jSrPCwSendg:gL9J5avXVSg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=jSrPCwSendg:gL9J5avXVSg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=jSrPCwSendg:gL9J5avXVSg:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=jSrPCwSendg:gL9J5avXVSg:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=jSrPCwSendg:gL9J5avXVSg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/newmediacampaigns/development/~4/jSrPCwSendg" height="1" width="1"/&gt;</description>
		<guid isPermaLink="false">http://www.newmediacampaigns.com/page/get-remote</guid>
		<pubDate>Thu, 22 Oct 2009 16:35:00 EDT</pubDate>
	<feedburner:origLink>http://www.newmediacampaigns.com/page/get-remote</feedburner:origLink></item>
		<item>
		<title>Free Regular Expression Tool for Writing and Testing RegExps</title>
		<link>http://feedproxy.google.com/~r/newmediacampaigns/development/~3/8gf42hhrqik/free-regular-expression-software-for-writing-and-testing-regexps</link>
		<description>&lt;a style="float: left; margin: 0 18px 18px 0;" href="http://api.tweetmeme.com/share?url=http://www.newmediacampaigns.com/page/free-regular-expression-software-for-writing-and-testing-regexps"&gt;&lt;img src="http://api.tweetmeme.com/imagebutton.gif?url=http://www.newmediacampaigns.com/page/free-regular-expression-software-for-writing-and-testing-regexps" height="61" width="51" /&gt;&lt;/a&gt;
			&lt;p&gt;&lt;a href="http://gethifi.com/regexp/"&gt;&lt;img src="/files/regex-screen.gif" alt="Regular Expression Editor" width="560" height="444" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You know the green-on-black code flying all over the computer screens in The Matrix? Regular Expressions don't look too different. They can be really gnarly and debugging them is a pain. This is why we created a &lt;a href="http://gethifi.com/regexp/"&gt;free Regular Expression tool to help learn, author, and test RegExps&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Regular Expressions are a powerful way of programmatically searching for and replacing text. They're used in a wide variety of ways.&amp;nbsp; Some examples include validating inputs to ensure a user has provided a valid phone number and email address, finding all links to external websites on a web page, extracting content from one website to another, etc.&lt;/p&gt;
&lt;p&gt;I will be using the HiFi RegExp Tool in an upcoming blog post series on "Getting Comfortable with Regular Expressions". You should &lt;a href="http://feeds.feedburner.com/newmediacampaigns/development"&gt;subscribe to our development blog&lt;/a&gt; to receive updates on this series as they come.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=8gf42hhrqik:UjvULwIJRRk:IuXuQPol3sE"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=8gf42hhrqik:UjvULwIJRRk:IuXuQPol3sE" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=8gf42hhrqik:UjvULwIJRRk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=8gf42hhrqik:UjvULwIJRRk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=8gf42hhrqik:UjvULwIJRRk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=8gf42hhrqik:UjvULwIJRRk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=8gf42hhrqik:UjvULwIJRRk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/newmediacampaigns/development/~4/8gf42hhrqik" height="1" width="1"/&gt;</description>
		<guid isPermaLink="false">http://www.newmediacampaigns.com/page/free-regular-expression-software-for-writing-and-testing-regexps</guid>
		<pubDate>Wed, 21 Oct 2009 11:10:00 EDT</pubDate>
	<feedburner:origLink>http://www.newmediacampaigns.com/page/free-regular-expression-software-for-writing-and-testing-regexps</feedburner:origLink></item>
		<item>
		<title>How to create a blog with the Recess PHP Framework, Part 2</title>
		<link>http://feedproxy.google.com/~r/newmediacampaigns/development/~3/KrGHt_0DDc0/how-to-create-a-blog-with-recess-php-framework-part2</link>
		<description>&lt;a style="float: left; margin: 0 18px 18px 0;" href="http://api.tweetmeme.com/share?url=http://www.newmediacampaigns.com/page/how-to-create-a-blog-with-recess-php-framework-part2"&gt;&lt;img src="http://api.tweetmeme.com/imagebutton.gif?url=http://www.newmediacampaigns.com/page/how-to-create-a-blog-with-recess-php-framework-part2" height="61" width="51" /&gt;&lt;/a&gt;
			&lt;p&gt;This is the second article in my Recess &lt;span class="caps"&gt;PHP&lt;/span&gt; Framework blog tutorial. The &lt;a href="http://www.newmediacampaigns.com/page/how-to-create-a-blog-with-recess-framework-part1"&gt;previous article&lt;/a&gt; demonstrates how to setup a basic blog that lists posts with links to individual posts. This tutorial will demonstrate how to create, update, and delete blog posts. The code in this tutorial picks up where the previous tutorial left off. If necessary, you can &lt;a href="http://www.newmediacampaigns.com/files/posts/recess-blog-tutorial/recess.zip"&gt;download the completed tutorial 1 code&lt;/a&gt; before reading further.&lt;/p&gt;
&lt;p&gt;As I explained in my previous tutorial, &lt;strong&gt;[BLOG_ROOT]&lt;/strong&gt; references the filesystem directory where you have installed the Recess &lt;span class="caps"&gt;PHP&lt;/span&gt; Framework; on my machine, &lt;strong&gt;[BLOG_ROOT]&lt;/strong&gt; is &lt;code&gt;~/Sites/recess/&lt;/code&gt;. &lt;strong&gt;[BLOG_URL]&lt;/strong&gt; references the &lt;span class="caps"&gt;URL&lt;/span&gt; with which you can access the blog application in a web browser; on my machine &lt;strong&gt;[BLOG_URL]&lt;/strong&gt; is &lt;code&gt;http://localhost/~joshlockhart/recess/&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Creating posts&lt;/h2&gt;
&lt;p&gt;We require two controller actions to create a post. First, we need a &lt;code&gt;PostsController::write()&lt;/code&gt; action to display an &lt;span class="caps"&gt;HTML&lt;/span&gt; form. This form will submit to the &lt;code&gt;PostsController::create()&lt;/code&gt; action that will insert the new post into the database.&lt;/p&gt;
&lt;p&gt;First, create the &lt;code&gt;PostsController::write()&lt;/code&gt; action using this code:&lt;/p&gt;
&lt;pre class="brush:php"&gt;/** !Route GET, posts/write */
public function write(){
    $this-&amp;gt;post = new Post();
}
&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 1: We can access this action with a &lt;span class="caps"&gt;GET&lt;/span&gt; request to &lt;code&gt;[BLOG_URL]index.php/blog/posts/write&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 3: We provide a new empty Post to our view template&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s create the template for this controller action. Create &lt;code&gt;[BLOG_ROOT]views/home/write.html.php&lt;/code&gt;. Open this file in a text editor and insert the following markup:&lt;/p&gt;
&lt;pre class="brush:html"&gt;&amp;lt;html&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Write Post&amp;lt;/h1&amp;gt;
    &amp;lt;form action="&amp;lt;?php echo Url::action('PostsController::create'); ?&amp;gt;" method="POST"&amp;gt;
        &amp;lt;fieldset&amp;gt;
            &amp;lt;label for="title"&amp;gt;Title&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;input type="text" id="title" name="post[title]" value=""/&amp;gt;&amp;lt;br/&amp;gt;    
            &amp;lt;label for="content"&amp;gt;Content&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;textarea id="content" name="post[content]" rows="20" cols="50"&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;input type="submit" value="Create Post"/&amp;gt;
        &amp;lt;/fieldset&amp;gt;
    &amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;View the &lt;code&gt;PostsController::write()&lt;/code&gt; action in a web browser at &lt;code&gt;[BLOG_URL]index.php/blog/posts/write/&lt;/code&gt;. You should see an error. This is expected, because we have not yet created the &lt;code&gt;PostsController::create()&lt;/code&gt; action which must exist since we reference this action with the Url helper in the &lt;code&gt;PostsController::write()&lt;/code&gt; template. Let&amp;rsquo;s do that now.&lt;/p&gt;
&lt;p&gt;Create the &lt;code&gt;PostsController::create()&lt;/code&gt; action using this code:&lt;/p&gt;
&lt;pre class="brush:php"&gt;/** !Route POST, posts */
public function create(){
    $post = new Post($this-&amp;gt;request-&amp;gt;post['post']);

    if( $post-&amp;gt;save() ){
        return $this-&amp;gt;redirect($this-&amp;gt;urlTo('listPosts'));
    } else {
        return $this-&amp;gt;ok('write');
    }
}
&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 1: We can access this action with a &lt;span class="caps"&gt;POST&lt;/span&gt; request to &lt;code&gt;[BLOG_URL]index.php/blog/posts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 3: We instantiate a new post with data passed from our form&lt;/li&gt;
&lt;li&gt;Line 5: We attempt to save the new post to the database&lt;/li&gt;
&lt;li&gt;Line 6: If post saves, redirect to the &lt;code&gt;PostsController::listPosts()&lt;/code&gt; action&lt;/li&gt;
&lt;li&gt;Line 8: If post does not save, render the &lt;code&gt;PostsController::write()&lt;/code&gt; template&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are several new features introduced in this action. First, we utilize the &lt;code&gt;$this-&amp;gt;request&lt;/code&gt; object. Every Recess controller provides easy access to the current request via &lt;code&gt;$this-&amp;gt;request&lt;/code&gt;. If you &lt;code&gt;print_r($this-&amp;gt;request);&lt;/code&gt; in a controller, you can see all of the information available. In the &lt;code&gt;PostsController::create()&lt;/code&gt; action, we access a &lt;span class="caps"&gt;POST&lt;/span&gt; variable from &lt;code&gt;$this-&amp;gt;request-&amp;gt;post&lt;/code&gt;, an array of all &lt;span class="caps"&gt;POST&lt;/span&gt; variables in the current request. Similarly, you could also access &lt;span class="caps"&gt;GET&lt;/span&gt; or &lt;span class="caps"&gt;PUT&lt;/span&gt; variables with &lt;code&gt;$this-&amp;gt;request-&amp;gt;get['variable_name']&lt;/code&gt; or &lt;code&gt;$this-&amp;gt;request-&amp;gt;put['variable_name']&lt;/code&gt;, respectively.&lt;/p&gt;
&lt;p&gt;We also use a controller helper method called &lt;code&gt;$this-&amp;gt;urlTo('action_name');&lt;/code&gt;. This helper method returns the full &lt;span class="caps"&gt;URL&lt;/span&gt; to a controller action, useful for redirects, etc, within a controller. Why not just hard-code a &lt;span class="caps"&gt;URL&lt;/span&gt;? Because routing information may change even if the controller actions do not (keep in mind this is a modular blog application that may be shared among Recess installations). This controller helper method allows us to maintain redirects while respecting controller action and route decoupling.&lt;/p&gt;
&lt;p&gt;Before you try to create a new post, there is one small caveat. Currently, Recess requires you to create a template for &lt;strong&gt;every&lt;/strong&gt; controller action, even if a template is not needed. So, go ahead and create an empty file at &lt;code&gt;[BLOG_ROOT]views/home/create.html.php&lt;/code&gt;. The Recess developers are aware of this issue and will remedy this in a future update. Also, Recess does not yet have a &amp;ldquo;Flash&amp;rdquo; messaging feature similar to Rails or CakePHP. If you need to display a message to users, you will need to set a controller instance variable (&lt;code&gt;$this-&amp;gt;flash&lt;/code&gt;) before rendering a template; you can then check if this variable exists in the template and render a message if necessary.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/recess-blog-tutorial2/write-post.png" alt="Write post HTML form" /&gt;&lt;/p&gt;
&lt;p&gt;Now you can create a new post at &lt;code&gt;[BLOG_URL]index.php/blog/posts/write&lt;/code&gt;. When you submit the form, you should be redirected to the listing page and see your new post in the list.&lt;/p&gt;
&lt;h2&gt;Editing posts&lt;/h2&gt;
&lt;p&gt;We also need two controller actions to edit a post; the &lt;code&gt;PostsController::edit()&lt;/code&gt; action will display an &lt;span class="caps"&gt;HTML&lt;/span&gt; form populated with the current post&amp;rsquo;s details; the &lt;code&gt;PostsController::update()&lt;/code&gt; action will update a post in the database with details passed from the &lt;span class="caps"&gt;HTML&lt;/span&gt; form.&lt;/p&gt;
&lt;p&gt;First, create the &lt;code&gt;PostsController::edit()&lt;/code&gt; action with this code:&lt;/p&gt;
&lt;pre class="brush:php"&gt;/** !Route GET, posts/$id/edit */
public function edit($id){
    $post = new Post($id);

    if( $post-&amp;gt;exists() ){
        $this-&amp;gt;post = $post-&amp;gt;find()-&amp;gt;first();
    } else {
        return $this-&amp;gt;redirect($this-&amp;gt;urlTo('listPosts'));
    }
}
&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 1: We can access this action with a &lt;span class="caps"&gt;GET&lt;/span&gt; request to &lt;code&gt;[BLOG_URL]index.php/blog/posts/1/edit&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 2: We pass the dynamic route &lt;code&gt;$id&lt;/code&gt; parameter into the action&lt;/li&gt;
&lt;li&gt;Line 3: We instantiate a Post with the specified ID&lt;/li&gt;
&lt;li&gt;Line 5: We check to see if the Post with the specified ID exists&lt;/li&gt;
&lt;li&gt;Line 6: If post exists, we pass the Post to the template&lt;/li&gt;
&lt;li&gt;Line 8: If post does not exist, we redirect to the listing view&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s now create the template for this action. Create &lt;code&gt;[BLOG_ROOT]views/home/edit.html.php&lt;/code&gt; and insert the following markup:&lt;/p&gt;
&lt;pre class="brush:html"&gt;&amp;lt;html&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Update Post&amp;lt;/h1&amp;gt;
    &amp;lt;form action="&amp;lt;?php echo Url::action('PostsController::update',$post-&amp;gt;id); ?&amp;gt;" method="POST"&amp;gt;
        &amp;lt;fieldset&amp;gt;
            &amp;lt;label for="title"&amp;gt;Title&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;input type="text" id="title" name="post[title]" value="&amp;lt;?php echo $post-&amp;gt;title; ?&amp;gt;"/&amp;gt;&amp;lt;br/&amp;gt;    
            &amp;lt;label for="content"&amp;gt;Content&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;textarea id="content" name="post[content]" rows="20" cols="50"&amp;gt;&amp;lt;?php echo $post-&amp;gt;content; ?&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;br/&amp;gt;
            &amp;lt;input type="hidden" name="_METHOD" value="PUT"/&amp;gt;
            &amp;lt;input type="submit" value="Update Post"/&amp;gt;
        &amp;lt;/fieldset&amp;gt;
    &amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This markup is very similar to the write &lt;span class="caps"&gt;HTML&lt;/span&gt; form, but there are subtle differences. First, the form action is different, pointing to the &lt;code&gt;PostsController::update()&lt;/code&gt; action instead of the &lt;code&gt;PostsController::write()&lt;/code&gt; action. Second, although the form&amp;rsquo;s method is technically &amp;ldquo;POST&amp;rdquo;, we override this method using a hidden field with name &amp;ldquo;_METHOD&amp;rdquo; (the leading underscore and capitalization are important) and value &amp;ldquo;PUT&amp;rdquo; (capitalization is important). When Recess receives this &lt;span class="caps"&gt;POST&lt;/span&gt; request, it will treat it like a &lt;span class="caps"&gt;PUT&lt;/span&gt; request. We will cover this concept more in a later tutorial about RESTful routing in Recess.&lt;/p&gt;
&lt;p&gt;We can now view this action in web browser at &lt;code&gt;[BLOG_URL]index.php/blog/posts/1/edit&lt;/code&gt;; however, we receive an error. Because we reference the &lt;code&gt;PostsController::update()&lt;/code&gt; action with the Url view helper, we must first create the &lt;code&gt;PostsController::update()&lt;/code&gt; action. Let&amp;rsquo;s do that now.&lt;/p&gt;
&lt;p&gt;Create the PostsController::update() action with this code:&lt;/p&gt;
&lt;pre class="brush:php"&gt;/** !Route PUT, posts/$id */
public function update($id){
    $post = new Post($id);

    if( $post-&amp;gt;exists() ){
        $this-&amp;gt;post = $post-&amp;gt;find()-&amp;gt;first()-&amp;gt;copy($this-&amp;gt;request-&amp;gt;put['post']);
        if( $this-&amp;gt;post-&amp;gt;save() ){
            //set a success message if necessary
        }
        return $this-&amp;gt;redirect($this-&amp;gt;urlTo('edit',$id));
    } else {
        return $this-&amp;gt;redirect($this-&amp;gt;urlTo('listPosts'));
    }
}
&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 1: We can access this action with a &lt;span class="caps"&gt;PUT&lt;/span&gt; request to &lt;code&gt;[BLOG_URL]index.php/blog/posts/1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 2: We pass the dynamic route &lt;code&gt;$id&lt;/code&gt; parameter into the action&lt;/li&gt;
&lt;li&gt;Line 3: We instantiate a post with the specified ID&lt;/li&gt;
&lt;li&gt;Line 5: We check to see if the post with the specified ID exists&lt;/li&gt;
&lt;li&gt;Line 6: If post exists, we copy the new form data into the post&lt;/li&gt;
&lt;li&gt;Line 7: We attempt to save the post&lt;/li&gt;
&lt;li&gt;Line 10: We redirect back to the post&amp;rsquo;s edit form&lt;/li&gt;
&lt;li&gt;Line 12: If post does not exist, we redirect to the listing view&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As I explained above, Recess currently requires a template for every controller action, even if a template is not needed. Create an empty template file at &lt;code&gt;[BLOG_ROOT]views/home/update.html.php&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/recess-blog-tutorial2/update-post.png" alt="Update post HTML form" /&gt;&lt;/p&gt;
&lt;p&gt;Now you can view &lt;code&gt;[BLOG_URL]index.php/blog/posts/1/edit&lt;/code&gt; in a web browser. You should see an &lt;span class="caps"&gt;HTML&lt;/span&gt; form populated with the specified post&amp;rsquo;s details. If you edit the details and click the &amp;lsquo;Update Post&amp;rsquo; button, you will see the same edit form with the new details.&lt;/p&gt;
&lt;h2&gt;Deleting posts&lt;/h2&gt;
&lt;p&gt;We only need one additional action to delete posts. Create the &lt;code&gt;PostsController::delete()&lt;/code&gt; action with this code:&lt;/p&gt;
&lt;pre class="brush:php"&gt;/**
 * !Route DELETE, posts/$id
 * !Route GET, posts/$id/delete
 */
public function delete($id){
    $post = new Post($id);

    if( $post-&amp;gt;exists() ) $post-&amp;gt;delete();
    return $this-&amp;gt;redirect($this-&amp;gt;urlTo('listPosts'));
}
&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 2: We can access this action with a &lt;span class="caps"&gt;DELETE&lt;/span&gt; request to &lt;code&gt;[BLOG_URL]index.php/blog/posts/1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 3: We can access this action with a &lt;span class="caps"&gt;GET&lt;/span&gt; request to &lt;code&gt;[BLOG_URL]index.php/blog/posts/1/delete&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 5: We pass the dynamic route &lt;code&gt;$id&lt;/code&gt; parameter into the action&lt;/li&gt;
&lt;li&gt;Line 6: We instantiate a post with the specified ID&lt;/li&gt;
&lt;li&gt;Line 8: We delete the post if it exists&lt;/li&gt;
&lt;li&gt;Line 9: We redirect back to the listing view&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The only new feature with this action is that we use multiple routes. I have added the &lt;span class="caps"&gt;GET&lt;/span&gt; route only for convenience. Although this action does not require a template, Recess requires a blank template at &lt;code&gt;[BLOG_ROOT]views/home/delete.html.php&lt;/code&gt; to avoid an error.&lt;/p&gt;
&lt;p&gt;Finally, let&amp;rsquo;s add &amp;ldquo;Edit&amp;rdquo; and &amp;ldquo;Delete&amp;rdquo; links to the &lt;code&gt;PostsController::listPosts()&lt;/code&gt; template for convenience. Update &lt;code&gt;[BLOG_ROOT]views/home/listPosts.html.php&lt;/code&gt; to read:&lt;/p&gt;
&lt;pre class="brush:html"&gt;&amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;Posts&amp;lt;/h1&amp;gt;
        &amp;lt;ul&amp;gt;
            &amp;lt;?php foreach( $posts as $post ): ?&amp;gt;
            &amp;lt;li&amp;gt;
                &amp;lt;h2&amp;gt;&amp;lt;a href="&amp;lt;?php echo Url::action('PostsController::show',$post-&amp;gt;id); ?&amp;gt;"&amp;gt;&amp;lt;?php echo $post-&amp;gt;title; ?&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h2&amp;gt;
                &amp;lt;p&amp;gt;Posted on &amp;lt;?php echo strftime("%B %e, %Y", $post-&amp;gt;created); ?&amp;gt;&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;&amp;lt;?php echo $post-&amp;gt;content; ?&amp;gt;&amp;lt;/p&amp;gt;
                &amp;lt;p&amp;gt;
                    &amp;lt;a href="&amp;lt;?php echo Url::action('PostsController::edit',$post-&amp;gt;id); ?&amp;gt;"&amp;gt;Edit&amp;lt;/a&amp;gt;
                    &amp;lt;a href="&amp;lt;?php echo Url::action('PostsController::delete',$post-&amp;gt;id); ?&amp;gt;" onclick="return confirm('Are you sure?');"&amp;gt;Delete&amp;lt;/a&amp;gt;
                &amp;lt;/p&amp;gt;
            &amp;lt;/li&amp;gt;
            &amp;lt;?php endforeach; ?&amp;gt;
        &amp;lt;/ul&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&lt;img src="http://www.newmediacampaigns.com/files/posts/recess-blog-tutorial2/list-posts.png" alt="Blog post listing with edit and delete links" /&gt;&lt;/p&gt;
&lt;p&gt;Now when you view &lt;code&gt;[BLOG_URL]index.php/blog/posts&lt;/code&gt; in a web browser, you can click &amp;ldquo;Edit&amp;rdquo; or &amp;ldquo;Delete&amp;rdquo; beside each post to edit or remove the post.&lt;/p&gt;
&lt;h2&gt;In summary&lt;/h2&gt;
&lt;p&gt;This article demonstrates how to create &lt;span class="caps"&gt;CRUD&lt;/span&gt; (create, update, and delete) actions in the posts controller. You can now view &lt;code&gt;[BLOG_URL]index.php/blog/posts/&lt;/code&gt; in a web browser to view posts, create posts, edit posts, and delete posts. The next article in this series will explore model relationships by adding comments and tags to our blog posts. Stay tuned!&lt;/p&gt;
&lt;h2&gt;Download the final project&lt;/h2&gt;
&lt;p&gt;This file contains the Recess Framework and all controllers, models, and views for this tutorial.&lt;/p&gt;
&lt;p&gt;&lt;a title="Download" href="http://www.newmediacampaigns.com/files/posts/recess-blog-tutorial2/blog.zip"&gt;Download&lt;/a&gt; (ZIP, 342 KB)&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=KrGHt_0DDc0:n_7GW20_Pg0:IuXuQPol3sE"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=KrGHt_0DDc0:n_7GW20_Pg0:IuXuQPol3sE" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=KrGHt_0DDc0:n_7GW20_Pg0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=KrGHt_0DDc0:n_7GW20_Pg0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=KrGHt_0DDc0:n_7GW20_Pg0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=KrGHt_0DDc0:n_7GW20_Pg0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=KrGHt_0DDc0:n_7GW20_Pg0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/newmediacampaigns/development/~4/KrGHt_0DDc0" height="1" width="1"/&gt;</description>
		<guid isPermaLink="false">http://www.newmediacampaigns.com/page/how-to-create-a-blog-with-recess-php-framework-part2</guid>
		<pubDate>Fri, 16 Oct 2009 10:45:00 EDT</pubDate>
	<feedburner:origLink>http://www.newmediacampaigns.com/page/how-to-create-a-blog-with-recess-php-framework-part2</feedburner:origLink></item>
		<item>
		<title>nmcDropDown: A Drop-Down Menu Plugin for jQuery</title>
		<link>http://feedproxy.google.com/~r/newmediacampaigns/development/~3/oh129ZoxjTw/nmcdropdown</link>
		<description>&lt;a style="float: left; margin: 0 18px 18px 0;" href="http://api.tweetmeme.com/share?url=http://www.newmediacampaigns.com/page/nmcdropdown"&gt;&lt;img src="http://api.tweetmeme.com/imagebutton.gif?url=http://www.newmediacampaigns.com/page/nmcdropdown" height="61" width="51" /&gt;&lt;/a&gt;
			&lt;p&gt;&lt;a href="http://www.ncbowd.com/" target="_blank"&gt;&lt;img src="/files/posts/randoms/nmcdropdown.jpg" alt="ncbowd" width="550" height="256" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In my post on &lt;a href="http://www.newmediacampaigns.com/page/lazy-development"&gt;using template site to save time&lt;/a&gt;, I included a quick jQuery plugin for creating drop-down menus. Although that code has served me well I thought it was time to revisit it and make it more flexible and bullet-proof. This is the updated version that I will be including in my blank site template going forward&lt;/p&gt;
&lt;p&gt;I found that I was still having to modify my plugin code to accomodate variations in interaction design. Since I sometimes wanted the menus to only appear whe
&lt;script src="http://newmediacampaigns.com/control/media/scripts/tinymce/themes/advanced/langs/en.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="http://newmediacampaigns.com/control/media/scripts/tinymce/plugins/filemanager/language/index.php?type=fm&amp;amp;format=tinymce_3_x&amp;amp;group=tinymce&amp;amp;prefix=filemanager_" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="http://newmediacampaigns.com/control/media/scripts/tinymce/plugins/imagemanager/language/index.php?type=im&amp;amp;format=tinymce_3_x&amp;amp;group=tinymce&amp;amp;prefix=imagemanager_" type="text/javascript"&gt;&lt;/script&gt;
n the
&lt;script src="http://newmediacampaigns.com/control/media/scripts/tinymce/themes/advanced/langs/en.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="http://newmediacampaigns.com/control/media/scripts/tinymce/plugins/filemanager/language/index.php?type=fm&amp;amp;format=tinymce_3_x&amp;amp;group=tinymce&amp;amp;prefix=filemanager_" type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="http://newmediacampaigns.com/control/media/scripts/tinymce/plugins/imagemanager/language/index.php?type=im&amp;amp;format=tinymce_3_x&amp;amp;group=tinymce&amp;amp;prefix=imagemanager_" type="text/javascript"&gt;&lt;/script&gt;
y are clicked&amp;mdash;rather than on hover&amp;mdash;now that is an option. Also, when the top-level menu items are smaller (or when the sub-menus fly out from the side) the usability can be vastly increased by Brian Cherne's wonderful &lt;a href="http://cherne.net/brian/resources/jquery.hoverIntent.html"&gt;hoverIntent plugin&lt;/a&gt;. HoverIntent keeps the sub-menu from disappearing if your pointer leaves the edge for a split-second. This version will use hoverIntent automatically if you have it; otherwise it will revert to the standard hover event. I recommend copying the &lt;a href="http://cherne.net/brian/resources/jquery.hoverIntent.minified.js"&gt;minified version of hoverIntent&lt;/a&gt; into the bottom of your main JavaScript file. It is quite small and including it in your main file will save an http request and speed up your page loading time.&lt;/p&gt;
&lt;h2&gt;Usage&lt;/h2&gt;
&lt;p&gt;You should create your navigation as a nested unordered list. Linking the top-level items is optional and will not change the working of the plugin. You should style your menu first with css. How you do that is up to you, but you should set the sub-menus to &lt;code&gt;display: none&lt;/code&gt; (this is actually optional, but will prevent the sub-menus from flashing on page-load).&lt;/p&gt;
&lt;pre class="brush:html"&gt;&amp;lt;ul id="nav"&amp;gt;
    &amp;lt;li&amp;gt;About
        &amp;lt;ul&amp;gt;
            &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Profile&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Board of Directors&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Contact Info&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
    &amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Products&amp;lt;/a&amp;gt;
        &amp;lt;ul&amp;gt;
            &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Widgets&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Doohickeys&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
            &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Thing-a-ma-bobs&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
    &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The nmcDropDown plugin comes with sensible defaults, so you can just call it on the &amp;lt;ul&amp;gt; element that contains your navigation. The plugin will check each menu item to see if it has a sub-menu and set it up as a drop-down if it does.&lt;/p&gt;
&lt;pre class="brush:js"&gt;$('#nav').nmcDropDown();&lt;/pre&gt;
&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;p&gt;If you want more control over the way your menu works, there are a number of configuration options available to you. Pass an object containing any that you want to change in as the first parameter of the nmcDropDown() call.&lt;/p&gt;
&lt;pre class="brush:js"&gt;$('#nav').nmcDropDown({trigger: 'click'});&lt;/pre&gt;
&lt;h4&gt;Available Options:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;trigger&lt;/strong&gt;: Event on which to show or hide the sub-menu, can be 'hover' or 'click'. (&lt;em&gt;'hover'&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;active_class&lt;/strong&gt;: Class to give open menu items, useful for styling. (&lt;em&gt;'open'&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;submenu_selector&lt;/strong&gt;: The element immediately below the top-level list-items containing the sub-menu. (&lt;em&gt;'ul'&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;show&lt;/strong&gt;: Effect(s) to use when showing the sub-menu. (&lt;em&gt;{opacity: 'show'}&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;show_speed&lt;/strong&gt;: Speed of the show transition. (&lt;em&gt;300&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;show_delay&lt;/strong&gt;: Delay before the sub-menu is shown (requires HoverIntent). (&lt;em&gt;50&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;hide&lt;/strong&gt;: Effect(s) to use when hiding the sub-menu. (&lt;em&gt;{opacity: 'hide'}&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;hide_speed&lt;/strong&gt;: Speed of the hide transition. (&lt;em&gt;200&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;hide_delay&lt;/strong&gt;: Delay before the sub-menu is hidden (requires HoverIntent). (&lt;em&gt;50&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fix_IE&lt;/strong&gt;: This will attempt to fix IE 6 and 7's problems with z-index, where the sub-menu appears behind the body of the page. Set to &lt;code&gt;false&lt;/code&gt; if it is interfering with your other styling. (&lt;em&gt;true&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Example&lt;/h3&gt;
&lt;p&gt;We recently launched a website for &lt;a href="http://www.ncbowd.com/"&gt;North Carolina Business Opportunity and Workforce Development&lt;/a&gt;, a program that helps women, minorities, and small businesses secure NCDOT contracts. I created drop-down menus that use the nmcDropDown plugin with custom show and hide animations for an interesting effect. The &lt;code&gt;show&lt;/code&gt; animation is &lt;code&gt;{opacity: 'show', top: '-=10px'}&lt;/code&gt; (&lt;code&gt;hide&lt;/code&gt; is exactly the reverse), which fades the menu in while sliding it up from below.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: I have also created and written up &lt;a href="http://www.newmediacampaigns.com/page/easy-menus-with-nmcdropdown"&gt;an example of how to use nmcDropDown&lt;/a&gt;, including HTML and CSS.&lt;/p&gt;
&lt;h2&gt;Download&lt;/h2&gt;
&lt;p&gt;&lt;a href="/files/media/nmcdropdown/jquery.nmcDropDown.js"&gt;Full, commented version (3.5kb)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/files/media/nmcdropdown/jquery.nmcDropDown.min.js"&gt;Minified version (1.3kb)&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=oh129ZoxjTw:Z97rYnH-Sb8:IuXuQPol3sE"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=oh129ZoxjTw:Z97rYnH-Sb8:IuXuQPol3sE" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=oh129ZoxjTw:Z97rYnH-Sb8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=oh129ZoxjTw:Z97rYnH-Sb8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=oh129ZoxjTw:Z97rYnH-Sb8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?i=oh129ZoxjTw:Z97rYnH-Sb8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/newmediacampaigns/development?a=oh129ZoxjTw:Z97rYnH-Sb8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/newmediacampaigns/development?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/newmediacampaigns/development/~4/oh129ZoxjTw" height="1" width="1"/&gt;</description>
		<guid isPermaLink="false">http://www.newmediacampaigns.com/page/nmcdropdown</guid>
		<pubDate>Wed, 14 Oct 2009 14:00:00 EDT</pubDate>
	<feedburner:origLink>http://www.newmediacampaigns.com/page/nmcdropdown</feedburner:origLink></item>
	</channel>
</rss>
