<?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:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Jotlab</title>
	
	<link>http://www.jotlab.com</link>
	<description>Jotlab</description>
	<lastBuildDate>Tue, 24 Aug 2010 00:37:19 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/Jotlab" /><feedburner:info uri="jotlab" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Protecting Files Against Hotlinking with PHP, Lighttpd, Apache &amp; X-Sendfile</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/suwBqJ2cHEg/</link>
		<comments>http://www.jotlab.com/2010/08/11/protecting-files-against-hotlinking-with-php-lighttpd-apache-x-sendfile/#comments</comments>
		<pubDate>Wed, 11 Aug 2010 01:53:33 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[hot]]></category>
		<category><![CDATA[hotlinking]]></category>
		<category><![CDATA[lighttpd]]></category>
		<category><![CDATA[linking]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[webserver]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4528</guid>
		<description><![CDATA[I recently had to lock down some important files that first required some level of authentication. The problem was the only way to guard against hotlinking or direct access to these files was to pipe the data through php after an Auth check. Using fread,... <span><a href="http://www.jotlab.com/2010/08/11/protecting-files-against-hotlinking-with-php-lighttpd-apache-x-sendfile/" title="Protecting Files Against Hotlinking with PHP, Lighttpd, Apache &#038; X-Sendfile" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>I recently had to lock down some important files that first required some level of authentication. The problem was the only way to guard against hotlinking or direct access to these files was to pipe the data through php after an Auth check. Using fread, readfile or file_get_contents produced extreme CPU usage, not really to my surprise. Usually I wouldn&#8217;t care about high CPU usage on smaller projects, however this was causing the webserver to become unresponsive as it had to fully parse the files.</p>
<p>I went searching and eventually came across a module for both Apache and Lighttpd called X-Sendfile. The module works by sending headers out to the webserver to simply output a static file, without touching PHP&#8217;s file handling functions. First we need to install the module before we can start sending headers out to the webserver.</p>
<h3>Apache:</h3>
<ol>
<li>Login as root or your administrator account</li>
<li>Download the following code to your server <a href="http://tn123.ath.cx/mod_xsendfile/mod_xsendfile.c">mod_xsendfile.c</a></li>
<li>Installing is simple with: sudo apxs -cia mod_xsendfile.c</li>
<li>You will now notice the messages on the screen stating it has been added to your Apache config file and copied to the apache modules directory</li>
<li>Restart Apache. Done <img src='http://www.jotlab.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
</ol>
<h3>Lighttpd</h3>
<p>Setting up X-Sendfile on Lighttpd is much simpler as the module is shipped with Lighttpd 1.4+. All we will need to do is add in the &#8220;allow-x-send-file&#8221; => &#8220;enable&#8221; setting to our fastcgi.server array:</p>
<pre class="brush: php; ">
fastcgi.server = ( &quot;.php&quot; =&gt;
  ( &quot;localhost&quot; =&gt;
    (&quot;socket&quot; =&gt; &quot;/opt/local/var/run/lighttpd/php-fastcgi.socket&quot;,
     &quot;allow-x-send-file&quot; =&gt; &quot;enable&quot;,
     .......etc
    )
  )
)
</pre>
<h3>PHP &#038; Lighttpd</h3>
<p>Next up we will handle some basic user authentication with PHP to show how you can protect your files with PHP:</p>
<pre class="brush: php; ">
&lt;?php

if (!empty($_SESSION[&#039;logged_in&#039;])) {
  header( &quot;Content-Disposition: attachment; filename=\&quot;The Name You Want Your Users To See.mp3\&quot;&quot;);
  header( &quot;X-LIGHTTPD-send-file: /full/path/to/your/file.mp3&quot;);
} else {
  echo &quot;Sorry, You&#039;re not on the list, You&#039;re not coming in!&quot;;
}

?&gt;
</pre>
<p>If you&#8217;re using Lighttpd version 1.5 then you can replace &#8220;X-LIGHTTPD-send-file&#8221; with &#8220;X-Sendfile&#8221;.</p>
<h3>PHP &#038; Apache</h3>
<p>Using X-Sendfile with Apache is very similar, however we need to turn X-Sendfile on. Either put &#8220;XSendFile On&#8221; in a .htaccess files where your script resides, or put this in your Apache config file. The php configuration is pretty much exactly like Lighttpd 1.5 handling:</p>
<pre class="brush: php; ">
&lt;?php

if (!empty($_SESSION[&#039;logged_in&#039;])) {
  header( &quot;Content-Disposition: attachment; filename=\&quot;The Name You Want Your Users To See.mp3\&quot;&quot;);
  header( &quot;X-Sendfile: /full/path/to/your/file.mp3&quot;);
} else {
  echo &quot;Sorry, You&#039;re not on the list, You&#039;re not coming in!&quot;;
}

?&gt;
</pre>
<p>Feel free to move your files outside of the root path of the webserver which will prevent hotlinking, or as always, &#8220;deny from all&#8221; will do the trick in a .htaccess file.</p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/suwBqJ2cHEg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2010/08/11/protecting-files-against-hotlinking-with-php-lighttpd-apache-x-sendfile/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2010/08/11/protecting-files-against-hotlinking-with-php-lighttpd-apache-x-sendfile/</feedburner:origLink></item>
		<item>
		<title>Stay Out CakePHP Component</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/_YFS1Nvdfww/</link>
		<comments>http://www.jotlab.com/2010/06/27/stay-out-cakephp-component/#comments</comments>
		<pubDate>Sun, 27 Jun 2010 06:23:56 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[component]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[sessions]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4420</guid>
		<description><![CDATA[I have recently been developing a new website which has found me in the position of writing a lot of session handling code. I came across the concept of logged out all sessions across multiple instances, and out came this CakePHP session flushing component stay... <span><a href="http://www.jotlab.com/2010/06/27/stay-out-cakephp-component/" title="Stay Out CakePHP Component" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>I have recently been developing a new website which has found me in the position of writing a lot of session handling code. I came across the concept of logged out all sessions across multiple instances, and out came this CakePHP session flushing component stay out.</p>
<p>Stay out works by storing a timestamp in the users session and cross referencing that in the database. The component works by storing a unique SHA1&#8242;d string at the time of a user logout action (which should be called manually). When other user sessions of the same user are active and previously a log out has been called, that logged in user is then forced to be logged out.</p>
<p>You can find the project status over at: <a href="http://github.com/voidet/stay_out">http://github.com/voidet/stay_out</a></p>
<h3>Usage:</h3>
<p>First thing that needs to be done is to include it in your app/controllers/components folder as stay_out.php. Next include it in your list of components, noting that it should come after Auth:</p>
<pre class="brush: php; ">
var $components = array(&#039;StayOut&#039;);
</pre>
<p>The only thing that you really need to handle is when to execute the logout function. This should be done before you log the user out for example:</p>
<pre class="brush: php; ">
function logout() {
	$this-&gt;StayOut-&gt;setLogout();
	$this-&gt;StayOut-&gt;logout(); // or $this-&gt;Auth-&gt;logout();
}
</pre>
<p>There is another method called StayOut::logout() available to you, however this is just a helpful tool to flush out a user and all cookies associated with them. This is used internally in the event that an older session attempts to use a logged out session, however can also be called as a red alert flush all info helper.</p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/_YFS1Nvdfww" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2010/06/27/stay-out-cakephp-component/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2010/06/27/stay-out-cakephp-component/</feedburner:origLink></item>
		<item>
		<title>Picking up where strtotime() left off</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/WKIsjyevALA/</link>
		<comments>http://www.jotlab.com/2010/05/06/picking-up-where-strtotime-left-off/#comments</comments>
		<pubDate>Thu, 06 May 2010 06:00:18 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[dates]]></category>
		<category><![CDATA[datetime]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[formatting]]></category>
		<category><![CDATA[functions]]></category>
		<category><![CDATA[strtotime]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4371</guid>
		<description><![CDATA[I was working on a project where dates were being calculated (sort of) based on user input. For example the user was able to type in &#8220;Next Saturday&#8221; or the &#8220;Second Sunday of the Month&#8221;. This wouldn&#8217;t really be sortable, as strtotime doesn&#8217;t exactly allow... <span><a href="http://www.jotlab.com/2010/05/06/picking-up-where-strtotime-left-off/" title="Picking up where strtotime() left off" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>I was working on a project where dates were being calculated (sort of) based on user input. For example the user was able to type in &#8220;Next Saturday&#8221; or the &#8220;Second Sunday of the Month&#8221;. This wouldn&#8217;t really be sortable, as strtotime doesn&#8217;t exactly allow all of these inputs, and so the saga of extending the functionality to accomodate almost any user input (well what i found common use for this client anyway) began.</p>
<p>This function will spit out timestamps of the following dates for example:</p>
<p>Sat 5 &#8211; Sun 27 Jun 10<br />
Sun 6 Jun 2010<br />
Tue 29 Jun &#8211; Sat 3 Jul + Tue 6 Jul &#8211; Sat 10 Jul 2010<br />
Thu 29 Jul &#8211; Sat 31 Jul + Wed 4 Aug &#8211; Sat 7 Aug 2010<br />
Every Fri<br />
1st Sat of Every Month<br />
3rd Wed of Every Month</p>
<p>This was developed for a particular use case, so please, if you would like some other terms thrown in let me know and I might go ahead and build them in for future use.</p>
<pre class="brush: php; ">
function calculateStartDates($data) {
	foreach ($data as &amp;$event) {
		if (!empty($event[&#039;Event&#039;][&#039;time_period&#039;])) {
			$event[&#039;Event&#039;][&#039;start_timestamp&#039;] = $event[&#039;Event&#039;][&#039;time_period&#039;];
			preg_match(&#039;/(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i&#039;, $event[&#039;Event&#039;][&#039;start_timestamp&#039;], $months);
			preg_match(&#039;/(.*)every\s*(\w+)/i&#039;, $event[&#039;Event&#039;][&#039;start_timestamp&#039;], $every);
			$datePlurals = array(&#039;first&#039;, &#039;second&#039;, &#039;third&#039;, &#039;fourth&#039;, &#039;fifth&#039;, &#039;sixth&#039;, &#039;seventh&#039;, &#039;eigth&#039;, &#039;nineth&#039;);
			$fullDays = array(&#039;sat&#039; =&gt; &#039;Saturday&#039;, &#039;sun&#039; =&gt; &#039;Sunday&#039;, &#039;mon&#039; =&gt; &#039;Monday&#039;, &#039;tue&#039; =&gt; &#039;Tuesday&#039;, &#039;wed&#039; =&gt; &#039;Wednesday&#039;, &#039;thu&#039; =&gt; &#039;Thursday&#039;, &#039;fri&#039; =&gt; &#039;Friday&#039;);

			if (!empty($every[2])) {
				if (stristr($every[2], &#039;month&#039;)) {
					preg_match(&#039;/(mon|tue|wed|thur|fri|sat|sun)/i&#039;, $event[&#039;Event&#039;][&#039;start_timestamp&#039;], $day);
					preg_match_all(&#039;/(\d)(nd|st|th|rd)/i&#039;, $every[1], $times);
					if (!empty($times[1][0])) {
						$event[&#039;Event&#039;][&#039;start_timestamp&#039;] = date(&#039;d-m-Y&#039;, strtotime($datePlurals[$times[1][0]-1].&#039; &#039;.$fullDays[strtolower($day[1])]));
					}
				} else {
					$event[&#039;Event&#039;][&#039;start_timestamp&#039;] = date(&#039;d-m-Y&#039;, strtotime(&#039;next &#039;.$every[2]));
				}
			} else {
				$dashMatch = split(&#039;[-\+]&#039;, $event[&#039;Event&#039;][&#039;start_timestamp&#039;]);

				foreach ($dashMatch as $timeFrame) {
					preg_match(&#039;/(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i&#039;, $timeFrame, $thisMonth);

					if (!empty($months[1]) &amp;&amp; empty($thisMonth[1])) {
						if (!stristr($timeFrame, $months[1])) {
							$timeFrame = $timeFrame.&#039; &#039;.$months[1];
						}
					}
					$tempTime[] = strtotime($timeFrame);
				}

				sort($tempTime);
				unset($setTime, $tempTime);
				if (!empty($tempTime)) {
					foreach ($tempTime as $time) {
						if ($time &gt; mktime()) {
							$setTime = date(&#039;d-m-Y&#039;, $time);
							break;
						}
					}
				}

				if (!empty($timeFrame) &amp;&amp; empty($setTime)) {
					$event[&#039;Event&#039;][&#039;start_timestamp&#039;] = $timeFrame;
				} elseif (!empty($setTime)) {
					$event[&#039;Event&#039;][&#039;start_timestamp&#039;] = $setTime;
				}

			}
			$event[&#039;Event&#039;][&#039;start_timestamp&#039;] = strtotime($event[&#039;Event&#039;][&#039;start_timestamp&#039;]);
		}
	}
	return $data;
}
</pre>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/WKIsjyevALA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2010/05/06/picking-up-where-strtotime-left-off/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2010/05/06/picking-up-where-strtotime-left-off/</feedburner:origLink></item>
		<item>
		<title>jQuery – Clear Default Input Values Once</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/56JkFVOVj6Y/</link>
		<comments>http://www.jotlab.com/2010/04/30/jquery-clear-default-input-values-once/#comments</comments>
		<pubDate>Fri, 30 Apr 2010 03:01:38 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[clearing]]></category>
		<category><![CDATA[default]]></category>
		<category><![CDATA[empty]]></category>
		<category><![CDATA[fields]]></category>
		<category><![CDATA[input]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[values]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4368</guid>
		<description><![CDATA[I like to have default values in my text input fields. However it is an annoying user experience to have to manually clear the value out and then enter in the value. Instead I thought I would write a simple script that would work site... <span><a href="http://www.jotlab.com/2010/04/30/jquery-clear-default-input-values-once/" title="jQuery &#8211; Clear Default Input Values Once" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>I like to have default values in my text input fields. However it is an annoying user experience to have to manually clear the value out and then enter in the value. Instead I thought I would write a simple script that would work site wide to clear out the default values. You of course will need your jquery included:</p>
<p>Following some brilliant comments on this there is a much quicker method:</p>
<pre class="brush: javascript; ">
$(&#039;input[type=text]&#039;).one(&#039;focus&#039;, function(){
	$(this).attr(&#039;value&#039;, &#039;&#039;);
});
</pre>
<p>However the original code I came up with is as follows:</p>
<pre class="brush: javascript; ">
$(function(){
	$(&#039;input[type=text]&#039;).focus(function(){
		if (!this.clicked) {
			$(this).attr(&#039;value&#039;, &#039;&#039;);
			this.clicked = true;
		}
	});
});
</pre>
<p>This will clear the fields out only once. So if the user enters a value, then focuses back on the field, their values won&#8217;t be cleared. So if they needed to edit a single letter, then their entire entry won&#8217;t be wiped. If you have any hints feel free to comment</p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/56JkFVOVj6Y" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2010/04/30/jquery-clear-default-input-values-once/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2010/04/30/jquery-clear-default-input-values-once/</feedburner:origLink></item>
		<item>
		<title>CakePHP Rainbow Table Protection Behaviour</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/OJXoeMnQ2iQ/</link>
		<comments>http://www.jotlab.com/2010/04/18/cakephp-rainbow-table-protection-behaviour/#comments</comments>
		<pubDate>Sun, 18 Apr 2010 05:57:57 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[auth]]></category>
		<category><![CDATA[behavior]]></category>
		<category><![CDATA[behaviour]]></category>
		<category><![CDATA[greytables]]></category>
		<category><![CDATA[model]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[passwords]]></category>
		<category><![CDATA[plugins]]></category>
		<category><![CDATA[rainbow]]></category>
		<category><![CDATA[security]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4363</guid>
		<description><![CDATA[So after looking over some security techniques and discovering the quite interestingly named Rainbow table password cracking mechanism I decided to look into a way around this table password cracker. The default way that CakePHP hashes its passwords into the database is via hashing from... <span><a href="http://www.jotlab.com/2010/04/18/cakephp-rainbow-table-protection-behaviour/" title="CakePHP Rainbow Table Protection Behaviour" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>So after looking over some security techniques and discovering the quite interestingly named Rainbow table password cracking mechanism I decided to look into a way around this table password cracker. The default way that CakePHP hashes its passwords into the database is via hashing from a single salt string in core.php. This is a lot better than no salt string, however if someone was keen/bored/lame enough to get to work a rainbow table based on your single salt string then all the passwords/hashed-data stored in your tables would become readable. </p>
<p>GitHub: <a href="http://github.com/voidet/grey_tables">http://github.com/voidet/grey_tables</a></p>
<p>A way around this is to add a unique salt string per record, along with the CakePHP&#8217;s salt string from core.php. This was a rainbow table would have to be constructed based on a per record basis, and not just on a table. This would mean that the lamer would have to wait a few lifetimes to crack all the passwords in your database table, or wait a long time to crack a single user&#8217;s. So my initial behaviour is as follows, and it does require some work arounds:</p>
<pre class="brush: php; ">
&lt;?php

	class GreyTablesBehavior extends ModelBehavior {

		function setup(&amp;$model, $settings = array()) {
			$default = array(&#039;field&#039; =&gt; &#039;salt&#039;);

			if (!isset($this-&gt;settings[$model-&gt;name])) {
				$this-&gt;settings[$model-&gt;name] = $default;
			}

			$this-&gt;settings[$model-&gt;name] = array_merge($this-&gt;settings[$model-&gt;name], ife(is_array($settings), $settings, array()));
		}

		function beforeFind(&amp;$model, $queryData) {
			if(!empty($queryData[&#039;conditions&#039;][$model-&gt;name.&#039;.password&#039;]) &amp;&amp; !empty($queryData[&#039;conditions&#039;][$model-&gt;name.&#039;.username&#039;])) {
				$user_id = $this-&gt;findSaltedUser($model, $queryData[&#039;conditions&#039;]);
				if (!empty($user_id)) {
					unset($queryData[&#039;conditions&#039;]);
					$queryData[&#039;conditions&#039;] = $user_id;
				}
			}
			return $queryData;
		}

		function beforeSave(&amp;$model) {
			if (empty($this-&gt;id) &amp;&amp; !empty($model-&gt;data[$model-&gt;name])) {
				$data = &amp;$model-&gt;data[$model-&gt;name];
				$data[&#039;password&#039;] = $this-&gt;generateSaltedPassword($data[&#039;password&#039;], $data[$this-&gt;settings[$model-&gt;name][&#039;field&#039;]]);
			}
			return parent::beforeSave(&amp;$model);
		}

		function generateSaltedPassword($password = &#039;&#039;, $saltString) {
			if (!empty($password)) {
				return Security::hash($password.$saltString, null, false);
			}
		}

		function findSaltedUser(&amp;$model, $fields = array()) {
			if (!empty($fields)) {
				$user_id = $model-&gt;query(&#039;SELECT `&#039;.$model-&gt;name.&#039;`.`id` as \&#039;id\&#039; FROM &#039;.$model-&gt;table.&#039; as &#039;.$model-&gt;name.&#039; WHERE `&#039;.$model-&gt;name.&#039;`.`username` = \&#039;&#039;.$fields[$model-&gt;name.&#039;.username&#039;].&#039;\&#039; AND `&#039;.$model-&gt;name.&#039;`.`password` = SHA1(CONCAT(\&#039;&#039;.$fields[$model-&gt;name.&#039;.password&#039;].&#039;\&#039;, `&#039;.$this-&gt;settings[$model-&gt;name][&#039;field&#039;].&#039;`)) LIMIT 1&#039;);
				if (!empty($user_id)) {
					$fields[$model-&gt;name.&#039;.id&#039;] = $user_id[0][$model-&gt;name][&#039;id&#039;];
					unset($fields[$model-&gt;name.&#039;.password&#039;], $fields[$model-&gt;name.&#039;.username&#039;]);
				}
			}
			return $fields;
		}

		function hashPasswords(&amp;$data, $alias) {
			if (isset($data[$alias][&#039;password&#039;])) {
				$model-&gt;data = $data;
				$model-&gt;data[$alias][$this-&gt;settings[$alias][&#039;field&#039;]] = Security::hash(String::uuid(), null, true);
				$model-&gt;data[$alias][&#039;password&#039;] = Security::hash($data[$alias][&#039;password&#039;], null, true);
				return $model-&gt;data;
			}
			return $data;
		}

	}

?&gt;
</pre>
<p>Now in your model you would do something like:</p>
<pre class="brush: php; ">
&lt;?php

	class Member extends AppModel {

		var $actsAs = array(&#039;GreyTables&#039;);

		function hashPasswords($data) {
			return $this-&gt;Behaviors-&gt;GreyTables-&gt;hashPasswords($data, $this-&gt;alias);
		}

	}

?&gt;
</pre>
<p>It&#8217;s a simple work around to get the behaviour to use the hashPasswords method automatically called. I am yet to do any extensive benchmarks on this code, and will have to include more settings in regards to how the salting is handled, and also the default username/password/email fields to be used to check the Auth. That will come soon. There is one flaw however, in that the conditions are constructed in two parts. The first part looks for the resalted username/password combo, then the second part uses the id found from that query and follows up with any other conditions to be used for the query. For example:</p>
<p>First query:</p>
<pre class="brush: sql; ">
SELECT `Member`.`id` as &#039;id&#039; FROM members as Member WHERE `Member`.`username` = &#039;testuser&#039; AND `Member`.`password` = SHA1(CONCAT(&#039;1e71a44447c4e3ea05e8f81f031702f00d19c48e&#039;, `salt`)) LIMIT 1
</pre>
<p>Second query:</p>
<pre class="brush: sql; ">
SELECT `Member`.`id`, `Member`.`username`, `Member`.`email`, `Member`.`password`, `Member`.`salt`, `Member`.`active`, `Member`.`created`, `Member`.`modified` FROM `members` AS `Member` WHERE `Member`.`active` = 1 AND `Member`.`id` = 52 LIMIT 1
</pre>
<p>I will be working to make the second query work as a read via the Id, as apposed to extra conditions.<br />
More to come!</p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/OJXoeMnQ2iQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2010/04/18/cakephp-rainbow-table-protection-behaviour/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2010/04/18/cakephp-rainbow-table-protection-behaviour/</feedburner:origLink></item>
		<item>
		<title>Accelerated Learning through Pressure</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/IrSXRnPbULI/</link>
		<comments>http://www.jotlab.com/2010/04/08/accelerated-learning-through-pressure/#comments</comments>
		<pubDate>Thu, 08 Apr 2010 10:34:20 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[learning]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4357</guid>
		<description><![CDATA[So my co-worker left 2 weeks ago to have a 3 month journey across Australia. This means that I am now in charge of all the web development/projects for our business. For one developer, the amount of “active” (bugs/todos) projects we have open for one... <span><a href="http://www.jotlab.com/2010/04/08/accelerated-learning-through-pressure/" title="Accelerated Learning through Pressure" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>So my co-worker left 2 weeks ago to have a 3 month journey across Australia. This means that I am now in charge of all the web development/projects for our business. For one developer, the amount of “active” (bugs/todos) projects we have open for one developer at this point in time is quite enormous, leaving me quite overwhelmed at this point in time.</p>
<p>It seems to be the case that if there isn’t a new job that is coming up in the next few days, it’s a client that has found a bug and demands it patched by the end of the business day. The problem with this is that there are lots of technologies that i have yet to deal with where the bugs have arisen; to be more specific, flash websites. Now i’ve got my action script 3 book, and although the book is interesting to some degree, I find the technology somewhat boring. I’ve never been the type of developer to commit to a technology/language that isn’t open source, or admittedly, one that is client side. Alas the technology is in demand, and I am in the position now to learn the language and get these bugs squashed.</p>
<p>The upside to this somewhat short written story is that i am learning an absolute bucket load! I’ve been totally immersed in my co-worker’s excellent code that exhausts almost everything that CakePHP can offer. Learning new techniques, practices and solutions to “uncommon” problems really forces me to analyse and understand code on a whole new level. It now extends beyond looking briefly at a function, putting an array of data into it and seeing what comes out to see exactly what the function can do. So all that is left to do is to cope with the load of the clients barking and new project planning, and come out on top with a deeper understanding of what is to offer with the technologies out there. More news to come.</p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/IrSXRnPbULI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2010/04/08/accelerated-learning-through-pressure/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2010/04/08/accelerated-learning-through-pressure/</feedburner:origLink></item>
		<item>
		<title>CakePHP &amp; Caching Until a Future Post</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/_R37d913M3Y/</link>
		<comments>http://www.jotlab.com/2010/03/23/cakephp-caching-until-a-future-post/#comments</comments>
		<pubDate>Tue, 23 Mar 2010 03:36:41 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[find]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[tutorials]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4350</guid>
		<description><![CDATA[We had implemented a caching system for all find, pagination and pagination count methods with CakePHP. You can find a post on this here. The cache was expiring after every hour, however we failed to realise something. Our CMS system that we built allows posts... <span><a href="http://www.jotlab.com/2010/03/23/cakephp-caching-until-a-future-post/" title="CakePHP &#038; Caching Until a Future Post" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>We had implemented a caching system for all find, pagination and pagination count methods with CakePHP. You can find a post on this <a href="http://www.jotlab.com/2010/02/09/pagination-caching-with-cakephp/">here</a>. The cache was expiring after every hour, however we failed to realise something. Our CMS system that we built allows posts to show up on the website at a later date, that is, you could set a publish date. Since the cache was only for an hour, or cleared when a new post was added/edited, then there would be a lag time for these new posts to show up. Instead we decided set the expiry date/time of the cached objects to their next future update, for example:</p>
<pre class="brush: php; ">
if (!empty($this-&gt;_schema[&#039;published_date&#039;])) {
	$next = $this-&gt;find(&#039;first&#039;, array(&#039;conditions&#039; =&gt; array($this-&gt;name.&#039;.published_date &gt; NOW()&#039;), &#039;order&#039; =&gt; array($this-&gt;name.&#039;.published_date&#039;), &#039;recursive&#039; =&gt; -1));
		if (!empty($next)) {
		$expires = $next[$this-&gt;name][&#039;published_date&#039;];
		// Set cache settings
		Cache::config(&#039;sql_cache&#039;, array(
			&#039;duration&#039;	=&gt; $expires
		));
	}
}
</pre>
<p>So what this snippet does is tests whether or not the current model being used has a field that would allow it to be posted into the future. This is done in $this->_schema['published_date']. From there you can override the cache settings expiry time to that of the post&#8217;s publish date. In reality you could have an infinite expiry on cache, if there are no future posts to post, as your system should also clear cache on add/editing of posts.</p>
<p>This blog post wasn&#8217;t exactly clear, as i wrote it on the spur of the moment, but you get the idea. If you want the full context of the find caching on generic models you can check it out:</p>
<pre class="brush: php; ">
function find ($conditions = null, $fields = array(), $order = null, $recursive = null) {
	if (Configure::read(&#039;Cache.disable&#039;) === false &amp;&amp; Configure::read(&#039;Cache.check&#039;) === true &amp;&amp; is_array($fields) &amp;&amp; array_key_exists(&#039;cache&#039;, $fields) &amp;&amp; $fields[&#039;cache&#039;] !== false) {
		$key = $fields[&#039;cache&#039;];
		$expires = &#039;+1 hour&#039;;

		if (is_array($fields[&#039;cache&#039;])) {
			$key = $fields[&#039;cache&#039;][0];

			if (isset($fields[&#039;cache&#039;][1])) {
				$expires = $fields[&#039;cache&#039;][1];
			}
		}
		Cache::config(&#039;sql_cache&#039;, array(
			&#039;prefix&#039; 	=&gt; strtolower($this-&gt;name) .&#039;-&#039;,
		));

		// Load from cache
		$results = Cache::read($key, &#039;sql_cache&#039;);

		if (empty($results)) {
			if (!empty($this-&gt;_schema[&#039;published_date&#039;])) {
				$next = $this-&gt;find(&#039;first&#039;, array(&#039;conditions&#039; =&gt; array($this-&gt;name.&#039;.published_date &gt; NOW()&#039;), &#039;order&#039; =&gt; array($this-&gt;name.&#039;.published_date&#039;), &#039;recursive&#039; =&gt; -1));
				if (!empty($next)) {
					$expires = $next[$this-&gt;name][&#039;published_date&#039;];
				}
			}

			// Set cache settings
			Cache::config(&#039;sql_cache&#039;, array(
				&#039;duration&#039;	=&gt; $expires
			));

			$results = parent::find($conditions, $fields, $order, $recursive);
			Cache::write($key, $results, &#039;sql_cache&#039;);
		}
		return $results;
	}

	// Not cacheing
	return parent::find($conditions, $fields, $order, $recursive);
}
</pre>
<p>Please note the original idea to override the find method was from Miles Johnson over at <a href="http://www.milesj.me/blog/read/34/Cacheing-Each-Query-Individually">http://www.milesj.me/blog/read/34/Cacheing-Each-Query-Individually</a>&#8230;&#8230;genius</p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/_R37d913M3Y" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2010/03/23/cakephp-caching-until-a-future-post/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2010/03/23/cakephp-caching-until-a-future-post/</feedburner:origLink></item>
		<item>
		<title>Pagination Caching With CakePHP</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/Bsflm4IXbJI/</link>
		<comments>http://www.jotlab.com/2010/02/09/pagination-caching-with-cakephp/#comments</comments>
		<pubDate>Tue, 09 Feb 2010 02:48:22 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[app_model]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[cake]]></category>
		<category><![CDATA[pagination]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4327</guid>
		<description><![CDATA[A site i was working with was using pagination quite extensively with lots of records and associations. This was producing unnecessary high load on the database and wait times on the production site. Caching had to be done. Unlike normal returned data from finds etc. Paginated... <span><a href="http://www.jotlab.com/2010/02/09/pagination-caching-with-cakephp/" title="Pagination Caching With CakePHP" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>A site i was working with was using pagination quite extensively with lots of records and associations. This was producing unnecessary high load on the database and wait times on the production site. Caching had to be done. Unlike normal returned data from finds etc. Paginated data can not be cached as easily, as the paginate method needs to be called to generate the pagination numbers etc. So what was done was to do a custom pagination query with cache built in. So in your <strong>app_model.php</strong> file add in:</p>
<pre class="brush: php; ">
function paginate ($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) {
		$args = func_get_args();
		$uniqueCacheId = &#039;&#039;;
		foreach ($args as $arg) {
			$uniqueCacheId .= serialize($arg);
		}
		if (!empty($extra[&#039;contain&#039;])) {
			$contain = $extra[&#039;contain&#039;];
		}
		$uniqueCacheId = md5($uniqueCacheId);
		$pagination = Cache::read(&#039;pagination-&#039;.$this-&gt;alias.&#039;-&#039;.$uniqueCacheId, &#039;paginate_cache&#039;);
		if (empty($pagination)) {
			$pagination = $this-&gt;find(&#039;all&#039;, compact(&#039;conditions&#039;, &#039;fields&#039;, &#039;order&#039;, &#039;limit&#039;, &#039;page&#039;, &#039;recursive&#039;, &#039;group&#039;, &#039;contain&#039;));
			Cache::write(&#039;pagination-&#039;.$this-&gt;alias.&#039;-&#039;.$uniqueCacheId, $pagination, &#039;paginate_cache&#039;);
		}
		return $pagination;
	}

	function paginateCount ($conditions = null, $recursive = 0, $extra = array()) {
		$args = func_get_args();
		$uniqueCacheId = &#039;&#039;;
		foreach ($args as $arg) {
			$uniqueCacheId .= serialize($arg);
		}
		$uniqueCacheId = md5($uniqueCacheId);
		if (!empty($extra[&#039;contain&#039;])) {
			$contain = $extra[&#039;contain&#039;];
		}

		$paginationcount = Cache::read(&#039;paginationcount-&#039;.$this-&gt;alias.&#039;-&#039;.$uniqueCacheId, &#039;paginate_cache&#039;);
		if (empty($paginationcount)) {
			$paginationcount = $this-&gt;find(&#039;count&#039;, compact(&#039;conditions&#039;, &#039;contain&#039;, &#039;recursive&#039;));
			Cache::write(&#039;paginationcount-&#039;.$this-&gt;alias.&#039;-&#039;.$uniqueCacheId, $paginationcount, &#039;paginate_cache&#039;);
		}
		return $paginationcount;
	}
</pre>
<p>This will then take over from any paginate calls and generate a cached version of the dataset for the paginated items and the pagination controls, unique to each page and query set. I am in the process of trying to convert this over to a behaviour, will post up if i get a chance to complete it.</p>
<p>Of course you need to specify the caching rule in core.php, something like:</p>
<pre class="brush: php; ">
Cache::config(&#039;paginate_cache&#039;, array(
	    &#039;engine&#039;		=&gt; &#039;File&#039;,
	    &#039;path&#039;		=&gt; CACHE .&#039;sql&#039;. DS,
	    &#039;serialize&#039;	=&gt; true,
			&#039;duration&#039; =&gt; &#039;+1 hour&#039;,
	));
</pre>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/Bsflm4IXbJI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2010/02/09/pagination-caching-with-cakephp/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2010/02/09/pagination-caching-with-cakephp/</feedburner:origLink></item>
		<item>
		<title>How to downgrade iPhone 3.1.3 to 3.1.2 &amp; Jailbreak</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/C-mST0jQiKI/</link>
		<comments>http://www.jotlab.com/2010/02/08/how-to-downgrade-iphone-3-1-3-to-3-1-2-jailbreak/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 08:48:19 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[iphone]]></category>
		<category><![CDATA[3.1.2]]></category>
		<category><![CDATA[3.1.3]]></category>
		<category><![CDATA[apple]]></category>
		<category><![CDATA[blackra1n]]></category>
		<category><![CDATA[break]]></category>
		<category><![CDATA[hack]]></category>
		<category><![CDATA[jail]]></category>
		<category><![CDATA[jailbreak]]></category>
		<category><![CDATA[redsn0w]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4320</guid>
		<description><![CDATA[If you would like to jailbreak your iphone, ipod or ipad up to 3.2 you should be using the new non-teathered jailbreak from spirit: http://spiritjb.com/ So I accidentally upgraded my iPhone to 3.1.3, bad news bears. I lost my jailbreak with no look of getting it back,... <span><a href="http://www.jotlab.com/2010/02/08/how-to-downgrade-iphone-3-1-3-to-3-1-2-jailbreak/" title="How to downgrade iPhone 3.1.3 to 3.1.2 &#038; Jailbreak" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>If you would like to jailbreak your iphone, ipod or ipad up to 3.2 you should be using the new non-teathered jailbreak from spirit: <em><strong><a href="http://spiritjb.com/">http://spiritjb.com/</a></strong></em></p>
<p>So I accidentally upgraded my iPhone to 3.1.3, bad news bears. I lost my jailbreak with no look of getting it back, especially on a newer bootrom. After playing around for hours on hours I finally locked it down to this workflow:</p>
<p><em><strong>**Please Note**</strong> If you bought a new phone with 3.1.3 already loaded, or if you upgraded to 3.1.3 without storing your signature on saurik&#8217;s server via Cydia then your chances of getting a jail broken 3.1.2 is zero. Sit tight until a new jailbreak is out, which unfortunately doesn&#8217;t seem like it will be soon.</em></p>
<ul>
<li>Open up iTunes. It doesn&#8217;t matter what version it is. Then click on restore with shift (win) or alt (mac) held down. Select the firmware for 3.1.2. You can download the firmware you need over at <a href="http://www.felixbruns.de/iPod/firmware/">http://www.felixbruns.de/iPod/firmware/</a></li>
<li>Wait for that to go through. You will probably hit an error 1050, which is no problem. Download redsn0w version 0.9.4. There are plenty of links out there. Load up the 3.1.2 firmware in that and then let it do its magic. It will probably hit the part where you need to activate and what not. I let me phone activate and enter a standard 3.1.2 state. But no cydia or jailbreak tools</li>
<li>Next i loaded up <a href="http://blackra1n.com/">blackra1n</a> and let it do its jailbreak. I had to do this twice to get in, so then i could install cydia.</li>
</ul>
<p>For those of you experiencing issues with iTunes reporting back a failure of verification on the requested build, you will need to add a rule to your hosts file. You will need to add the entry: <strong>74.208.105.171 gs.apple.com</strong></p>
<p><strong>Windows:</strong><br />
go to C:\Windows\System32\drivers\etc\ and use a text editor to open the file “hosts”</p>
<p><strong>Mac:</strong><br />
open up Terminal.app an type sudo /Applications/TextEdit.app/Contents/MacOS/TextEdit /etc/hosts</p>
<p>Now I have my iPhone back onto 3.1.2 and i don&#8217;t think i am going to bother with jail-breaking the 3.1.3.</p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/C-mST0jQiKI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2010/02/08/how-to-downgrade-iphone-3-1-3-to-3-1-2-jailbreak/feed/</wfw:commentRss>
		<slash:comments>60</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2010/02/08/how-to-downgrade-iphone-3-1-3-to-3-1-2-jailbreak/</feedburner:origLink></item>
		<item>
		<title>Fixing Vi and Arrows in Insert Mode</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/m2Tk3soG20E/</link>
		<comments>http://www.jotlab.com/2010/01/20/fixing-vi-and-arrows-in-insert-mode/#comments</comments>
		<pubDate>Wed, 20 Jan 2010 07:08:38 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[arrows]]></category>
		<category><![CDATA[problem]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[troubles]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[vi]]></category>
		<category><![CDATA[vim]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4308</guid>
		<description><![CDATA[Recently I moved one of my sites onto another server, different installation. I noticed when i was working in Vi in insert mode my arrow keys would insert their values instead of navigating around the text. To fix this: Make a new file in your... <span><a href="http://www.jotlab.com/2010/01/20/fixing-vi-and-arrows-in-insert-mode/" title="Fixing Vi and Arrows in Insert Mode" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>Recently I moved one of my sites onto another server, different installation. I noticed when i was working in Vi in insert mode my arrow keys would insert their values instead of navigating around the text. To fix this:</p>
<ul>
<li>Make a new file in your home directory valled .vimrc</li>
<li>Inside it put in:
<p><code>:set backspace=indent,eol,start<br />
:set term=builtin_ansi</code></p>
</li>
</ul>
<p>Now that global setting should be applied whenever you are working with files in vi and your cursor navigation troubles should be no more!</p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/m2Tk3soG20E" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2010/01/20/fixing-vi-and-arrows-in-insert-mode/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2010/01/20/fixing-vi-and-arrows-in-insert-mode/</feedburner:origLink></item>
		<item>
		<title>The Year of 2009</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/f72_Xh5fE3c/</link>
		<comments>http://www.jotlab.com/2009/12/27/the-year-of-2009/#comments</comments>
		<pubDate>Sun, 27 Dec 2009 00:36:14 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[Ideas]]></category>
		<category><![CDATA[australia]]></category>
		<category><![CDATA[brisbane]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[Canada]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4298</guid>
		<description><![CDATA[It&#8217;s been quite a turbulent year this year. Kicking it off it began with a temperature of -35 on New Years day, lots of cleaning up from the celebrations the night before but more holiday fun ahead in Canada. Travelled through Western Canada with my... <span><a href="http://www.jotlab.com/2009/12/27/the-year-of-2009/" title="The Year of 2009" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been quite a turbulent year this year. Kicking it off it began with a temperature of -35 on New Years day, lots of cleaning up from the celebrations the night before but more holiday fun ahead in Canada. Travelled through Western Canada with my awesome Canadian partner and got to see college life first hand.</p>
<p>After leaving Canada it was time to set up some projects and learning. Having programmed in PHP for a few years, at a hobbyist level it was time to step it up and bite the bullet and jump into the long avoided frameworks. A quick touch back to Django had me scratching my head again at the challenge of learning a new language alongside a framework methodology I had no idea about.</p>
<p>A friend of mine from Germany recommend that I take a decent look at the CakePHP framework and after getting my head around why I would abstract my functions out into an MVC structure I got going on building up the projects that I had started by nowhere near completed.</p>
<p>My podcast hosting project got off to a decent start, blazesyn.com, but then hit some big walls, of which I plan to get around in the new year. This got my CakePHP and PHP skills up to much stronger level. My partner, after killing her University degree came down to Australia to live, landing in May. We took some time out, and travelled around Brisbane and down to Melbourne, enjoying the time together to make up for time lost. However the holidays slowly ran down as the bank dried up.</p>
<p>In the mean time I stumbled on an idea while on Holidays, an idea that I had roughly three years ago, but the plans fell through due to costs. It was a decal/signage business purely online. Luckily I had built up my CakePHP skills and got straight to work on wedecal.com which has been going strong. We&#8217;ve invested a decent amount of money into advertising and we&#8217;re seeing the return slowly climb as the repeat business kicks into effect.</p>
<p>Then I got lucky, I received two expressions of interest for full time work through this blog at jotlab.com. One position was working remotely for a fashion website in Sydney. That fell through, poor communication, which is something you simply can&#8217;t have when working remotely. I pulled the pin on that one, but as soon as I did a new opportunity opened up. A local advertising agency contacted me, again through this blog, saying they&#8217;ve been following my topics on CakePHP and they would like to offer me a full time position as a CakePHP developer in Brisbane.</p>
<p>I jumped at the opportunity. The workplace was a pad, it had a BBQ area, a swimming pool and the whole place was a decked out church! What&#8217;s more they put on a free lunch and beer every Friday afternoon, that&#8217;s my kind of work environment. The profile of the clients was jaw dropping and the projects were always challenging. I was on a three week trial then the work ran out. I was back to looking for work.</p>
<p>It was job time and I was sending out resumes to pretty much any web development or web related job I could find. The replies came in, recruiters were calling, positions were opening up and everyone was interested, but not on the terms I wanted. Then I got a phone call from the church of CakePHP, they wanted me back, this time it was I writing for a full time position, and that&#8217;s where I am now.</p>
<p>As for development, I am aiming to getting a bit deeper into the CakePHP core. Understanding the inner workings and what seems to be a large array of undocumented CakePHP functions. Besides that I really want to get into Ruby on Rails. It was a bit unnerving to see the CakePHP team split as a core dev moved to create his own branch/framework based on CakePHP3. If i do get extra time it&#8217;s onto iPhone development, however one step at a time.</p>
<p>And that is my year of 2009, in terms of development, work and travel. As for next year, our concentration is on moving closer to our workplaces and visa&#8217;s. Let&#8217;s hope Australia deals us an okay hand for 2010.</p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/f72_Xh5fE3c" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2009/12/27/the-year-of-2009/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2009/12/27/the-year-of-2009/</feedburner:origLink></item>
		<item>
		<title>How to use FileMerge with Git as a Diff Tool on OSX</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/LoRcdrhysMY/</link>
		<comments>http://www.jotlab.com/2009/11/16/how-to-use-filemerge-with-git-as-a-diff-tool-on-osx/#comments</comments>
		<pubDate>Mon, 16 Nov 2009 07:38:07 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[difference]]></category>
		<category><![CDATA[filemerge]]></category>
		<category><![CDATA[files]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[management]]></category>
		<category><![CDATA[opendiff]]></category>
		<category><![CDATA[vcs]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4268</guid>
		<description><![CDATA[As I&#8217;m migrating from Subversion brainwaves I&#8217;ve been slowly adapting my workflow into Git&#8217;s. Problem was i was finding it hard to double check changes in my working folder to that of what was previously committed. In other words I missed the love of FileMerge.... <span><a href="http://www.jotlab.com/2009/11/16/how-to-use-filemerge-with-git-as-a-diff-tool-on-osx/" title="How to use FileMerge with Git as a Diff Tool on OSX" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>As I&#8217;m migrating from Subversion brainwaves I&#8217;ve been slowly adapting my workflow into Git&#8217;s. Problem was i was finding it hard to double check changes in my working folder to that of what was previously committed. In other words I missed the love of FileMerge.</p>
<p>So I began puttering around on the internet and found people writing scripts for git&#8217;s external diff editor. But they were missing one important element, the location of OpenDiff to save to! So I did this:</p>
<p>Firstly we need to make the script that git will run when it runs it&#8217;s diff. To do this run:</p>
<pre class="brush: bash; ">
vi ~/git-diff-cmd.sh
</pre>
<p>Then we will drop some script in here which basically routes the diff statements generated by Git to the OpenDiff application</p>
<pre class="brush: bash; ">
#!/bin/sh
/usr/bin/opendiff &quot;$2&quot; &quot;$5&quot; -merge &quot;$1&quot;
</pre>
<p>Notice the &#8220;<code>-merge "$1"</code>&#8221; this is the destination to save to. Before hand with other people&#8217;s code you would have to manually select the save path. But i did a man opendiff on it to realise all it was, was setting the merge path!</p>
<p>Anyhow. Save that via pressing &#8220;<code>shift + ZZ</code>&#8221; Saved! Next we need to make that file executable via &#8220;<code>chmod +x</code>&#8221;</p>
<p>All that is left is to tell Git to use this script as it&#8217;s diff editor, which will then invoke the FileMerge (OpenDiff) program. To do that type in:</p>
<pre class="brush: bash; ">
git config --global diff.external ~/git-diff-cmd.sh
</pre>
<p>Now how i check my staged files with the previous commit would be:</p>
<pre class="brush: bash; ">
git diff --cached
</pre>
<p>Which would then open up all the staged files and allow me to choose which one i wanted to keep or not <img src='http://www.jotlab.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> </p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/LoRcdrhysMY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2009/11/16/how-to-use-filemerge-with-git-as-a-diff-tool-on-osx/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2009/11/16/how-to-use-filemerge-with-git-as-a-diff-tool-on-osx/</feedburner:origLink></item>
		<item>
		<title>JotPad – TextMate Theme</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/N38PtFvQvmc/</link>
		<comments>http://www.jotlab.com/2009/11/10/jotpad-textmate-theme/#comments</comments>
		<pubDate>Tue, 10 Nov 2009 03:23:53 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[colours]]></category>
		<category><![CDATA[font]]></category>
		<category><![CDATA[mate]]></category>
		<category><![CDATA[programmig]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[text]]></category>
		<category><![CDATA[textmate]]></category>
		<category><![CDATA[theme]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4260</guid>
		<description><![CDATA[Recently i started playing with TextMate because of all the hype and the billions of shortcuts it has. This was a big shift from the editor Coda.app on the Mac. Immediately i was taken by the ability to have themes! So i decided to create... <span><a href="http://www.jotlab.com/2009/11/10/jotpad-textmate-theme/" title="JotPad &#8211; TextMate Theme" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>Recently i started playing with TextMate because of all the hype and the billions of shortcuts it has. This was a big shift from the editor Coda.app on the Mac. Immediately i was taken by the ability to have themes! So i decided to create one myself. Adapted from the eclips3.media theme freely available.</p>
<p>Simply download and double click on the following theme:</p>
<p><a href='http://www.jotlab.com/wp-content/uploads/2009/11/JotPad.tmTheme.zip'><strong>JotPad.tmTheme.zip</strong></a></p>
<p>If you wish to use the same font as me then you can download the Bitstream Vera Sans Mono font from:</p>
<p><a href='http://www.jotlab.com/wp-content/uploads/2009/11/ttf-bitstream-vera-1.10.tar.gz'><strong>Bitstream Vera</strong></a></p>
<p>This is v1.0 of the theme, if updates happen in the near future i&#8217;ll write up the changelog here!</p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/N38PtFvQvmc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2009/11/10/jotpad-textmate-theme/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2009/11/10/jotpad-textmate-theme/</feedburner:origLink></item>
		<item>
		<title>Installing Ruby on Rails + MySQL + Imagemagick + Nginx + Passenger With Macports OSX</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/9dhotaktX4s/</link>
		<comments>http://www.jotlab.com/2009/11/08/installing-ruby-on-rails-mysql-imagemagick-nginx-passenger-with-macports/#comments</comments>
		<pubDate>Sun, 08 Nov 2009 03:04:11 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[guide]]></category>
		<category><![CDATA[imagemagick]]></category>
		<category><![CDATA[macports]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[passenger]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[ruby on rails]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4239</guid>
		<description><![CDATA[This was one epic battle for me when i tried to get rails going with Lighttpd. But with that said, i have been spending quite sometime on just getting my development environment up and running on an environment similar to how i would deploy the... <span><a href="http://www.jotlab.com/2009/11/08/installing-ruby-on-rails-mysql-imagemagick-nginx-passenger-with-macports/" title="Installing Ruby on Rails + MySQL + Imagemagick + Nginx + Passenger With Macports OSX" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>This was one epic battle for me when i tried to get rails going with Lighttpd. But with that said, i have been spending quite sometime on just getting my development environment up and running on an environment similar to how i would deploy the app. So this is how i managed to get Rails up and running with Nginx.</p>
<p>First you need to download and install macports. You can get it from: <a href="http://www.macports.org/">http://www.macports.org/</a>. Install that, it may take some time, but it&#8217;s well worth it!</p>
<p>Next we need to get some updating going on, so that your sources are up to date and fresh to get some ports installing. Open up terminal and type:</p>
<pre class="brush: bash; ">
sudo port -v selfupdate
</pre>
<p>The <strong>-v</strong> is for verbose output, so you can see exactly what is going on. After that is complete lets going with installing ruby and some rails.</p>
<p>First we need to install ruby. To do this type:</p>
<pre class="brush: bash; ">
sudo port install ruby
</pre>
<p>This will go ahead and install ruby. Next up run:</p>
<pre class="brush: bash; ">
sudo port install rb-rubygems
</pre>
<p>This will allow us to access the awesome gems feature that allows you to import third-party gems, or plugins. Once that has been installed you will want to run an update, to get the latest gems sources. This can now be done through the gem command.</p>
<pre class="brush: bash; ">
sudo gem update --system
</pre>
<p>To add support for Imagemagick run these commands:</p>
<pre class="brush: bash; ">
sudo port install Imagemagick
sudo gem install rmagick
</pre>
<p>Pretty simple hey? Next up we will want to add some support for mysql5. To do this we will go back to our port system and run:</p>
<pre class="brush: bash; ">
sudo port install mysql5 +server
</pre>
<p>You will then be shown some hints as to how to set up the test tables and run the secure installation. I would recommend doing this as it allows you to setup your root password and clear test tables. Just follow the onscreen prompts on how to install it. Some people have permission issues with sockets here, if you are one of them check out <a href="http://www.martinoflynn.com/blog/2008/08/13/installing-mysql-on-mac-os-x-105/">this</a> page. Once that is done run:</p>
<pre class="brush: bash; ">
sudo port install rb-mysql
</pre>
<p>This will install the ruby mysql adapter. Wait for that to go through next we can add rails! Yep we&#8217;re up to that stage already!</p>
<p>It&#8217;s pretty simple:</p>
<pre class="brush: bash; ">
sudo gem install rails
</pre>
<p>Rails is installed! You can start a new rails app now simply by navigating to the folder where you would like to create the app and typing:</p>
<pre class="brush: bash; ">
rails app_name
</pre>
<p>and to make sure that you have mysql support created for you instead of manually type:</p>
<pre class="brush: bash; ">
rails -d mysql app_name
</pre>
<p>To test that your app ran smoothly you can simply change directory into your new app and run:</p>
<pre class="brush: bash; ">
ruby script/server
</pre>
<p>and this will load up the built in Webrick server, of which you can now navigate to in your browser via http://localhost:3000</p>
<p>If all went successfully then you should see a screen like:<br />
<a href="http://www.jotlab.com/wp-content/uploads/2009/11/Screen-shot-2009-11-08-at-12.13.10-PM.jpg"><img class="aligncenter size-medium wp-image-4244" title="Screen shot 2009-11-08 at 12.13.10 PM" src="http://www.jotlab.com/wp-content/uploads/2009/11/Screen-shot-2009-11-08-at-12.13.10-PM-500x354.jpg" alt="Screen shot 2009-11-08 at 12.13.10 PM" width="500" height="354" /></a></p>
<p>We don&#8217;t really want to use this built in server, we want to use Nginx with passenger! And to do that we don&#8217;t want to build from source, we want to use the Macports elegance, well i did anyway. Unfortunately macports doesn&#8217;t support this off the bat. But with some tweaking its very easy to do. First we will need to grab passenger. This is once again very easy to achieve, thanks to gems:</p>
<pre class="brush: bash; ">
sudo gem install passenger
</pre>
<p>Now run:</p>
<pre class="brush: bash; ">
passenger-config --root
</pre>
<p>this will tell you where passenger installed and where it&#8217;s hiding out. We will need this for telling Nginx where to grab it from. So please take now, paste it to a text file or something.</p>
<p>Next step up is to get Nginx installed. As i said before this will require some hackering. So lets do that first. What we need to do is add the passenger variant which currently isn&#8217;t available with the nginx macport. So to do that navigate to the nginx portfile directory, which can be done via:</p>
<pre class="brush: bash; ">
cd /opt/local/var/macports/sources/rsync.macports.org/release/ports/www/nginx
</pre>
<p>Deep hey. Lets add the variant. Open it up via:</p>
<pre class="brush: bash; ">
sudo vi Portfile
</pre>
<p>press <code><strong>SHIFT + G</strong></code> this will take you to the end of the file, where you should now insert (via pressing the key i) the following:</p>
<pre class="brush: bash; ">
variant passenger description {Adds passenger support} {
    configure.args-append   --add-module=/opt/local/lib/ruby/gems/1.8/gems/passenger-2.2.5/ext/nginx
}
</pre>
<p>Please take note of the &#8220;/opt/local/lib/ruby/gems/1.8/gems/passenger-2.2.5/ext/nginx&#8221;. This is too important! You need to change that with your path to passenger, but with /etc/nginx added to the end. So if you get any compiling errors, it&#8217;s probably because you didn&#8217;t have enough coffee at this step of the process. Once you&#8217;ve done this hit ESC to get out of insert mode and press <code><strong>SHIFT + ZZ</strong></code> this will save and close the file.</p>
<p>Next its time to install nginx via the macports system. We will also want to add our variation in. So to install nginx with the passenger module type:</p>
<pre class="brush: bash; ">
sudo port install nginx +gzip_static +passenger +ssl
</pre>
<p>Let it do its magic. Once done cd to <code><strong>/opt/local/etc/nginx/</strong></code>. We need to copy the example config files to ones we want to use. So do that.</p>
<pre class="brush: bash; ">
sudo cp mime.types.example mime.types
sudo cp nginx.conf.example nginx.conf
</pre>
<p>So all that is left is to add our passenger config and a site. Open up the nginx.conf file via <code><strong>sudo vi nginx.conf</strong></code></p>
<p>My config file looks like:</p>
<pre class="brush: bash; ">
worker_processes  1;
events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    gzip  on;

    server {
        listen       80;
        server_name  localhost;
        location / {
            root   /opt/local/www/htdocs/;
            index  index.html index.htm;
        }

    }

		passenger_root /opt/local/lib/ruby/gems/1.8/gems/passenger-2.2.5;
		passenger_ruby /opt/local/bin/ruby;
		passenger_max_pool_size 8;
		passenger_max_instances_per_app 1;
		passenger_pool_idle_time 3600;

		server {
			listen 80;
			client_max_body_size 250M;
			server_name dev.app;
			root /opt/local/www/my_app_name/public;
			passenger_enabled on;
			rails_env development;
		}
}
</pre>
<p>What you need to pay attention to here is ther location / for root. I.e where it says /opt/local/www/htdocs/ Make sure that exists. Next thing to look out for is the passenger_root /opt/local/lib/ruby/gems/1.8/gems/passenger-2.2.5; This is where you get your passenger path from <code><strong>passenger-config --root</strong></code> The next thing is passenger_ruby. This setting sets the path to ruby, and also where ruby will grab its gems with. I ran into an error first with passenger, stating that my gems were too old, and that i should update. But when i went into terminal and did gem update &#8211;system it said i had the latest. So it became apparent that passenger was by default using Mac OSX&#8217;s ruby version! So you set the path to the ruby you want, in this case, its the one macports installed.</p>
<p>Anyhow. You might be asking well how do i start, stop and restart MySql or Nginx. Well I&#8217;ve created some aliases you should add to your .profile file. To do this run:</p>
<pre class="brush: bash; ">
vi ~/.profile
</pre>
<p>then add in:</p>
<pre class="brush: bash; ">
alias nginx-start=&quot;sudo launchctl load -w /Library/LaunchDaemons/org.macports.nginx.plist&quot;
alias nginx-stop=&quot;sudo launchctl unload /Library/LaunchDaemons/org.macports.nginx.plist&quot;
alias nginx-restart=&quot;sudo launchctl unload /Library/LaunchDaemons/org.macports.nginx.plist &amp;amp;&amp;amp; sudo launchctl load -w /Library/LaunchDaemons/org.macports.nginx.plist&quot;
alias mysqlstart=&quot;sudo launchctl load -w /Library/LaunchDaemons/org.macports.mysql5.plist&quot;
alias mysqlstop=&quot;sudo launchctl unload -w /Library/LaunchDaemons/org.macports.mysql5.plist&quot;
</pre>
<p>Save the file, then close down terminal and reload it. You should now be able to run nginx-restart, or start, or stop, same with mysql except for restart. Handy?</p>
<p>You should now be able to navigate to http://localhost and see a success screen, or whatever you have in your htdocs folder. Or if you have set up your hosts file in /etc/hosts to route 127.0.0.1 dev.app then it will show your rails app!</p>
<p>If you don&#8217;t you have a problem. tail /opt/local/var/log/nginx/error.log should be able to tell you whats up. Any problems or issues you can&#8217;t work out in the first hour by yourself add a comment![/code]</p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/9dhotaktX4s" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2009/11/08/installing-ruby-on-rails-mysql-imagemagick-nginx-passenger-with-macports/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2009/11/08/installing-ruby-on-rails-mysql-imagemagick-nginx-passenger-with-macports/</feedburner:origLink></item>
		<item>
		<title>What’s Been Doing?</title>
		<link>http://feedproxy.google.com/~r/Jotlab/~3/meNC6Rc_umM/</link>
		<comments>http://www.jotlab.com/2009/10/29/whats-been-doing/#comments</comments>
		<pubDate>Thu, 29 Oct 2009 13:06:24 +0000</pubDate>
		<dc:creator>VoiDeT</dc:creator>
				<category><![CDATA[Ideas]]></category>
		<category><![CDATA[australia]]></category>
		<category><![CDATA[been]]></category>
		<category><![CDATA[brisbane]]></category>
		<category><![CDATA[cake]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[doing]]></category>
		<category><![CDATA[entertainment]]></category>
		<category><![CDATA[i've]]></category>
		<category><![CDATA[life]]></category>
		<category><![CDATA[me]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[stuff]]></category>
		<category><![CDATA[visa]]></category>
		<category><![CDATA[weekend]]></category>
		<category><![CDATA[what]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://www.jotlab.com/?p=4223</guid>
		<description><![CDATA[Well it&#8217;s been a long time since I&#8217;ve posted pretty much anything on my pages of wisdom here. Which pretty much indicates that I&#8217;ve been busy. Very busy, and loving it. I&#8217;ve been buried in books and code. My previous employer got me back off... <span><a href="http://www.jotlab.com/2009/10/29/whats-been-doing/" title="What&#8217;s Been Doing?" rel="bookmark">[+]</a></span>]]></description>
			<content:encoded><![CDATA[<p>Well it&#8217;s been a long time since I&#8217;ve posted pretty much anything on my pages of wisdom here. Which pretty much indicates that I&#8217;ve been busy. Very busy, and loving it. I&#8217;ve been buried in books and code. My previous employer got me back off the job market and has been prepared to take me on for a full time position as a web developer. If you&#8217;ve been following the story on <a href="http://www.twitter.com/voidet">twitter</a> then you will know that I got picked up for a CakePHP developer role through my blog here. That unfortunately fell through because of the demand for work delivered by the company. Which was kind of depressing.</p>
<p>But nevertheless they got back to me, and I&#8217;m doing just as I was before and more. I&#8217;m in a really excellent position. My co-worker is miles ahead of me in terms of experience with web programming and he understands my position and in turn delegates jobs that are just a little outside of my comfort zone, non-stop. So I&#8217;m constantly learning and developing more advanced apps. So yes, I&#8217;m always digging through documentation and buying books. I pretty much get up early in the morning, arrive up to an hour early at work, study some bit with coffee. Work all day, drive home, and study my books!</p>
<p>Otherwise we&#8217;ve been taking it easy. My partner, who is on a working holiday visa here in Australia is finding it very tough to get any sort of job. We started out trying to get her on track with a her desired career path but none of the companies wanted to hear about it, simply because she was not an Australian resident. With that, we have been striving for just about any work, retail, receptionist, office work and even cleaning. She&#8217;s got a degree, a ton of experience, well mannered and presented and just has the best attitude to make things happen, and yet still no luck. If you want to help her out, or know of anyone that could give her a chance that she deserves, so she can stop handing out hundreds of resumés check her out at <a href="http://www.lizzyinbrizzy.com">www.lizzyinbrizzy.com</a>.</p>
<p>What I&#8217;m currently looking into now is source code versioning with Git and setting up git repos and access control via Gitosis. Also learning all the PHP functions i can and understanding how to extend CakePHP methods to be more DRY and MVC endorsed. Also this Lithium by Nate Abele, I do want to check this out sometime soon, however had a quick look at the documentation, and didn&#8217;t really see much there yet. But alas, the weekend is coming up haha</p>
<p>And with that expect a lot more code snippets and reviews of books coming in the near future! As always if you have any questions or comments please let me know, I&#8217;m always up for helping.</p>
<img src="http://feeds.feedburner.com/~r/Jotlab/~4/meNC6Rc_umM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.jotlab.com/2009/10/29/whats-been-doing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.jotlab.com/2009/10/29/whats-been-doing/</feedburner:origLink></item>
	</channel>
</rss><!-- Dynamic page generated in 0.465 seconds. --><!-- Cached page generated by WP-Super-Cache on 2010-09-06 08:51:41 -->
