<?xml version="1.0" ?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
	<channel>
		<title>Zeroflux</title>
		<link>http://www.zeroflux.org/blog/</link>
		<description>Selected excerpts from a jaded technocrat</description>
		<dc:date>2008-07-24T01:07:36Z</dc:date>
		<copyright>Judd Vinet</copyright>
		<generator>Zeroflux</generator>
		<dc:language>en</dc:language>
		<item>
			<title>Life: Version 3.0</title>
			<link>http://www.zeroflux.org/post/view?id=242</link>
			<content:encoded xmlns="http://www.w3.org/1999/xhtml">&lt;p&gt;
I've been a little neglectful of my tiny corner of the web and its loyal readers, so I feel I should update you all with the next phase of my life.&amp;nbsp; 3.0, baby.
&lt;/p&gt;

&lt;p&gt;
The big news is that Rebecca and I are moving out to Halifax, Nova Scotia.&amp;nbsp; Wayyy out east.&amp;nbsp; We live in Victoria (BC) right now, so that's about a 6000 km move.
&lt;/p&gt;

&lt;p&gt;
The reason?&amp;nbsp; Bec got into Dalhousie University to start her fantabulous education/career in architecture.&amp;nbsp; We're both pretty excited about her potential there, as she's consistently staring at, evaluating, and remarking about architecture.&amp;nbsp; She's almost as fanatical as I am with computers.&amp;nbsp; Almost.
&lt;/p&gt;

&lt;p&gt;
But it's all been very bittersweet.&amp;nbsp; We have so many friends, colleagues, and well-wishers (in that they don't wish us any &lt;em&gt;specific&lt;/em&gt; harm) here, that it feels like we're throwing away our old lives.&amp;nbsp; We don't know anybody in Halifax, so it should be an interesting progression as we infiltrate a new city.
&lt;/p&gt;

&lt;p&gt;
To make things more interesting, we also bought a used VW Westfalia campervan to take us (and our more-prized possessions) out to Halifax.&amp;nbsp; Exciting, huh?
&lt;/p&gt;

&lt;p&gt;
We searched high and low for the right and found it in Vancouver.&amp;nbsp; It was pretty pricey, but most of the big mechanical stuff had already been done, so we felt it was a worthwhile investment.
&lt;/p&gt;

&lt;p&gt;
On our first test journey (short-haul) out of the city, the damn thing blows the transmission.&amp;nbsp; Ka-blammo.&amp;nbsp; Stuck in 3rd gear.&amp;nbsp; We're sitting on a major highway, in the middle of the lane.&amp;nbsp; We can't drive forward, we can't roll backward.&amp;nbsp; So we call a tow truck and sit on the side of the road for 45 minutes, hoping the Westy doesn't get hit by an 18-wheeler.
&lt;/p&gt;

&lt;p&gt;
Long story short, we ended up replacing the transmission, which, as it turns out, is quite expensive.&amp;nbsp; We took an extra week in Salmon Arm waiting for it to get fixed, which actually turned out to be the silver lining, as I got to spend some more time with my family.
&lt;/p&gt;

&lt;p&gt;
And now here I sit, on the floor of a mostly-empty apartment, staring at boxes and thinking about how I have to live with only &lt;em&gt;one&lt;/em&gt; active computer for the next month or so.&amp;nbsp; Talk about arduous.
&lt;/p&gt;

&lt;p&gt;
So wish us luck, dear readers.&amp;nbsp; We're sailing the asphalt seas in a 20-year-old hippie van, across the country to a city full of strangers.&amp;nbsp; Version 2.0 is over.&amp;nbsp; It's time for version 3.0. 
&lt;/p&gt;

&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
</content:encoded>
			<dc:date>2008-07-04T13:07:00Z</dc:date>
		</item>
		<item>
			<title>New Pronto Snapshot: 20080428</title>
			<link>http://www.zeroflux.org/post/view?id=241</link>
			<content:encoded xmlns="http://www.w3.org/1999/xhtml">&lt;p&gt;
I've been busy lately, but still steadily working on the ol' web
framework.&amp;nbsp; I'm starting to reorganize the core components to maintain
a loose coupling between layers.&amp;nbsp; Behold an incremental improvement!&amp;nbsp;
Huzzah!
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Changes:&lt;/strong&gt;
&lt;/p&gt;

&lt;ul&gt;
            
        
    &lt;li&gt;Change in nomenclature: &amp;quot;template plugins&amp;quot; are now also
            known as &amp;quot;helpers&amp;quot;, and &amp;quot;page plugins&amp;quot; are now also known as simply
            &amp;quot;plugins&amp;quot;&lt;/li&gt;
            
        
    &lt;li&gt;Added a datetime widget to the Form helper&lt;/li&gt;
            
        
    &lt;li&gt;Moved
            template logic into a separate class so it can be accessed outside of
            page controllers (eg, a commandline client can now use it to render
            email content)&lt;/li&gt;
            
        
    &lt;li&gt;CSS fixes for IE6 and IE7 (oh how I hate thee)&lt;/li&gt;
            
        
    &lt;li&gt;Changed Mailer plugin to use SwiftMailer instead of PHPMailer.  PHPMailer is still available via ppPHPMailer for the time being.&lt;/li&gt;
            
        
    &lt;li&gt;Upgraded TinyMCE to 3.0.7&lt;/li&gt;
            
        
    &lt;li&gt;Changes to display callback functions in &lt;span class=&quot;code&quot;&gt;tpGrid::build_grid()&lt;/span&gt;&lt;/li&gt;
            
        
    &lt;li&gt;More bugfixes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Now that input validation and templates are where they should be, I'd like to focus on URL mobility a bit more.&amp;nbsp; Mapping URLs to controller/action pairs is dead easy, but templates and controllers still hardcode URLs in many cases.&amp;nbsp; For example, if an action redirects to another controller/action after it has finished its business, then it hardcodes that URL.&amp;nbsp; Works most of the time, but if I want to move a controller to a new URL location (eg, move &lt;span class=&quot;code&quot;&gt;/blog/post/edit&lt;/span&gt; to &lt;span class=&quot;code&quot;&gt;/admin/blog/post/edit&lt;/span&gt;) then I have to actually go through the code and change all instances of that URL.
&lt;/p&gt;

&lt;p&gt;
My answer is going to be basically the reverse of the current URL-&amp;gt;Controller mapping that's currently found in &lt;span class=&quot;code&quot;&gt;app/config/urls.php&lt;/span&gt;.&amp;nbsp; So instead of rendering a hardcoded URL like &lt;span class=&quot;code&quot;&gt;/blog/post/edit&lt;/span&gt;, you would ask to render the controller &lt;span class=&quot;code&quot;&gt;Blog_Post&lt;/span&gt; and the action &lt;span class=&quot;code&quot;&gt;Edit&lt;/span&gt;.
&lt;/p&gt;

&lt;p&gt;
Sounds good in theory.&amp;nbsp; I'll let you know how it works out. 
&lt;/p&gt;
</content:encoded>
			<dc:date>2008-04-28T11:04:48Z</dc:date>
		</item>
		<item>
			<title>Pronto Lives</title>
			<link>http://www.zeroflux.org/post/view?id=240</link>
			<content:encoded xmlns="http://www.w3.org/1999/xhtml">&lt;p&gt;
