posts from thechriswalker.nethttp://thechriswalker.net/The opinions here are those of thechriswalker. Mostly CakePHP related. Some general coding ones. Some totally different.MVC framwork - from scratch!http://thechriswalker.net/post/display/mvc-framwork-from-scratch-http://thechriswalker.net/post/display/mvc-framwork-from-scratch-<p>So, I started a new job. And the first thing I get to work with is a large PHP application for managing Wireless Hotspots. The application presents a portal page to people trying to connect and handles payment and creation of the Radius info to allow them to use the service. The PHP element is mainly a large and complicated database frontend, with a few special functions to interface with some other systems. Anyway, the thing was originally written about 5 years ago in PHP 4.2.2 and the code makes me shudder.</p> <p>So, I gave some thought to how I would do it if I was writing the same thing from scratch. I immediately thought "Cake" but then realised that for a commecial application I would want to fully understand all the under-the-hood business and as good as CakePHP is, I don't have that full understanding. So instead I thought about writing a framework from scratch - albeit based on many CakePHP principles.</p> <h3>Requirements</h3> <p>The first thing to think about is what you want the framework to achieve on it's own before we start to think about building the app on top of it. This project has some features which must be available in the framework.</p> <ul> <li><p><strong>Multiple points of entry:</strong></p> <p>The application is accessed from 3 different places, and they will share much code.</p> <ul> <li>The <strong>user</strong> interface is what a person trying to connect to the wireless network would see.</li> <li>The <strong>customer</strong> interface is a Customer self-care portal for a hotspot provider to manage their hotspots.</li> <li>The <strong>admin</strong> interface allows management of the entire system and reporting.</li> </ul></li> <li><p><strong>Multiple Databases:</strong> specifically, their are 2 databases in use, the "main" one and the Radius AAA database.</p></li> <li><p><strong>Authentication and Accesss control:</strong> for the 2 restricted interfaces, this should be ingrained into the framework at a low level, but extendible to accept different authentication mechanisms.</p></li> <li><p><strong>Licensing:</strong> this product may be sold under license so the ability to restrict capabilities is required.</p></li> </ul> <h3>Design</h3> <p>The next think to think about is how the framework will be put together. I leaned heavily on the cake approach in designing the basic structure of the framework. I tried to imagine the flow of a request and what I would need to build to make it work. So I got the following:</p> <ol> <li><strong>Index.php:</strong> accepts the request set's some paths, loads a common "boot.php" file.</li> <li><strong>Boot.php:</strong> loads the generic functions (including a "Loader" class for autoloading other classes), loads some config options from "ini" files, creates a Dispatcher instance and starts the ball rolling.</li> <li><strong>Configure.php:</strong> handles the config options loaded, a "Globals" replacement.</li> <li><strong>Dispatcher.php:</strong> co-ordinates the request. Processes POST data, gets info from Router class, and instantiates our Controller, and creates and renders the View.</li> <li><strong>Router.php:</strong> Router class does forwards and reverse routing, extension parsing and parameter handling.</li> <li><strong>Controller.php:</strong> has methods to set options for the view.</li> <li><strong>View.php:</strong> takes the request options, controller settings and renders the appropriate template / layout.</li> </ol> <p>Then there's all the other useful side classes Session, Cookie, HTML, Money and I haven't even got onto to Models yet.</p> <h3>Next time</h3> <p>I might have made a diagram to explain how this works, or myabe will be ranting about the Model parts, and multiple database access. To be honest to point of this article was not so much about the actual code, but more the structure and how if you break it down, a framework with much of the principles of Cake can be put together relatively easily. Of course I am only half way through, but progress has been quick.</p> <p>In conclusion, I found it extremely interesting to see how the varous part of a framework work together and it is very satisfying to see it all fall into place. I now have the basis of a web-application framework that I understand inside out because I wrote every line of code. That is the benefit. When I use this now I will be supremely confident that I understand every nuance and feature.</p> theChrisWalkerThu, 24 Jul 2008 10:01:17 +0100No more Japanesehttp://thechriswalker.net/post/display/no-more-japanesehttp://thechriswalker.net/post/display/no-more-japanese<p>Not an apocalyptic incident, just that my Japanese course has finished for the summer, which is a shame because I was enjoying it and now I have to keep that momentum up myself before they restart in October.</p> <p>So, I thought I'd write something about it to encourage myself. Also if you live anywhere around Exeter (Devon, UK) then you should sign up at <a href="http://www.exe-coll.ac.uk/Courses/N/n090.aspx">Exeter College</a>, because we could use the numbers and Noriko's classes are excellent.</p> <h3>Learning Japanese</h3> <p>When we first started we were told about "<em>how easy it is to learn Japanese</em>" and some of the arguments are quite convincing. I studied Latin at school, which is about as complex as I thought a language could get. A lot of the things that surprised me about learning Japanese is just <strong>how</strong> different the structure of the language is to anything I had seen before (i.e. Romance and Germanic languages).</p> <h4>Why Japanese is easy to learn:</h4> <ol> <li>There's no articles (i.e. <strong>a</strong> book, or <strong>the</strong> book, just "book")</li> <li>There's no gender or plural. Objects just are, they have no male/female/neuter preferences and the word for one is the word for many.</li> <li>There's only 2 tenses, past or present/future.</li> <li>Questions are formed form statements by simply adding the word "ka" at the end. No re-arranging of words / sentence structure.</li> </ol> <p>Which was great and we're were all thinking how good this was going to be. Then we actually started learning...</p> <h4>Why Japanese is difficult to learn:</h4> <ol> <li>They have 4 differents character sets: <ul> <li><strong>Romaji</strong>: Japanese transliterated into Roman characters</li> <li><strong>Hiragana</strong>: 46 symbols indicating sounds, some of which can be combined, and some of which can be accented, making about 100 sounds. These characters are used to write "Japanese origin" words.</li> <li><strong>Katakana</strong>: A different set of 46 symbols, as above, but used to write words of "non-Japanese origin".</li> <li><strong>Kanji</strong>: the pictograms adapted from Chinese that represent ideas, there are about 2000 standard Kanji, but many, many more exist.</li> </ul></li> <li>The structure of the language is nothing like any language I have learnt. For example they have no verb "to have", so instead of saying: "<em>I have this thing</em>", they say "This thing exists" which doesn't really sound right to me and I can't get my head around it.</li> <li>Verbs are strange. They have 4 basic forms, which I don't really understand at all. <ul> <li>the "dictionary" form is only used in a dictionary as far as I can tell.</li> <li>the "-masu" form is a polite form</li> <li>the "-tai" form indicates a want to do the action of the verb</li> <li>the "-te" form indicates some other meaning, and is also used when using more than one verb in a sentence to "and" them together.</li> </ul></li> </ol> <p>And there's probably some more I can't remember right now. Basically it means that learning is pretty hard for me. I like structure and rules. I leanr languages by understanding the grammar and I don't understand Japanese grammar.</p> <p>This is by no means a critisism of our Sensei, she's been excellent, but because of the complexity and just how different it is, Japanese is not taught by grammar, but in a more fuzzy way, particularly to start with.</p> <p>In turn this then means that I have been learning it for an academic year, yet can say very little, and cannot form anything but the most basic sentence, and only with predefined verbs. It's very frustatrating.</p> <p>On the other hand, it is an absolute joy to be able to read and write in a brand new alphabet and learning hiragana and katakana has been very rewarding.</p> <p>To aid my learning I fell back on my other skill, code, to create a web based Kana Test, to practice learning the Kana. Unfortunately, Julie, on our course completely obliterated any score I was able to manage and now reigns at the top of the scoreboard, no-one able to budge her off...</p> <p>You can try it out if you want: <a href="http://jp.thechriswalker.net/">Hiragana/Katakana Recognition Tests</a></p> <p>Still, I am looking forward to continuing next year, and if I'm up to it, I might enter myself for a GCSE!</p> theChrisWalkerTue, 01 Jul 2008 14:26:07 +0100DarkAuth update, and test app.http://thechriswalker.net/post/display/darkauth-update-and-test-app-http://thechriswalker.net/post/display/darkauth-update-and-test-app-<p>I found another small glitch in DarkAuth, which stemmed from the major bug after CakePHP 1.2 RC1 was released.</p> <p>The <strong>Controller::render()</strong> method has changed, which was integral to DarkAuth's functionality. The change stopped anything from being displayed if Auth was not present or sufficient. To fix it I had to force an <strong>exit()</strong> into the code.</p> <p>This <strong>exit()</strong> then meant that the Component was not setting the <strong>$_DarkAuth</strong> variable at all, if the "login" or "deny" view where being displayed.</p> <p>The updated code fixes this, and has been updated on the <a href="/darkauth-2">DarkAuth</a> page.</p> <p>In addition I introduced a nice utility method, <strong>DarkAuthComponent::getUserId()</strong> which simply the returns the id of the currently auth'd user. Something I needed often.</p> <p>Also I decided it would be good to have a "test application", a simple setup of a working DarkAuth + Cake combo. So I have built one, and you can use it to see how DarkAuth works, and to play around with it to your hearts content!</p> <p>There's 2 versions: one is just the <strong>app/</strong> folder, and the other contains the full CakePHP1.2 RC1 code as well:</p> <ul> <li><a href="/files/DarkAuth.test.app.zip">DarkAuth.test.app.zip</a> just <strong>app/</strong> folder</li> <li><del><a href="/files/DarkAuth.test.inc.cake.zip">DarkAuth.test.inc.cake.zip</a> full example</del> <em>Awaiting Update...</em></li> </ul> <p>I put some useful code in there to help create the database config file and set up the required tables, so just extract and play!</p> theChrisWalkerThu, 26 Jun 2008 12:26:26 +0100Using DarkAuth with Groups and Roleshttp://thechriswalker.net/post/display/using-darkauth-with-groups-and-roleshttp://thechriswalker.net/post/display/using-darkauth-with-groups-and-roles<p>I have recently updated the code in /darkauth-2 to be compliant with the newly released CakePHP 1.2 RC. Only a small fix, but it made me think about DarkAuth and how it works best.</p> <p>When I originally wrote it, I recieved some feedback from several different sources syaing that it was difficult to understand and didn't necessarily work as expected.</p> <p>This was because DarkAuth has a very specific methodology and and is just as much an <em>Access Control</em> component as an Authentication one.</p> <h3>Authentication and Access Control</h3> <ul> <li>Authentication is a method of identifying users.</li> <li>Access Control is a method of restricting those user to what they are allowed to do.</li> </ul> <p>DarkAuth has some useful functions that help with both. In the next release of the code I intend to rename all references to "Group" with "Role" as it is really about Role-Based Access Control.</p> <p>In my usual setup for using DarkAuth I create 5 tables: <strong>users, roles, roles_users, groups, groups_roles</strong></p> <p>The first three are straight forwards, they define the users of the system, any roles that might exist (think roles like "<em>CanAddPosts</em>", "<em>CanEditItems</em>", etc...) and the join table that defines their relationship.</p> <p>The next two is where I define <strong>Groups of Roles</strong>. The whole point of this is simply a helper - I can pick which roles I give to my users explicitly, or I can choose a group which contains a certain array of roles. It just makes it easier to manage. Enough talk, on to an example.</p> <h3>Let's see it it action...</h3> <p>Using the following tables:</p> <div class="geshicodeblock mysql" style="font-size:1em;"><ol><li class="li1"><span class="kw1">CREATE TABLE</span> IF <span class="kw3">NOT</span> <span class="kw1">EXISTS</span> `users` <span class="br0">&#40;</span></li> <li class="li1">&nbsp; `id` <span class="kw2">INT</span><span class="br0">&#40;</span><span class="nu0">11</span><span class="br0">&#41;</span> <span class="kw3">NOT NULL</span> <span class="kw3">AUTO_INCREMENT</span>,</li> <li class="li1">&nbsp; `created` <span class="kw2">DATETIME</span> <span class="kw3">DEFAULT</span> <span class="kw3">NULL</span>,</li> <li class="li1">&nbsp; `modified` <span class="kw2">DATETIME</span> <span class="kw3">DEFAULT</span> <span class="kw3">NULL</span>,</li> <li class="li1">&nbsp; `email` <span class="kw2">VARCHAR</span><span class="br0">&#40;</span><span class="nu0">64</span><span class="br0">&#41;</span> <span class="kw3">NOT NULL</span>,</li> <li class="li1">&nbsp; `pswd` <span class="kw2">VARCHAR</span><span class="br0">&#40;</span><span class="nu0">32</span><span class="br0">&#41;</span> character <span class="kw1">SET</span> utf8 <span class="kw5">COLLATE</span> utf8_bin <span class="kw3">NOT NULL</span>,</li> <li class="li1">&nbsp; `live` <span class="kw2">TINYINT</span><span class="br0">&#40;</span><span class="nu0">1</span><span class="br0">&#41;</span> <span class="kw3">NOT NULL</span> <span class="kw3">DEFAULT</span> <span class="st0">'0'</span>,</li> <li class="li1">&nbsp; <span class="kw1">PRIMARY KEY</span> &nbsp;<span class="br0">&#40;</span>`id`<span class="br0">&#41;</span></li> <li class="li1"><span class="br0">&#41;</span> <span class="kw3">ENGINE</span><span class="sy0">=</span><span class="st0">'MyISAM'</span> &nbsp;<span class="kw3">DEFAULT</span> <span class="kw3">CHARSET</span><span class="sy0">=</span><span class="st0">'utf8'</span>;</li> <li class="li1">&nbsp;</li> <li class="li1"><span class="kw1">CREATE TABLE</span> IF <span class="kw3">NOT</span> <span class="kw1">EXISTS</span> `roles` <span class="br0">&#40;</span></li> <li class="li1">&nbsp; `id` <span class="kw2">INT</span><span class="br0">&#40;</span><span class="nu0">11</span><span class="br0">&#41;</span> <span class="kw3">NOT NULL</span> <span class="kw3">AUTO_INCREMENT</span>,</li> <li class="li1">&nbsp; `name` <span class="kw2">VARCHAR</span><span class="br0">&#40;</span><span class="nu0">32</span><span class="br0">&#41;</span> <span class="kw3">NOT NULL</span>,</li> <li class="li1">&nbsp; <span class="kw1">PRIMARY KEY</span> &nbsp;<span class="br0">&#40;</span>`id`<span class="br0">&#41;</span></li> <li class="li1"><span class="br0">&#41;</span> <span class="kw3">ENGINE</span><span class="sy0">=</span><span class="st0">'MyISAM'</span> &nbsp;<span class="kw3">DEFAULT</span> <span class="kw3">CHARSET</span><span class="sy0">=</span><span class="st0">'utf8'</span>;</li> <li class="li1">&nbsp;</li> <li class="li1"><span class="kw1">CREATE TABLE</span> IF <span class="kw3">NOT</span> <span class="kw1">EXISTS</span> `roles_users` <span class="br0">&#40;</span></li> <li class="li1">&nbsp; `role_id` <span class="kw2">INT</span><span class="br0">&#40;</span><span class="nu0">11</span><span class="br0">&#41;</span> <span class="kw3">NOT NULL</span>,</li> <li class="li1">&nbsp; `user_id` <span class="kw2">INT</span><span class="br0">&#40;</span><span class="nu0">11</span><span class="br0">&#41;</span> <span class="kw3">NOT NULL</span>,</li> <li class="li1">&nbsp; <span class="kw1">KEY</span> `role_id` <span class="br0">&#40;</span>`role_id`,`user_id`<span class="br0">&#41;</span></li> <li class="li1"><span class="br0">&#41;</span> <span class="kw3">ENGINE</span><span class="sy0">=</span><span class="st0">'MyISAM'</span> &nbsp;<span class="kw3">DEFAULT</span> <span class="kw3">CHARSET</span><span class="sy0">=</span><span class="st0">'utf8'</span>;</li> <li class="li1">&nbsp;</li> <li class="li1"><span class="kw1">CREATE TABLE</span> IF <span class="kw3">NOT</span> <span class="kw1">EXISTS</span> `groups` <span class="br0">&#40;</span></li> <li class="li1">&nbsp; `id` <span class="kw2">INT</span><span class="br0">&#40;</span><span class="nu0">11</span><span class="br0">&#41;</span> <span class="kw3">NOT NULL</span> <span class="kw3">AUTO_INCREMENT</span>,</li> <li class="li1">&nbsp; `name` <span class="kw2">VARCHAR</span><span class="br0">&#40;</span><span class="nu0">24</span><span class="br0">&#41;</span> <span class="kw3">NOT NULL</span>,</li> <li class="li1">&nbsp; <span class="kw1">PRIMARY KEY</span> &nbsp;<span class="br0">&#40;</span>`id`<span class="br0">&#41;</span></li> <li class="li1"><span class="br0">&#41;</span> <span class="kw3">ENGINE</span><span class="sy0">=</span><span class="st0">'MyISAM'</span> &nbsp;<span class="kw3">DEFAULT</span> <span class="kw3">CHARSET</span><span class="sy0">=</span><span class="st0">'utf8'</span>;</li> <li class="li1">&nbsp;</li> <li class="li1"><span class="kw1">CREATE TABLE</span> IF <span class="kw3">NOT</span> <span class="kw1">EXISTS</span> `groups_roles` <span class="br0">&#40;</span></li> <li class="li1">&nbsp; `group_id` <span class="kw2">INT</span><span class="br0">&#40;</span><span class="nu0">11</span><span class="br0">&#41;</span> <span class="kw3">NOT NULL</span>,</li> <li class="li1">&nbsp; `role_id` <span class="kw2">INT</span><span class="br0">&#40;</span><span class="nu0">11</span><span class="br0">&#41;</span> <span class="kw3">NOT NULL</span>,</li> <li class="li1">&nbsp; <span class="kw1">KEY</span> `group_id` <span class="br0">&#40;</span>`group_id`,`role_id`<span class="br0">&#41;</span></li> <li class="li1"><span class="br0">&#41;</span> <span class="kw3">ENGINE</span><span class="sy0">=</span><span class="st0">'MyISAM'</span> &nbsp;<span class="kw3">DEFAULT</span> <span class="kw3">CHARSET</span><span class="sy0">=</span><span class="st0">'utf8'</span>;</li></ol></div> <p>And with some sample data:</p> <div class="geshicodeblock mysql" style="font-size:1em;"><ol><li class="li1"><span class="kw1">INSERT</span> <span class="kw1">INTO</span> `users` <span class="kw1">VALUES</span> <span class="br0">&#40;</span><span class="nu0">1</span>,<span class="kw1">NOW</span><span class="br0">&#40;</span><span class="br0">&#41;</span>,<span class="kw1">NOW</span><span class="br0">&#40;</span><span class="br0">&#41;</span>,<span class="st0">'reader@example.com'</span>,<span class="st0">'5f5a3325bce31f27472ececf3f9aee9a'</span>,<span class="nu0">1</span><span class="br0">&#41;</span>, &nbsp;</li> <li class="li1"><span class="br0">&#40;</span><span class="nu0">2</span>,<span class="kw1">NOW</span><span class="br0">&#40;</span><span class="br0">&#41;</span>,<span class="kw1">NOW</span><span class="br0">&#40;</span><span class="br0">&#41;</span>,<span class="st0">'writer@example.com'</span>,<span class="st0">'5f5a3325bce31f27472ececf3f9aee9a'</span>,<span class="nu0">1</span><span class="br0">&#41;</span>,</li> <li class="li1"><span class="br0">&#40;</span><span class="nu0">3</span>,<span class="kw1">NOW</span><span class="br0">&#40;</span><span class="br0">&#41;</span>,<span class="kw1">NOW</span><span class="br0">&#40;</span><span class="br0">&#41;</span>,<span class="st0">'moderator@example.com'</span>,<span class="st0">'5f5a3325bce31f27472ececf3f9aee9a'</span>,<span class="nu0">1</span><span class="br0">&#41;</span>;</li> <li class="li1"><span class="kw1">INSERT</span> <span class="kw1">INTO</span> `roles` <span class="kw1">VALUES</span> <span class="br0">&#40;</span><span class="nu0">1</span>, <span class="st0">'Root'</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">2</span>, <span class="st0">'CanAddPosts'</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">3</span>, <span class="st0">'CanModeratePosts'</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">4</span>, <span class="st0">'CanEditOwnPosts'</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">5</span>, <span class="st0">'CanEditOtherPeoplesPosts'</span><span class="br0">&#41;</span>;</li> <li class="li1"><span class="kw1">INSERT</span> <span class="kw1">INTO</span> `groups` <span class="kw1">VALUES</span> <span class="br0">&#40;</span><span class="nu0">1</span>, <span class="st0">'Reader'</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">2</span>, <span class="st0">'Writer'</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">3</span>, <span class="st0">'Moderator'</span><span class="br0">&#41;</span>;</li> <li class="li1"><span class="co1">-- Now the clever bit:</span></li> <li class="li1"><span class="kw1">INSERT</span> <span class="kw1">INTO</span> `groups_roles` <span class="kw1">VALUES</span> <span class="br0">&#40;</span><span class="nu0">2</span>, <span class="nu0">2</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">2</span>, <span class="nu0">4</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">3</span>, <span class="nu0">2</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">3</span>, <span class="nu0">3</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">3</span>, <span class="nu0">4</span><span class="br0">&#41;</span>, <span class="br0">&#40;</span><span class="nu0">3</span>, <span class="nu0">5</span><span class="br0">&#41;</span>;</li></ol></div> <p>Which creates a mapping of roles to groups. We can then create a function which applies the given set of roles to the user. I put mine in GroupsController for lack of a better place, but I am not sure it should stay there. It goes like this:</p> <div class="geshicodeblock php" style="font-size:1em;"><ol><li class="li1"><span class="kw1">function</span> _applyGroup<span class="br0">&#40;</span><span class="re1">$user_id</span><span class="sy0">,</span><span class="re1">$group_id</span><span class="br0">&#41;</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; &nbsp; <span class="re1">$data</span><span class="br0">&#91;</span><span class="st0">'User'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">'id'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re1">$user_id</span><span class="sy0">;</span></li> <li class="li1">&nbsp; &nbsp; <span class="re1">$data</span><span class="br0">&#91;</span><span class="st0">'Role'</span><span class="br0">&#93;</span><span class="br0">&#91;</span><span class="st0">'Role'</span><span class="br0">&#93;</span> <span class="sy0">=</span> <a href="http://www.php.net/array_keys"><span class="kw3">array_keys</span></a><span class="br0">&#40;</span><span class="re1">$this</span><span class="sy0">-&gt;</span><span class="me1">Group</span><span class="sy0">-&gt;</span><span class="me1">GroupsRole</span><span class="sy0">-&gt;</span><span class="me1">find</span><span class="br0">&#40;</span><span class="st0">'list'</span><span class="sy0">,</span> <a href="http://www.php.net/array"><span class="kw3">array</span></a><span class="br0">&#40;</span><span class="st0">'conditions'</span><span class="sy0">=&gt;</span>array<span class="br0">&#40;</span><span class="st0">'group_id'</span><span class="sy0">=&gt;</span><span class="re1">$group_id</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st0">'fields'</span><span class="sy0">=&gt;</span><span class="st0">'GroupsRole.role_id,GroupsRole.group_id'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></li> <li class="li1">&nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span><span class="re1">$this</span><span class="sy0">-&gt;</span><span class="me1">User</span><span class="sy0">-&gt;</span><span class="me1">exists</span><span class="br0">&#40;</span><span class="re1">$user_id</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span><span class="re1">$this</span><span class="sy0">-&gt;</span><span class="me1">User</span><span class="sy0">-&gt;</span><span class="me1">save</span><span class="br0">&#40;</span><span class="re1">$data</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">//Success!</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">true</span><span class="sy0">;</span> &nbsp; &nbsp; &nbsp; &nbsp;</li> <li class="li1">&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="kw1">else</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">//failure :(</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">false</span><span class="sy0">;</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></li> <li class="li1">&nbsp; &nbsp; <span class="br0">&#125;</span><span class="kw1">else</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">//failure, no user found...</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">return</span> <span class="kw2">false</span><span class="sy0">;</span></li> <li class="li1">&nbsp; &nbsp; <span class="br0">&#125;</span></li></ol></div> <p>This function will then allow you to align your users' access to fit all the roles you have associated with the group.</p> <p>This brings a question, should I links my groups with users directly? and read their security roles from the group. Or do I simply use the groups to define set of roles to Apply to users as I want?</p> <p>I don't know.</p> <p>The first sounds better, but then we couldn't do any bespoke customisations (i.e. one-off adding of roles to particular users). The second is what I currently use, but if you update a group the use's dont' automatically update.</p> <p>I guess there's an answer in there that you update all users' permission's that match the previous role set of a group to the new role set whenever you change a group. Confused? hopefully not too much...</p> theChrisWalkerTue, 10 Jun 2008 16:56:08 +0100GeSHI: Syntax Highlightinghttp://thechriswalker.net/post/display/geshi-syntax-highlightinghttp://thechriswalker.net/post/display/geshi-syntax-highlighting<p>I wanted to have a syntax highlighting facility for this site as I knew there would be plenty of code on here. First I just used PHP's <strong>highlight_string</strong> function, but soon realised it's drawbacks - not XHTML compliant, no line numbers, only works for PHP...</p> <p>So I did a bit of Googling and found two major contenders, the <strong><a href="http://pear.php.net/package/Text_Highlighter" title="Visit the PEAR::Package::Text_Highlighter website">PEAR::Package::Text_Highlighter</a></strong> library, and <strong><a href="http://qbnz.com/highlighter/" title="Visit the GeSHI website">GeSHI</a></strong>. The PEAR one looks a little more complex and bulky, but GeSHI seemed perfect.</p> <p>So I started playing with GeSHI and found it incredibly easy to get to grips with. The highlighting was very customiseable and it natively supported more languages than I would ever need.</p> <p>The main issue I have with GeSHI was that it killed my lovely lined display. for some reason I couldn't get it to follow the lines. Eventually I realised that it was a Firefox / IE issue, with IE behaving and Firefox being odd (?!). I didn't see that one coming. However I found that by removing all of the inline styles and using CSS instead I managed to allieviate this issue.</p> <p>Now I have the facility to syntax highlight code in any one of the 53 odd languages GeSHI supports. Pretty nice!</p> <p>The next step was to integrate this with my existing CakePHP text-to-html converting helper. I created some new BBCode tags like <code>[code syntax='javascript']</code> to make it easy.</p> theChrisWalkerThu, 05 Jun 2008 09:27:22 +0100Bjax: A Lightweight AJAX helperhttp://thechriswalker.net/post/display/bjax-a-lightweight-ajax-helperhttp://thechriswalker.net/post/display/bjax-a-lightweight-ajax-helper<p>Before I embraced frameworks, I created an <abbr title='Asynchronous Javascript And XML'>AJAX</abbr> Object that was lightweight, easy to use and efficient. I still find it very useful when making small, standalone, single-page type web-apps and certainly proved invaluable when I wasn't.</p> <p>It's usage is very simple, first include the library:</p> <div class="geshicodeblock javascript" style="font-size:1em;"><ol><li class="li1"><span class="sy0">&lt;</span>script type=<span class="st0">'text/javascript'</span> src=<span class="st0">'/js/bjax.js'</span><span class="sy0">&gt;&lt;/</span>script<span class="sy0">&gt;</span></li></ol></div> <p>Then define what you want to do and away:</p> <div class="geshicodeblock javascript" style="font-size:1em;"><ol><li class="li1"><span class="sy0">&lt;</span>script type=<span class="st0">'text/javascript'</span><span class="sy0">&gt;</span></li> <li class="li1"><span class="co1">// &lt;![CDATA[</span></li> <li class="li1"><span class="kw2">function</span> responseHandler<span class="br0">&#40;</span> data <span class="br0">&#41;</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; <span class="kw3">alert</span><span class="br0">&#40;</span><span class="st0">&quot;the response was&quot;</span> + data.<span class="me1">responseText</span><span class="br0">&#41;</span>;</li> <li class="li1"><span class="br0">&#125;</span></li> <li class="li1">&nbsp;</li> <li class="li1">window.<span class="kw3">onload</span> = <span class="kw2">function</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; Bjax.<span class="me1">go</span><span class="br0">&#40;</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'/somefile.htm'</span>,</li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">null</span>,</li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span> <span class="st0">&quot;onSuccess&quot;</span> : responseHandler <span class="br0">&#125;</span></li> <li class="li1">&nbsp; <span class="br0">&#41;</span>;</li> <li class="li1"><span class="br0">&#125;</span></li> <li class="li1"><span class="co1">// ]]&gt;</span></li> <li class="li1"><span class="sy0">&lt;/</span>script<span class="sy0">&gt;</span></li></ol></div> <p>The code is nice and efficient, killing and reusing the XMLHttpRequest Objects (or the ActiveX versions of them). So you don't need to worry about it at all.</p> <p>The <strong>go</strong> function takes the follow options:</p> <div class="geshicodeblock javascript" style="font-size:1em;"><ol><li class="li1">Bjax.<span class="me1">go</span><span class="br0">&#40;</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">'http://domain.com/path/to/file.ext'</span>, <span class="co1">//The URL to Request</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="st0">'key1=value1&amp;key2=value2...'</span>, <span class="co1">//Post data, correctly URL encoded</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#123;</span> &nbsp;<span class="co1">//Object literal containing options</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'onSuccess'</span> : <span class="kw2">function</span>, <span class="co1">//function to be called after successful response.</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'onSend'</span> : <span class="kw2">function</span>, <span class="co1">//function to be called on sending request</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'onError'</span> : <span class="kw2">function</span>, <span class="co1">//function to be called if a non 2xx http response code</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'onTimeout'</span> : <span class="kw2">function</span>, <span class="co1">//function to be called if request times out.</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'timeout'</span> : integer, <span class="co1">//this is the number of seconds before we consider a request timed out (default 15)</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'method'</span> : <span class="st0">&quot;string&quot;</span> <span class="co1">// this sets the request method, defaults to &quot;POST&quot;</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></li> <li class="li1">&nbsp; <span class="br0">&#41;</span>;</li></ol></div> <p>If things go wrong, you can debug the AJAX requests easily using the "debug" flag. It will then alert at various stages of the process allowing you to easily see what's going on. To enable, simply set <strong>Bjax.debug</strong> to true:</p> <div class="geshicodeblock javascript" style="font-size:1em;"><ol><li class="li1">Bjax.<span class="me1">debug</span> = <span class="kw2">true</span>;</li></ol></div> <p>There's also included a really useful function prototype, which I took the idea from from the <a href="http://www.prototypejs.org">Prototype</a> library. The prototype is <strong>bind</strong> and always you to <em>bind</em> functions to objects so that when the AJAX response comes back, you can call a function in the context you where in when it left off!</p> <p>If you understood that last paragraph then you know how useful that is, if you didn't, then you will find that you need this functionality at some stage or other...</p> <p>Anyway, the source is available here: <a href="/files/bjax.js">bjax.js (3782B)</a> or the minified version (with debugging removed) <a href="/files/bjax.min.js">bjax.min.js (2406B)</a></p> theChrisWalkerFri, 23 May 2008 15:50:09 +0100DarkAuth: Authentication for CakePHPhttp://thechriswalker.net/post/display/darkauth-authentication-for-cakephphttp://thechriswalker.net/post/display/darkauth-authentication-for-cakephp<p>As I had already written some articles (hard coded too...) I figured it would probably be a good idea to link to them directly. The first is an authentication plugin for CakePHP, DarkAuth.</p> <p>I wrote DarkAuth as an alternative to the inbuilt "Auth" authentication system in CakePHP 1.2. I never really looked at "Auth" so cannot comment on how good it is. I <strong>can</strong> say that DarkAuth works for me and how I want it to.</p> <h3>"How I want it to."</h3> <p>That's right, how <strong>I</strong> want it to. It may be perfect for you, or it may just make your life difficult. That being said, let me walk you through the concepts, then give you the links to the article.</p> <p>DarkAuth is designed around <strong>role-based</strong> authentication for actions. I call them "groups" in the tutorial but more often than not I consider them access roles. This means that if you are not using <abbr title='Has And Belongs To Many'>HABTM</abbr> associations in your app, DarkAuth probably isn't for you. the exception to that is if you don't use groups at all, then it's probably quite good!</p> <p>The other "feature" of DarkAuth is that you don't have to create a specific "<em>login</em>" URL. If auth is required by your app on a page and the current user is not authenticated then the login is auto<em>magic</em>ally displayed. If the user is logged but doesn't have the correct "role" or "group" then the access denied page is shown instead.</p> <p>It's quite clever really and allows you to do things like this:</p> <div class="geshicodeblock php" style="font-size:1em;"><ol><li class="li1"><span class="kw1">&lt;?php</span></li> <li class="li1"><span class="kw1">class</span> ExamplesController <span class="kw1">extends</span> AppController <span class="br0">&#123;</span></li> <li class="li1">&nbsp; <span class="kw1">var</span> <span class="re1">$name</span> <span class="sy0">=</span> <span class="st0">'Examples'</span><span class="sy0">;</span></li> <li class="li1">&nbsp; </li> <li class="li1">&nbsp; <span class="kw1">function</span> index<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; &nbsp; <span class="co1">// This function is un-restricted.</span></li> <li class="li1">&nbsp; <span class="br0">&#125;</span></li> <li class="li1">&nbsp;</li> <li class="li1">&nbsp; <span class="kw1">function</span> secret<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; &nbsp; <span class="co1">// this function is Auth secured.</span></li> <li class="li1">&nbsp; &nbsp; <span class="re1">$this</span><span class="sy0">-&gt;</span><span class="me1">DarkAuth</span><span class="sy0">-&gt;</span><span class="me1">requiresAuth</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></li> <li class="li1">&nbsp; <span class="br0">&#125;</span></li> <li class="li1">&nbsp;</li> <li class="li1">&nbsp; <span class="kw1">function</span> roleOnly<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; &nbsp; <span class="co1">// this function require the &quot;can_do_this&quot; role</span></li> <li class="li1">&nbsp; &nbsp; <span class="re1">$this</span><span class="sy0">-&gt;</span><span class="me1">DarkAuth</span><span class="sy0">-&gt;</span><span class="me1">requiresAuth</span><span class="br0">&#40;</span><span class="st0">&quot;can_do_this&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span></li> <li class="li1">&nbsp; <span class="br0">&#125;</span></li> <li class="li1">&nbsp;</li> <li class="li1">&nbsp; <span class="kw1">function</span> variedResult<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; &nbsp; <span class="co1">// this function does one thing is auth and another if not.</span></li> <li class="li1">&nbsp; &nbsp; <span class="kw1">if</span><span class="br0">&#40;</span><span class="re1">$this</span><span class="sy0">-&gt;</span><span class="me1">DarkAuth</span><span class="sy0">-&gt;</span><span class="me1">isAllowed</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">// do something</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; &nbsp;<span class="re1">$this</span><span class="sy0">-&gt;</span><span class="me1">set</span><span class="br0">&#40;</span><span class="st0">'some_data'</span><span class="sy0">,</span><span class="st0">'User Authorised!'</span><span class="br0">&#41;</span><span class="sy0">;</span></li> <li class="li1">&nbsp; &nbsp; <span class="br0">&#125;</span><span class="kw1">else</span><span class="br0">&#123;</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; <span class="co1">// do something else...</span></li> <li class="li1">&nbsp; &nbsp; &nbsp; <span class="re1">$this</span><span class="sy0">-&gt;</span><span class="me1">set</span><span class="br0">&#40;</span><span class="st0">'some_data'</span><span class="sy0">,</span><span class="st0">'No Auth!'</span><span class="br0">&#41;</span><span class="sy0">;</span></li> <li class="li1">&nbsp; &nbsp; <span class="br0">&#125;</span></li> <li class="li1">&nbsp; <span class="br0">&#125;</span></li> <li class="li1"><span class="br0">&#125;</span></li> <li class="li1"><span class="kw1">?&gt;</span></li></ol></div> <p>So anyway, the reason you're reading this. The article and source code is on this website at <a href="/darkauth-1">/darkauth-1</a> and also <a href="http://bakery.cakephp.org/articles/view/darkauth-v1-3-an-auth-component">at the bakery</a>.</p> <p>Hope you like it.</p> theChrisWalkerWed, 21 May 2008 14:14:05 +0100The first posthttp://thechriswalker.net/post/display/the-first-posthttp://thechriswalker.net/post/display/the-first-post<p>What an incredibly original title for this - my first post.</p> <p>Well, I figured that it covered the main point of this entry, that is to introduce myself and this website. My name is Chris. I live near Exeter, Devon. I like to surf, am enjoying learning Japanese and am a bit of a techie. I enjoy building websites and learning the technologies behind them.</p> <p>This blog is my second major attempt to keep my own website. The first, <a href="http://modbychris.net/">ModByChris.net</a>, was OK, but not built to any rigorous design standards, had a poor <em>lightbox</em> effect for images and more importantly, I didn't update it nearly as frequently as I should have.</p> <p>This time thing will be different! This time the pages will not be solely focussed around what I have been doing (i.e. personal diary type posts), and be more focussed around the technical side of things: experiements, tutorials and experiences. Thus these articles may actually end up being of some use to someone (?) and, if not even that, a good back-reference for me. That is not so say I won't post about how amazing the surf was last weekend, or some pictures of random events in my life (I may even build a simple gallery!), but they will probably be less common.</p> <p>At least, this is plan now. Time shall tell how it happens...</p> <p>I have been building this site over a fair while now, much longer than expected, due to the fact I have been trying to move house, jobs and organise a wedding all at once. But now it is here and I aim to write here at least once a week. The next step is to build a comments system so if anyone actually reads it, they can leave comments and there can be discussion.</p> theChrisWalkerTue, 20 May 2008 15:23:24 +0100