In between my gazillion contracts and side projects, I've managed to eek out a little site for my latest toy, &lt;strong&gt;Pronto&lt;/strong&gt;.&amp;nbsp; Welcome to it!
&lt;/p&gt;

&lt;p&gt;
The site now lives at &lt;a href=&quot;http://www.prontoproject.com&quot;&gt;www.prontoproject.com&lt;/a&gt;.&amp;nbsp; There's not a ton of stuff there yet, but I will expand the site whenever I can find the time.&amp;nbsp; For now, you can poke at an SVN snapshot of Pronto and play with an example app, a dumb little knowledge base.&amp;nbsp; I like to learn by example, so I plan to release a few more mini apps that show Pronto in action. 
&lt;/p&gt;

&lt;p&gt;
In case you haven't been keeping up, Pronto is yet another PHP web framework.&amp;nbsp; You may roll your eyes and say &amp;quot;Oh God, &lt;em&gt;another&lt;/em&gt; web framework?&amp;quot; and you're probably right.&amp;nbsp; But I came from the world of linux distributions, and at last count, I think DistroWatch tracked about 550 of them.&amp;nbsp; So plenty of room left in the framework world!&amp;nbsp; ;)
&lt;/p&gt;

&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
And now, to round out this post a bit, some back story...
&lt;/p&gt;

&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
I started Pronto about 18 months ago.&amp;nbsp; I'd been doing solid web contract work for some time and was using &lt;a href=&quot;http://www.cakephp.org&quot;&gt;CakePHP&lt;/a&gt; as my primary weapon.&amp;nbsp; I'd also used &lt;a href=&quot;http://www.mojavi.org/&quot;&gt;Mojavi&lt;/a&gt; and &lt;a href=&quot;http://www.symfony-project.org/&quot;&gt;Symfony&lt;/a&gt; in the past, and flirted with &lt;a href=&quot;http://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; as well.&amp;nbsp; Django was pretty cool, but when you're building websites at varying levels (mom-n-pops right up to huge ecommerce retailers) you often have to use something more ubiquitous and understood.&amp;nbsp; mod_python (and certainly Ruby) still weren't widespread enough to make deployment trivial, so it was PHP.&amp;nbsp; In fact, in many cases it was still PHP4.
&lt;/p&gt;

&lt;p&gt;
Aside: It seems that the PHP world is caught in a weird cycle where we can't get out of PHP4, despite the long-standing availability of PHP5 and many obvious reasons to upgrade.&amp;nbsp; If you haven't seen it yet, check out &lt;a href=&quot;http://www.gophp5.org&quot;&gt;gophp5.org&lt;/a&gt;. They explain the problem quite well. 
&lt;/p&gt;

&lt;p&gt;
So I ended up using CakePHP for most projects, since it was fairly easy to use and it supported PHP4.&amp;nbsp; I didn't end up using it for long before I found &lt;a href=&quot;http://f.simplesideias.com.br/web.phps&quot;&gt;WebPHP&lt;/a&gt;, which was simply a PHP port of &lt;a href=&quot;http://webpy.org/&quot;&gt;WebPy&lt;/a&gt;, a popular, lightweight Python web framework (actually, more of a dispatcher).&amp;nbsp; I enjoyed the simplicity of it and started using it for some smaller stuff that didn't require Cake.
&lt;/p&gt;

&lt;p&gt;
In my classic itch-scratching fashion, I ended up adding pieces to my pseudo-framework in a get-the-job-done mentality.&amp;nbsp; But it started looking kind of elegant, and soon I was abandoning Cake in favour of my own, as-yet-unnamed framework.
&lt;/p&gt;

&lt;p&gt;
I found the code reusability was great, even better than what I had with Cake, and the template plugins gave me big gains in development time, saving me from the most tedious parts of web development: crafting the forms and tables/grids that are so commonly needed in any web app.
&lt;/p&gt;

&lt;p&gt;
And so it went.&amp;nbsp; I kept working on Pronto, intending it to be a personal tool/project.&amp;nbsp; But then it started to round out and look semi-professional, so I began to clean it up and document it.&amp;nbsp; The hopes being, of course, to garner a community around it so bright minds will help clean up and improve various facets.&amp;nbsp; I even tried to correct my mistakes of the past by writing a manual early on in the game.&amp;nbsp; Documentation is a classic weakness of mine.&amp;nbsp; To this day I still use Pronto's code is a primary reference point, but I'm trying to use the manual and API docs more and more, hoping that I will improve them more if I personally use them.&amp;nbsp; I'll let you know how that works out.
&lt;/p&gt;

&lt;p&gt;
In summary, welcome to Pronto.&amp;nbsp; You may hate it, you may like it.&amp;nbsp; You may not even care because you're not a web developer, in which case, I just stole 10 minutes of your life.&amp;nbsp; Hah! 
&lt;/p&gt;
</content:encoded>
			<dc:date>2008-03-26T09:03:21Z</dc:date>
		</item>
		<item>
			<title>SXSW 2008 and Updates</title>
			<link>http://www.zeroflux.org/post/view?id=239</link>
			<content:encoded xmlns="http://www.w3.org/1999/xhtml">&lt;p&gt;
Hey, long time no post.&amp;nbsp; There are typically two reasons why a blogger goes AWOL for a while:
&lt;/p&gt;

&lt;ol&gt;
        
    &lt;li&gt;They have nothing worthwhile to write about; or&lt;/li&gt;
        
    &lt;li&gt;They are in head-down, get-stuff-done mode&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
I'm leaning towards the latter, but there may be a hint of the former in there as well.
&lt;/p&gt;

&lt;p&gt;
I've been backing out of my contract work a bit lately and focusing on a new startup called &lt;a href=&quot;http://www.envirospeak.tv&quot;&gt;EnviroSpeak&lt;/a&gt;.&amp;nbsp; It's yet another social/video/blog style of website but with a focus on environmental problems and solutions.&amp;nbsp; We're shooting for a beta launch sometime this month, so stay tuned.
&lt;/p&gt;

&lt;p&gt;
I've also been working on getting Pronto documented so I can release it.&amp;nbsp; EnviroSpeak, among other sites, are already using it, so I believe it is now production-quality.&amp;nbsp; Stay tuned for updates!&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
And finally... 2008 will mark my first year as an attendee at &lt;a href=&quot;http://2008.sxsw.com/interactive/&quot;&gt;South by Southwest Interactive&lt;/a&gt;.&amp;nbsp; So if you're around, look for the lanky guy with the Vaio and say Hi.&amp;nbsp; I'll be hanging around the nerdy panels and the nuclear taco party.
&lt;/p&gt;

&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
</content:encoded>
			<dc:date>2008-03-05T15:03:14Z</dc:date>
		</item>
		<item>
			<title>PHP and UTF-8 Tips</title>
			<link>http://www.zeroflux.org/post/view?id=238</link>
			<content:encoded xmlns="http://www.w3.org/1999/xhtml">&lt;p&gt;
Many PHP-based web applications use ISO-8859-1 as the character set, as this is pretty much the default in North America and Western Europe, as I understand it.&amp;nbsp; The problem is that this doesn't work out so well down the road if you intend to internationalize your web application.&amp;nbsp; What if someone wants to write a blog comment in Greek, or drop a little Chinese in there?&amp;nbsp; You'll probably end up with a bunch of weird diamond-shaped blocks or odd-looking question marks, depending on your browser.
&lt;/p&gt;

&lt;p&gt;
Nowadays everybody is pretty much in agreement that UTF-8 seems to be the way to go.&amp;nbsp; It's backwards-compatible with 7-bit ASCII and it can handle all the other character encodings out there, so you don't have to juggle them all anymore.&amp;nbsp; So with the choice of ultimate charset out of the way, it's just a matter of convincing your RDBMS and scripting language to agree on the same charset.
&lt;/p&gt;

&lt;p&gt;
First, start at the data layer.&amp;nbsp; Ours is MySQL.
&lt;/p&gt;

&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;1. Table Definitions&lt;/strong&gt; 
&lt;/p&gt;

&lt;p&gt;
In MySQL, you can append the &lt;span class=&quot;code&quot;&gt;CHARACTER SET&lt;/span&gt; keyword to your &lt;span class=&quot;code&quot;&gt;CREATE TABLE&lt;/span&gt; statements.
&lt;/p&gt;

&lt;pre&gt;
DROP TABLE IF EXISTS `sessions`;
CREATE TABLE `sessions` (
    `id` CHAR(255) NOT NULL,
    `lastupdate` INT UNSIGNED NOT NULL,
    `data` BLOB,
    PRIMARY KEY(`id`),
    KEY(`lastupdate`)
) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
&lt;/pre&gt;

&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;2. VARCHAR Isn't Always Your Friend&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
I've been told that CHAR, VARCHAR, and TEXT can be a little misguided in their attempts to automagically convert encodings for you.&amp;nbsp; If you want to be absolutely sure that what you put into MySQL is what you get out of it, then it's a good idea to use BINARY instead of CHAR.
&lt;/p&gt;

&lt;p&gt;
Instead of CHAR, use BINARY&lt;br /&gt;
Instead of VARCHAR, use VARBINARY&lt;br /&gt;
Instead of TEXT, use BLOB
&lt;/p&gt;

&lt;p&gt;
There are downsides to using binary field definitions instead of text.&amp;nbsp; A big one for me is no collation, meaning you can't sort on those fields.&amp;nbsp; If you need this, I'd recommend you stay with VARCHAR et al.&amp;nbsp; I usually do as well, but I thought I'd mention this one since I've seen it used this way in other applications (eg, MediaWiki).&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
That pretty much does it for MySQL.&amp;nbsp; In PHP, there are a few tricks that can help you out.
&lt;/p&gt;

&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;3. Default Character Set&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
If you don't explicitly throw out a &lt;span class=&quot;code&quot;&gt;Content-Type&lt;/span&gt; header to the browser, PHP will send one for you.&amp;nbsp; And chances are, it's going to send one like this:
&lt;/p&gt;

&lt;pre&gt;
Content-Type: text/html; charset=ISO-8859-1
&lt;/pre&gt;

&lt;p&gt;
Instead of littering your code with &lt;span class=&quot;code&quot;&gt;header()&lt;/span&gt; calls wherever you deem necessary, you can use the &lt;span class=&quot;code&quot;&gt;default_charset&lt;/span&gt; setting to tell PHP you're a UTF-8 guy now.
&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
  // UTF-8, please!
  ini_set('default_charset', 'UTF-8');
?&amp;gt; 
&lt;/pre&gt;

&lt;p&gt;
Now if PHP sends out &lt;span class=&quot;code&quot;&gt;Content-Type&lt;/span&gt; headers on your behalf, it will use this character set.&amp;nbsp; It's also a good idea to throw a META tag in your HTML too:
&lt;/p&gt;

&lt;pre&gt;
&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=UTF-8&amp;quot; /&amp;gt;
&lt;/pre&gt;

&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;4. Multibyte String Functions&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
If you're working with UTF-8 strings, PHP's standard string functions won't do you any favours.&amp;nbsp; Try chucking a multibyte string into &lt;span class=&quot;code&quot;&gt;strlen()&lt;/span&gt; and what happens.&amp;nbsp; PHP expects single byte, so it will return an incorrect result.
&lt;/p&gt;

&lt;p&gt;
Fortunately, PHP's &lt;strong&gt;mbstring&lt;/strong&gt; extension provides another set of string functions that can handle multibyte strings.&amp;nbsp; They follow the same naming pattern as the standard string functions, except they're prefixed with &lt;span class=&quot;code&quot;&gt;mb_&lt;/span&gt;.&amp;nbsp; Eg, &lt;span class=&quot;code&quot;&gt;mb_strlen()&lt;/span&gt;.
&lt;/p&gt;

&lt;p&gt;
It's kind of a pain to search-and-replace all your string functions for their multibyte counterparts, but the PHP developers have realized this as well and accomodated.&amp;nbsp; If you have the mbstring extension, you can ask PHP to overload all the standard string functions with their mbstring counterparts.&amp;nbsp; This means you can call &lt;span class=&quot;code&quot;&gt;substr()&lt;/span&gt; as you normally would and PHP will actually call &lt;span class=&quot;code&quot;&gt;mb_substr()&lt;/span&gt; instead, saving you the search-and-replace work.
&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
  // Override standard string functions
  if(extension_loaded('mbstring')) {
    ini_set('mbstring.func_overload', 7);
  }
?&amp;gt;
&lt;/pre&gt;

&lt;p&gt;
For the reasoning behind the '7' in that call, see the &lt;a href=&quot;http://ca.php.net/mbstring&quot;&gt;PHP reference for mbstring&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
I'm sure there are some more conveniences that could be used to gracefully glide into the world of UTF-8.&amp;nbsp; Anyone have any?&amp;nbsp;
&lt;/p&gt;
</content:encoded>
			<dc:date>2007-12-07T16:12:38Z</dc:date>
		</item>
		<item>
			<title>Pronto: Templates and Plugins</title>
			<link>http://www.zeroflux.org/post/view?id=237</link>
			<content:encoded xmlns="http://www.w3.org/1999/xhtml">&lt;p&gt;
So in our past episodes, we covered the basics of page controllers and data models, so it's probably time to go over templates.
&lt;/p&gt;

&lt;p&gt;
Templates are pretty basic -- they don't vary much from framework to framework, except for the choice of template language: native PHP or a third-party markup such as Smarty?
&lt;/p&gt;

&lt;p&gt;
My choice is native PHP.&amp;nbsp; I see no reason to introduce another layer of abstraction when proper PHP is just as easy to read or write.&amp;nbsp; The plus side is you get to avoid the performance detriments (and extra caching layers used as workarounds) of parsing another type of markup.&amp;nbsp; If you look at something like Smarty, all it's really doing is parsing your template code for Smarty tags and converting them to native PHP.&amp;nbsp; Why not skip that step?
&lt;/p&gt;

&lt;p&gt;
This also gives us greater flexibiliy in the template, as we can now create PHP functions, call template plugins, and do some snazzy PHP stuff that probably isn't possible in a templating layer.
&lt;/p&gt;

&lt;p&gt;
Like most web frameworks, Pronto organizes templates into directories, usually named after the page controller that renders them.&amp;nbsp; Templates are usually rendered inside of &lt;strong&gt;layouts&lt;/strong&gt; (or &amp;quot;base templates&amp;quot;), which serve as a container for the repetitive content of a web app -- you know, all the html that's responsible for the general layout and structure of the web page, but not the content within.
&lt;/p&gt;

&lt;p&gt;
A quick layout example:
&lt;/p&gt;

&lt;pre&gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;title&amp;gt;My Web App&amp;lt;/title&amp;gt;
  &amp;lt;?php echo $html-&amp;gt;css('site') ?&amp;gt;
  &amp;lt;?php echo $html-&amp;gt;js('jquery') ?&amp;gt;
  &amp;lt;?php echo $HTML_HEAD ?&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;h1&amp;gt;My Site Header&amp;lt;/h1&amp;gt;
  &amp;lt;?php echo $CONTENT_FOR_LAYOUT ?&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;

&lt;p&gt;
This is pretty basic stuff.&amp;nbsp; You can see we're using a template plugin (&lt;code&gt;$html&lt;/code&gt;) in the head as a shortcut to render some CSS and script tags.&amp;nbsp; The special &lt;code&gt;$HTML_HEAD&lt;/code&gt; variable will be filled in by Pronto at render time, replaced with any HTML/JS/CSS code that may be required by various widgets.&amp;nbsp; We can talk more about template code scheduling later.&amp;nbsp; And finally, the &lt;code&gt;$CONTENT_FOR_LAYOUT&lt;/code&gt; variable is replaced by the actual content of the rendered template.&amp;nbsp; This variable only appears in template &lt;strong&gt;layouts&lt;/strong&gt;, not regular templates.
&lt;/p&gt;

&lt;p&gt;
So now that we have a layout, how do we render a normal template from a page controller?&amp;nbsp; Like this.
&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php
class pSandbox extends Page
{
  function __init__() {
    // set a common layout for this entire controller
    $this-&amp;gt;web-&amp;gt;base_template = 'layout.php';
  }
  function GET_helloworld() {
    $this-&amp;gt;tset('recipient', 'World');
    $this-&amp;gt;render('sandbox/hello.php');
  }
}
?&amp;gt;
&lt;/pre&gt;

&lt;p&gt;
We set a layout file in the &lt;code&gt;__init__()&lt;/code&gt; method, though we didn't really need to.&amp;nbsp; A site-wide default is set in the entry script (&lt;code&gt;index.php&lt;/code&gt;) so we only need to set it if we're using something other than the default &lt;code&gt;layout.php&lt;/code&gt;.&amp;nbsp; Then, in our action method &lt;code&gt;GET_helloworld()&lt;/code&gt;, we use the &lt;code&gt;Page::tset()&lt;/code&gt; method to set a template variable.&amp;nbsp; Template variables will be passed through to the template file when it is rendered.
&lt;/p&gt;

&lt;p&gt;
And here are the contents of our &lt;code&gt;templates/sandbox/hello.php&lt;/code&gt; file:
&lt;/p&gt;

&lt;pre&gt;
&amp;lt;p&amp;gt;Hello, &amp;lt;?php echo $recipient ?&amp;gt;!&amp;lt;/p&amp;gt;
&lt;/pre&gt;

&lt;p&gt;
As you can see, we access our template variables just as we would any other global variable.&amp;nbsp; You're free to use any other PHP code you like, but you cannot (well, &lt;em&gt;should&lt;/em&gt; not) access variables from the dispatcher, models, or page controllers.&amp;nbsp; Templates should use template variables (and plugins) only, nothing else.
&lt;/p&gt;

&lt;p&gt;
Now, let's do something more interesting by taking advantage of the template plugins.&amp;nbsp; There are currently four template plugins that come with Pronto, but you can add as many as you want.&amp;nbsp; The four base ones are &lt;strong&gt;html&lt;/strong&gt;, &lt;strong&gt;form&lt;/strong&gt;, &lt;strong&gt;table&lt;/strong&gt;, and &lt;strong&gt;ajax&lt;/strong&gt;.&amp;nbsp; Their uses are fairly self-explanatory.
&lt;/p&gt;

&lt;p&gt;
&lt;table border=&quot;0&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot; width=&quot;80%&quot; class=&quot;content&quot; align=&quot;center&quot;&gt;
                                                            
                                                        
                                                    
                                                
                                            
                                        
                                    
                                
                            
                        
                    
                
            
        
    &lt;tbody&gt;
                                                                                                                        
                                                                                                                
                                                                                                        
                                                                                                
                                                                                        
                                                                                
                                                                        
                                                                
                                                        
                                                
                                        
                                
                        
                
        &lt;tr&gt;
                                                                                                                                                                                    &lt;th&gt;html&lt;/th&gt;
                                                                                                                                                                                    
                                                                                                                                                                        
                                                                                                                                                            
                                                                                                                                                
                                                                                                                                    
                                                                                                                        
                                                                                                            
                                                                                                
                                                                                    
                                                                        
                                                            
                                                
                                    
                        
            &lt;td&gt;Generate basic HTML elements -- images, links, javascript, css, urls&lt;/td&gt;
                                                                                                                                                                                
                                                                                                                                                                    
                                                                                                                                                        
                                                                                                                                            
                                                                                                                                
                                                                                                                    
                                                                                                        
                                                                                            
                                                                                
                                                                    
                                                        
                                            
                                
                    
        &lt;/tr&gt;
                                                                                                                        
                                                                                                                
                                                                                                        
                                                                                                
                                                                                        
                                                                                
                                                                        
                                                                
                                                        
                                                
                                        
                                
                        
                
        &lt;tr&gt;
                                                                                                                                                                                    &lt;th&gt;form&lt;/th&gt;
                                                                                                                                                                                    
                                                                                                                                                                        
                                                                                                                                                            
                                                                                                                                                
                                                                                                                                    
                                                                                                                        
                                                                                                            
                                                                                                
                                                                                    
                                                                        
                                                            
                                                
                                    
                        
            &lt;td&gt;Generate form elements and entire forms&lt;/td&gt;
                                                                                                                                                                                
                                                                                                                                                                    
                                                                                                                                                        
                                                                                                                                            
                                                                                                                                
                                                                                                                    
                                                                                                        
                                                                                            
                                                                                
                                                                    
                                                        
                                            
                                
                    
        &lt;/tr&gt;
                                                                                                                        
                                                                                                                
                                                                                                        
                                                                                                
                                                                                        
                                                                                
                                                                        
                                                                
                                                        
                                                
                                        
                                
                        
                
        &lt;tr&gt;
                                                                                                                                                                                    &lt;th&gt;table&lt;/th&gt;
                                                                                                                                                                                    
                                                                                                                                                                        
                                                                                                                                                            
                                                                                                                                                
                                                                                                                                    
                                                                                                                        
                                                                                                            
                                                                                                
                                                                                    
                                                                        
                                                            
                                                
                                    
                        
            &lt;td&gt;Generate basic tables and more advanced record grids&lt;/td&gt;
                                                                                                                                                                                
                                                                                                                                                                    
                                                                                                                                                        
                                                                                                                                            
                                                                                                                                
                                                                                                                    
                                                                                                        
                                                                                            
                                                                                
                                                                    
                                                        
                                            
                                
                    
        &lt;/tr&gt;
                                                                                                                        
                                                                                                                
                                                                                                        
                                                                                                
                                                                                        
                                                                                
                                                                        
                                                                
                                                        
                                                
                                        
                                
                        
                
        &lt;tr&gt;
                                                                                                                                                                                    &lt;th&gt;ajax&lt;/th&gt;
                                                                                                                                                                                    
                                                                                                                                                                        
                                                                                                                                                            
                                                                                                                                                
                                                                                                                                    
                                                                                                                        
                                                                                                            
                                                                                                
                                                                                    
                                                                        
                                                            
                                                
                                    
                        
            &lt;td&gt;Generate AJAX widgets such as dialogs (this module is still in a developmental state)&lt;/td&gt;
                                                                                                                                                                                
                                                                                                                                                                    
                                                                                                                                                        
                                                                                                                                            
                                                                                                                                
                                                                                                                    
                                                                                                        
                                                                                            
                                                                                
                                                                    
                                                        
                                            
                                
                    
        &lt;/tr&gt;
                                                                                                                    
                                                                                                            
                                                                                                    
                                                                                            
                                                                                    
                                                                            
                                                                    
                                                            
                                                    
                                            
                                    
                            
                    
            
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;
&amp;nbsp;Let's take the old &lt;a href=&quot;../pronto_demo/person/create&quot;&gt;Create Person&lt;/a&gt; form and spruce it up a bit.&amp;nbsp; The new template is below, and &lt;a href=&quot;../pronto_demo/person/create/new&quot;&gt;here's what it looks like&lt;/a&gt;.
&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php if(isset($data['id'])): ?&amp;gt;
  &amp;lt;h2&amp;gt;Edit Person&amp;lt;/h2&amp;gt;
&amp;lt;?php else: ?&amp;gt;
  &amp;lt;h2&amp;gt;Create a New Person&amp;lt;/h2&amp;gt;
&amp;lt;?php endif ?&amp;gt;
&amp;lt;?php
$f = array(
  'action'  =&amp;gt; $html-&amp;gt;url(CURRENT_URL),
  'submit'  =&amp;gt; array('Create Person','Update Person'),
  'data_id' =&amp;gt; $data['id'],
  'options' =&amp;gt; array('numcols'=&amp;gt;2),
  'layout'  =&amp;gt; array(
    'col1' =&amp;gt; array('colspan'=&amp;gt;1),
    'col2' =&amp;gt; array('colspan'=&amp;gt;1),
    'col3' =&amp;gt; array('colspan'=&amp;gt;2),
  ),
  'elements' =&amp;gt; array(
    'col1' =&amp;gt; array(
      'name'        =&amp;gt; array('prompt'=&amp;gt;'Name:', 'type'=&amp;gt;'text'),
      'address'     =&amp;gt; array('prompt'=&amp;gt;'Address:', 'type'=&amp;gt;'text'),
      'city'        =&amp;gt; array('prompt'=&amp;gt;'City:', 'type'=&amp;gt;'text'),
      'country'     =&amp;gt; array('prompt'=&amp;gt;'Country:', 'type'=&amp;gt;'text'),
      'signup_date' =&amp;gt; array('prompt'=&amp;gt;'Signup Date:', 'type'=&amp;gt;'date','help'=&amp;gt;'This is a help popup')
    ),
    'col2' =&amp;gt; array(
      'fav_color' =&amp;gt; array('prompt'=&amp;gt;'Favourite Colour:', 'type'=&amp;gt;'color', 'help'=&amp;gt;'An example of a colour selection widget'),
      'birthdate' =&amp;gt; array('prompt'=&amp;gt;'Birthdate:', 'type'=&amp;gt;'date'),
      'fav_foods' =&amp;gt; array('prompt'=&amp;gt;'Favourite Foods:', 'type'=&amp;gt;'multiselect', 'options'=&amp;gt;array_hash(array('Pizza','Sushi','Lasagna','Lobster','Hamburgers','Caviar'))),
      'other'     =&amp;gt; array('prompt'=&amp;gt;'Other:', 'type'=&amp;gt;'text'),
    ),
    'col3' =&amp;gt; array(
      'about'     =&amp;gt; array('prompt'=&amp;gt;'About:', 'type'=&amp;gt;'htmlarea', 'attribs'=&amp;gt;array('style'=&amp;gt;'width:800px;height:300px')),
    )
  )
);
echo $form-&amp;gt;build_form($f, $data, $errors);
?&amp;gt;&amp;nbsp;
&lt;/pre&gt;

&lt;p&gt;
As you can see, the &lt;span class=&quot;code&quot;&gt;$form-&amp;gt;build_form()&lt;/span&gt; function can generate some decent-looking forms without much more than a basic form definition.&amp;nbsp; It supports tooltip-style context help, various widget types (both simple and complex) and it can do rudimentary multi-column, tableless layouts.
&lt;/p&gt;

&lt;p&gt;
Now let's make one more change.&amp;nbsp; This time, we'll split the form up into separate tabs.&amp;nbsp; &lt;a href=&quot;../pronto_demo/person/create/tabs&quot;&gt;Result is here&lt;/a&gt;.
&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php if(isset($data['id'])): ?&amp;gt;
  &amp;lt;h2&amp;gt;Edit Person&amp;lt;/h2&amp;gt;
&amp;lt;?php else: ?&amp;gt;
  &amp;lt;h2&amp;gt;Create a New Person&amp;lt;/h2&amp;gt;
&amp;lt;?php endif ?&amp;gt;
&amp;lt;?php
$f1 = array(
  'data_id' =&amp;gt; $data['id'],
  'options' =&amp;gt; array('noopen'=&amp;gt;true,'noclose'=&amp;gt;true,'numcols'=&amp;gt;2),
  'elements' =&amp;gt; array(
    'col1' =&amp;gt; array(
      'name'        =&amp;gt; array('prompt'=&amp;gt;'Name:', 'type'=&amp;gt;'text'),
      'address'     =&amp;gt; array('prompt'=&amp;gt;'Address:', 'type'=&amp;gt;'text'),
      'city'        =&amp;gt; array('prompt'=&amp;gt;'City:', 'type'=&amp;gt;'text'),
      'country'     =&amp;gt; array('prompt'=&amp;gt;'Country:', 'type'=&amp;gt;'text'),
      'signup_date' =&amp;gt; array('prompt'=&amp;gt;'Signup Date:', 'type'=&amp;gt;'date','help'=&amp;gt;'This is a help popup')
    ),
    'col2' =&amp;gt; array(
      'fav_color' =&amp;gt; array('prompt'=&amp;gt;'Favourite Colour:', 'type'=&amp;gt;'color', 'help'=&amp;gt;'An example of a colour selection widget'),
      'birthdate' =&amp;gt; array('prompt'=&amp;gt;'Birthdate:', 'type'=&amp;gt;'date'),
      'fav_foods' =&amp;gt; array('prompt'=&amp;gt;'Favourite Foods:', 'type'=&amp;gt;'multiselect', 'options'=&amp;gt;array_hash(array('Pizza','Sushi','Lasagna','Lobster','Hamburgers','Caviar'))),
      'other'     =&amp;gt; array('prompt'=&amp;gt;'Other', 'type'=&amp;gt;'text'),
  ))
);
$f2 = array(
  'data_id' =&amp;gt; $data['id'],
  'options' =&amp;gt; array('noopen'=&amp;gt;true,'noclose'=&amp;gt;true),
  'elements' =&amp;gt; array(
    'about' =&amp;gt; array('prompt'=&amp;gt;'About:', 'type'=&amp;gt;'htmlarea', 'attribs'=&amp;gt;array('style'=&amp;gt;'width:800px;height:300px')),
  )
);
?&amp;gt;
&amp;lt;form method=&amp;quot;post&amp;quot; action=&amp;quot;&amp;lt;?php echo $html-&amp;gt;url(CURRENT_URL) ?&amp;gt;&amp;quot;&amp;gt;
&amp;lt;div id=&amp;quot;form_container&amp;quot;&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#tab1&amp;quot;&amp;gt;Basic Info&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#tab2&amp;quot;&amp;gt;Other Stuff&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
  &amp;lt;div id=&amp;quot;tab1&amp;quot; style=&amp;quot;background:#fff&amp;quot; class=&amp;quot;form&amp;quot;&amp;gt;
    &amp;lt;?php echo $form-&amp;gt;build_form($f1, $data, $errors) ?&amp;gt;
    &amp;lt;br style=&amp;quot;clear:both&amp;quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div id=&amp;quot;tab2&amp;quot; style=&amp;quot;background:#fff&amp;quot; class=&amp;quot;form&amp;quot;&amp;gt;
    &amp;lt;?php echo $form-&amp;gt;build_form($f2, $data, $errors) ?&amp;gt;
    &amp;lt;br style=&amp;quot;clear:both&amp;quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;br /&amp;gt;
&amp;lt;?php echo $form-&amp;gt;submit('submit', 'Update Person', array('style'=&amp;gt;'float:right')) ?&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;?php $html-&amp;gt;js_run('jq_tabs', &amp;quot;$('#form_container').tabs();&amp;quot;) ?&amp;gt;
&lt;/pre&gt;

&lt;p&gt;
This one is a little longer due to the tab setup, but it's pretty self-explanatory.&amp;nbsp; You can see we're now building two forms but they exist under the same &lt;span class=&quot;code&quot;&gt;&amp;lt;form&amp;gt;&lt;/span&gt; tag since we asked &lt;span class=&quot;code&quot;&gt;build_form&lt;/span&gt; not to &amp;quot;open&amp;quot; or &amp;quot;close&amp;quot; the subforms.&amp;nbsp; And at the very bottom, you see how we can queue some JavaScript to be run once the DOM is loaded.&amp;nbsp; It's important to use Pronto's JS queueing system instead of just throwing out the necessary JS in a &lt;span class=&quot;code&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;.&amp;nbsp; The reason is that Pronto can make sure the JS code is run properly if this template is returned as an AJAX response.
&lt;/p&gt;

&lt;p&gt;
You'll also notice that the line of JavaScript we're running is using &lt;a href=&quot;http://www.jquery.com&quot;&gt;jQuery&lt;/a&gt;.&amp;nbsp; I heart jQuery, so Pronto does too.&amp;nbsp;
&lt;/p&gt;

&lt;p&gt;
Now let's make the list/edit process a little more fluid.&amp;nbsp; We'll take another look at the grid template we use to list all records, but this time we'll add an another edit action that uses AJAX instead of a normal request.
&lt;/p&gt;

&lt;p&gt;
No changes are required to our new tab-based create/edit template, but we have to make a few alterations to the grid template.&amp;nbsp; It's shown below.
&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php $form-&amp;gt;htmlarea_preload() ?&amp;gt;
&amp;lt;h1&amp;gt;People&amp;lt;/h1&amp;gt;
&amp;lt;?php echo $html-&amp;gt;link_button('New Person', '/person/create/tabs', 'icons/add.gif', '', false, array('style'=&amp;gt;'float:right')) ?&amp;gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;
&amp;lt;?php
echo $table-&amp;gt;build_grid(array(
  'url'      =&amp;gt; $html-&amp;gt;url(CURRENT_URL),
  'rowclick' =&amp;gt; $html-&amp;gt;url('/person/edit/tabs?id=_ID_'),
  'options'  =&amp;gt; array('ajax'=&amp;gt;true),
  'columns'  =&amp;gt; array(
    '_OPTIONS_' =&amp;gt; array(
      'edit'   =&amp;gt; $html-&amp;gt;link($html-&amp;gt;image('icons/edit.gif', array('title'=&amp;gt;'Edit Person','class'=&amp;gt;'ajax_action')), '/person/edit/tabs?id=_ID_'),
      'delete' =&amp;gt; $html-&amp;gt;link($html-&amp;gt;image('icons/delete.gif', array('title'=&amp;gt;'Delete Person')), '/person/delete?id=_ID_', 'Are you sure?')),
    'name'        =&amp;gt; array('label'=&amp;gt;'Name','type'=&amp;gt;'text'),
    'address'     =&amp;gt; array('label'=&amp;gt;'Address','type'=&amp;gt;'text'),
    'city'        =&amp;gt; array('label'=&amp;gt;'City','type'=&amp;gt;'text'),
    'country'     =&amp;gt; array('label'=&amp;gt;'Country','type'=&amp;gt;'text'),
    'signup_date' =&amp;gt; array('label'=&amp;gt;'Signup Date','type'=&amp;gt;'date'),
  ),
  'id'      =&amp;gt; 'id',
  'data'    =&amp;gt; $data,
  'perpage' =&amp;gt; $perpage,
  'curpage' =&amp;gt; $curpage,
  'rows'    =&amp;gt; $totalrows
));
?&amp;gt;
&lt;/pre&gt;

&lt;p&gt;
And &lt;a href=&quot;../pronto_demo/person/list/ajax&quot;&gt;here are the results&lt;/a&gt;. Click the &amp;quot;edit&amp;quot; icon beside any record and you should see the form popup below the record you selected.&amp;nbsp; Kinda cool, huh?&amp;nbsp; If you are unfortunate enough to be using IE, you'll probably get a JS error when trying to load that last form via AJAX.&amp;nbsp; This is a bug in the &lt;span class=&quot;code&quot;&gt;htmlarea&lt;/span&gt; widget that I haven't fixed yet.&amp;nbsp; Find yourself a &lt;a href=&quot;http://www.mozilla.com/en-US/firefox/&quot;&gt;better&lt;/a&gt; &lt;a href=&quot;http://www.opera.com/&quot;&gt;browser&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
I think that's enough PHP code for one post.&amp;nbsp; As you can see, Pronto tries hard to eliminate any mundane or repetitive HTML in the template layer.&amp;nbsp; It can't do everything and often there's no better solution than to sit down and hack out the HTML yourself, but for rapid application development, these template plugins are a big time-saver. 
&lt;/p&gt;

&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
</content:encoded>
			<dc:date>2007-11-07T10:11:04Z</dc:date>
		</item>
		<item>
			<title>String Theory is Cooler than You</title>
			<link>http://www.zeroflux.org/post/view?id=236</link>
			<content:encoded xmlns="http://www.w3.org/1999/xhtml">&lt;p&gt;If you're into theoretical physics (at the armchair level) and string theory, then this 12-minute video is a must.&amp;nbsp; Thanks Hapy!&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;div align=&quot;center&quot;&gt;&lt;embed width=&quot;480&quot; height=&quot;392&quot; wmode=&quot;transparent&quot; flashvars=&quot;width=480&amp;amp;height=392&amp;amp;mediaId=99898&amp;amp;affiliateId=33530&amp;amp;javascriptContext=true&amp;amp;skinURL=http://flash.revver.com/player/1.0/skins/Default_Raster.swf&amp;amp;skinImgURL=http://flash.revver.com/player/1.0/skins/night_skin.png&amp;amp;actionBarSkinURL=http://flash.revver.com/player/1.0/skins/DefaultNavBarSkin.swf&amp;amp;resizeVideo=True&amp;amp;pngLogo=http://tenthdimension.com/assets/tenth-icon.png&quot; bgcolor=&quot;#ffffff&quot; salign=&quot;TL&quot; scale=&quot;noScale&quot; pluginspage=&quot;http://www.macromedia.com/go/getflashplayer&quot; src=&quot;http://flash.revver.com/player/1.0/player.swf&quot; type=&quot;application/x-shockwave-flash&quot;&gt;&lt;/embed&gt;&lt;/div&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;To see some more short videos explaining string theory, check out &lt;a href=&quot;http://discovermagazine.com/twominutesorless&quot;&gt;the winners of the string theory contest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;
</content:encoded>
			<dc:date>2007-10-26T13:10:35Z</dc:date>
		</item>
		<item>
			<title>A real-world example: TipCache</title>
			<link>http://www.zeroflux.org/post/view?id=234</link>
			<content:encoded xmlns="http://www.w3.org/1999/xhtml">&lt;p&gt;Here's an example of Pronto in real-world use.&amp;nbsp; Though most uses of Pronto at present are for client websites, &lt;a href=&quot;http://www.tipcache.com&quot;&gt;TipCache&lt;/a&gt; was a personal site of mine that I whipped up one afternoon last Spring.&amp;nbsp;&lt;/p&gt;

&lt;p&gt;It's about the quickest site one could write while still calling it &amp;quot;dynamic&amp;quot; -- it has two basic entities, users and tips.&amp;nbsp; The whole function of the site is to provide an easily-searchable database of quick, one-off tips and howtos related to IT.&lt;/p&gt;

&lt;p&gt;The reason I wrote it was due to the amount of time I was spending looking for the more esoteric answers to some sysadmin-related issues I was having.&amp;nbsp; Google is a programmer's best friend in most cases, but there are often times when one has a problem that is just too edge-case to show up in the first few pages of a Google search.&amp;nbsp; I end up poring through mailing list archives and dead forums, just trying to get a sniff in the right direction.&amp;nbsp; And many of the results take me to a site like experts-exchange, which wants me to signup and &lt;em&gt;pay&lt;/em&gt; just to see the answer!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.tipcache.com&quot;&gt;TipCache&lt;/a&gt; is my answer.&amp;nbsp; Its purpose is to hold tips for others to find.&amp;nbsp; No catches, no forced registrations, easily searchable through standard tagging.&amp;nbsp; If the site proves useful to others, I plan to add a little more to it -- feedback/reputation ratings for tips/contributors, comments/corrections, etc.&lt;/p&gt;

&lt;p&gt;If you have any technical tips/howtos that others may benefit from, please do add them to the site.&amp;nbsp; &lt;a href=&quot;http://www.tipcache.com&quot;&gt;TipCache&lt;/a&gt; uses the well-known &lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt; syntax for clean, non-HTML markup and its a cinch to add new tips.&lt;/p&gt;
</content:encoded>
			<dc:date>2007-10-24T14:10:54Z</dc:date>
		</item>
		<item>
			<title>Pronto: Data Models</title>
			<link>http://www.zeroflux.org/post/view?id=233</link>
			<content:encoded xmlns="http://www.w3.org/1999/xhtml">&lt;p&gt;Since Ruby on Rails first came out, many frameworks have adopted its ActiveRecord data modeling system.&amp;nbsp; I've never used Rails, but I did spend some time with CakePHP, which has a similar &lt;acronym title=&quot;Object-relational Mapping&quot;&gt;ORM&lt;/acronym&gt; scheme.&lt;/p&gt;

&lt;p&gt;I realize the potential power of such a model, but its never really done me any favours.&amp;nbsp; And since it's my party, I've left out any complicated ORM stuff from Pronto.&amp;nbsp; We have data models, but they are relatively dumb compared to some of the ORM stuff I've seen in other frameworks.&lt;/p&gt;

&lt;p&gt;Why?&amp;nbsp; Personally, I believe typical SQL isn't very hard to write, so I craft it manually for each and every data model I have in a project.&amp;nbsp; I find this just as easy (and sometimes easier) than trying to define all the inter-entity relations in some weird format.&amp;nbsp; Writing a SQL join is usually fast and easy, and if I lay out my data models correctly, I only have to do it once per relation, then I just call the proper methods in my data models and I have the same functionality as I would with a more dynamic ORM approach.&lt;/p&gt;

&lt;p&gt;Unless you have a compelling reason not to, each entity in your project should have a corresponding data model.&amp;nbsp; These models are imported by page controllers and used by them to manipulate the data.&amp;nbsp; So here's the plot summary so far:&amp;nbsp; Dispatcher receives a request, passes control to a page controller; the page controller imports data models and manipulates data through them, then renders a template which is sent back to the browser.&amp;nbsp; A pretty standard MVC dance.&lt;/p&gt;

&lt;p&gt;Here's a basic vanilla data model, as generated by Pronto:&lt;/p&gt;

&lt;pre&gt;&amp;lt;?php&lt;br /&gt;&lt;br /&gt;class mPerson extends Model&lt;br /&gt;{&lt;br /&gt;  var $default_sort = 'name ASC';&lt;br /&gt;  var $table        = 'people';&lt;br /&gt;&lt;br /&gt;  function validate_for_insert($data)&lt;br /&gt;  {&lt;br /&gt;    $errors = array();&lt;br /&gt;    $this-&amp;gt;page-&amp;gt;required($errors, array('name','address','city','country'));&lt;br /&gt;    $this-&amp;gt;page-&amp;gt;validate($errors, 'email', VALID_EMAIL, 'Valid email address is required');&lt;br /&gt;    return $errors;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  function validate_for_update($data)&lt;br /&gt;  {&lt;br /&gt;    $errors = array();&lt;br /&gt;    $this-&amp;gt;page-&amp;gt;required($errors, array('name','address','city','country'));&lt;br /&gt;    $this-&amp;gt;page-&amp;gt;validate($errors, 'email', VALID_EMAIL, 'Valid email address is required');&lt;br /&gt;    return $errors;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  function insert($data)&lt;br /&gt;  {&lt;br /&gt;    return parent::insert($data);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  function update($data)&lt;br /&gt;  {&lt;br /&gt;    return parent::update($data);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  function delete($id)&lt;br /&gt;  {&lt;br /&gt;    parent::delete($id);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  function list_data()&lt;br /&gt;  {&lt;br /&gt;    $from   = $this-&amp;gt;table;&lt;br /&gt;    $exprs  = array();&lt;br /&gt;    $select = '*';&lt;br /&gt;    foreach($exprs as $k=&amp;gt;$v) {&lt;br /&gt;      $select .= &amp;quot;,$v $k&amp;quot;;&lt;br /&gt;    }&lt;br /&gt;    return array($exprs, $select, $from);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;?&amp;gt;&lt;/pre&gt;

&lt;p&gt;Pretty boring.&amp;nbsp; This file was generated by Pronto, so most of the content is just placeholder stuff to be extended from the parent class, &lt;code&gt;Model&lt;/code&gt;.&amp;nbsp; You can see some basic data validation stuff, which uses &lt;code&gt;Page::required()&lt;/code&gt; and &lt;code&gt;Page::validate()&lt;/code&gt; to ensure incoming form data isn't erroneous.&amp;nbsp; You can also see some basic methods for data manipulation: insert, update, get, and delete.&amp;nbsp; Any data model should have these methods, especially if you're using the &lt;code&gt;Page_CRUD&lt;/code&gt; class to manipulate the data, as it will expect these methods to be functional.&lt;/p&gt;

&lt;p&gt;If a model needs to join with other entities/tables or provide some special access methods, then the model class is where this logic will go.&amp;nbsp; As an example, let's assume we always need to see all address records associated with a person.&amp;nbsp; Might as well change the &lt;code&gt;get()&lt;/code&gt; method to pull these in.&lt;/p&gt;

&lt;pre&gt;&amp;lt;?php&lt;br /&gt;  function get($id) {&lt;br /&gt;    $person = parent::get($id);&lt;br /&gt;    $person['addresses'] = $this-&amp;gt;db-&amp;gt;GetAll(&amp;quot;SELECT * FROM addresses WHERE person_id=%i&amp;quot;, array($person['id']));&lt;br /&gt;    return $person;&lt;br /&gt;  }&lt;br /&gt;?&amp;gt;&lt;/pre&gt;

&lt;p&gt;Not terribly difficult.&amp;nbsp; And how do we access a model from a page controller?&amp;nbsp; Like this:&lt;/p&gt;

&lt;pre&gt;&amp;lt;?php&lt;br /&gt;class pPerson extends Page&lt;br /&gt;{&lt;br /&gt;  function __init__()&lt;br /&gt;  {&lt;br /&gt;    $this-&amp;gt;import_model('person');&lt;br /&gt;  }&lt;br /&gt;  function GET_view()&lt;br /&gt;  {&lt;br /&gt;    $person = $this-&amp;gt;models-&amp;gt;person-&amp;gt;get($this-&amp;gt;param('id'));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;?&amp;gt;&lt;/pre&gt;

&lt;p&gt;Now if we're following the rule to always have a distiinct model for each entity, then we should have a data model for addresses too.&amp;nbsp; Models can depend on other models, so let's rewrite our &lt;code&gt;mPerson::get()&lt;/code&gt; method to use the address model to load in addresses, instead of grabbing them from a foreign table in &lt;code&gt;mPerson&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&amp;lt;?php&lt;br /&gt;  function get($id) {&lt;br /&gt;    $person = parent::get($id);&lt;br /&gt;    $this-&amp;gt;depend('address');&lt;br /&gt;    $person['addresses'] = $this-&amp;gt;depends-&amp;gt;address-&amp;gt;get_all_by('person_id', $person['id'])&lt;br /&gt;    return $person;&lt;br /&gt;  }&lt;br /&gt;?&amp;gt;&lt;/pre&gt;

&lt;p&gt;There are a number of methods in the base &lt;code&gt;Model&lt;/code&gt; class that will take care of any mundane SQL, leaving you, the developer, the task of writing the more interesting ones.&lt;/p&gt;

&lt;p&gt;Now let's look at that weird &lt;code&gt;list_data()&lt;/code&gt; method in &lt;code&gt;mPerson&lt;/code&gt;.&amp;nbsp; This method's purpose is to return any and all columns that will be used when creating a table/grid/list of records for this entity.&amp;nbsp; As you can see above, it returns three items in an array: a list of expressions, a SELECT clause, and a FROM clause.&amp;nbsp; All three of these items are destined to be passed straight through to the database layer, so they should be in well-formed SQL.&amp;nbsp; Once again, the reason for this method is better illustrated with a good example.&lt;/p&gt;

&lt;pre&gt;&amp;lt;?php&lt;br /&gt;  function list_data()&lt;br /&gt;  {&lt;br /&gt;    $from = &amp;quot;people p INNER JOIN addresses a ON (a.person_id=p.id AND a.is_primary)&amp;quot;;&lt;br /&gt;    $exprs = array(&lt;br /&gt;      'full_name'       =&amp;gt; 'CONCAT(p.first_name,&amp;quot; &amp;quot;,p.last_name)',&lt;br /&gt;      'primary_address' =&amp;gt; 'CONCAT_WS(&amp;quot;,&amp;quot;,a.street,a.city,a.state)'&lt;br /&gt;    );&lt;br /&gt;    $select = &amp;quot;p.*&amp;quot;;&lt;br /&gt;    foreach($exprs as $k=&amp;gt;$v) $select .= &amp;quot;,$v $k&amp;quot;;&lt;br /&gt;    return array($exprs, $select, $from);&lt;br /&gt;  }&lt;br /&gt;?&amp;gt;&lt;/pre&gt;

&lt;p&gt;Now any interface that wants to create a list of records for this entity can ask the model for the proper list data before it queries the database.&amp;nbsp; Currently the only class that does this is &lt;code&gt;Page_CRUD&lt;/code&gt;, but custom classes are free to use this function if it makes sense.&lt;/p&gt;

&lt;p&gt;By now you've also seen a quick usage of the database class, referenced as &lt;code&gt;$this-&amp;gt;db&lt;/code&gt; in &lt;code&gt;mPerson&lt;/code&gt;.&amp;nbsp; This is the class that actually talks to the database server, whatever it may be.&amp;nbsp; It uses SafeSQL to protect against injection attacks and it's fairly robust for basic DB work.&amp;nbsp; If you're a stringent MVC follower, then you'll probably only need to use &lt;code&gt;$this-&amp;gt;db&lt;/code&gt; in data models, but it is available to most objects throughout Pronto.&lt;/p&gt;

&lt;p&gt;And so concludes a basic introduction to the lightweight data model.&amp;nbsp; While it doesn't do everything under the sun, it is lean and mean and makes sure your page controllers aren't littered with SQL or redundant data-manipulation code.&lt;/p&gt;
</content:encoded>
			<dc:date>2007-10-23T09:10:34Z</dc:date>
		</item>
		<item>
			<title>Facelift</title>
			<link>http://www.zeroflux.org/post/view?id=231</link>
			<content:encoded xmlns="http://www.w3.org/1999/xhtml">&lt;p&gt;Website designs tend to get a bit stale, especially if you stare at them a lot.&amp;nbsp; Here is this year's incarnation of Zeroflux.&lt;/p&gt;

&lt;p&gt;I've tested it in Firefox, Opera, and Safari and it looks good in all three.&amp;nbsp; If you're using IE and it doesn't look good, then switch to another browser.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href=&quot;http://www.styleshout.com&quot;&gt;Styleshout&lt;/a&gt; for the base design.&lt;/p&gt;
</content:encoded>
			<dc:date>2007-10-19T13:10:18Z</dc:date>
		</item>
	</channel>
</rss>
