NO MORE CAREER
POLITICIANS!
Get Out Of Our House: Replacing congress with TRUE citizens!
Contact Doug!
Learn About Doug!
View Doug Boude's online resume
updated 11/18/2009

View Doug Boude's profile on LinkedIn
Link to me!

Follow Doug Boude on Twitter
Follow me!

Be Doug's friend on Facebook
Befriend me!
(I promise not to follow you home)
OO Lexicon
Chat with Doug!
Recent Entries
You may also be interested in...
Web Hosting

best web hosting - top web hosting sites, thetop10bestwebhosting.com

Czech your Page Rank!
Check Page Rank of any web site pages instantly:
This free page rank checking tool is powered by Page Rank Checker service
Surf's Up!
Visit Egosurf.org and massage YOUR web ego!
My Score: 9,001
Doug's Books

Read (and recommend)

  • Men are from Mars, Women are from Venus
  • The Wisdom of Crowds: Why the Many Are Smarter Than the Few and How Collective Wisdom Shapes Business, Economies, Societies and Nations
  • Blink: The Power of Thinking Without Thinking
  • Head First Design Patterns
  • Transact-SQL Programming
  • What's So Amazing About Grace?
  • Just So Stories (Rudyard Kipling collection)

Reading

  • Prayer: Does it Make Any Difference?
  • Data Mining (Practical Machine Learning Tools and Techniques)
<< September, 2010 >>
SMTWTFS
1234
567891011
12131415161718
19202122232425
2627282930
Search Blog

Recent Comments
Re: Railo 3.1 on Windows Server 2008 and IIS7 - Part 3 of 3 (by Jon at 8/27 2:04 PM)
Re: Hosts File Changes Not Acknowledged on Vista 64 (by Spacy at 8/24 3:46 PM)
Re: THE DAY CFUNITED DIED (by ComboFusion at 8/23 10:50 AM)
Re: My Grandpa (by Tasha at 8/10 4:29 PM)
Re: Just What IS a 'Service Layer', Anyway? (by dougboude at 8/02 10:10 AM)
Re: Just What IS a 'Service Layer', Anyway? (by Isaac at 8/02 2:25 AM)
Re: PayPal IPN Coldfusion CFC (by Soyestudiambre at 7/25 6:12 PM)
Re: PHP vs COLDFUSION (by Tony Garcia at 7/17 11:24 AM)
Re: PHP vs COLDFUSION (by dougboude at 7/14 8:45 AM)
Re: PHP vs COLDFUSION (by Lola LB at 7/14 5:51 AM)
Categories
Archives
Photo Albums
Funnies (5)
Family (3)
RSS

Powered by
BlogCFM v1.11

17 November 2009
Coldbox 101 Training Review

This past weekend I had the privilege of attending Ortus Solution's Coldbox 101 training class in Grapevine, Texas. It is true that I did pay for the opportunity, but the price was obscured and more than justified by the quantity, quality, and cohesiveness of the knowledge that was shared. Coldbox's master architect himself, Luis Majano, was the instructor, and it was with the same level of zeal and attention to detail that he puts into his framework that he also enlightened and empowered his students on Coldbox. In this case, the "no student left behind" policy was absolutely successful. With a student body composed of everyone from absolute CF beginner to CF expert, and from designer to "design-blind", Luis ensured that the content was both palatable and applicable to one and all.

I came to the class being what I would best describe as semi-proficient in Coldbox. I've written a large Coldbox app, done a few Coldbox presentations, blogged about some aspects of Coldbox, and was even hired recently to present a full day instructional course on Coldbox to a local group of San Antonio developers. But, as with any subject that one teaches themselves, there are always gaps in the knowledge you glean from reading docs and your own experiences. It was these gaps I came to the training hoping to fill, and fill them I did. I was given solid insight into how the framework itself is architected, allowing me to fully visualize the request lifecycle and the framework components that interact with and manipulate it; I honed my knowledge of ALL of Coldbox's core concepts, such as interceptors, views,layouts, viewlets, and plugins; I gained insight into the workflow and approaches that Luis himself uses on a daily basis; and my mind was opened up to a whole new facet of the development lifecycle that I personally have always avoided: unit testing.

Coldbox is SO keen on the idea of unit/integration testing that it has the built in ability to simulate itself! In a nutshell, Coldbox makes it VERY simple and easy to test entire events (the equivalent of fuseactions, if that's a more relevant term for you) without the need for the developer to write any additional code. It was truly beautiful to see integration and unit tests run, and just as easy to write them.

I also had an opportunity to flip through the pre-copy of the official Coldbox book, and I must say I had a hard time handing it off to the next student. The Table of Contents was lengthy and robust, and the associated content was clear, concise, and easy to read. Oh, and it is in color! The plethora of code snippets really jump out at you, and  highlighted items are truly highlighted. I would say that the book is definitely one that you will want to have on your desk as a reference. If I understood correctly, we should be seeing it released into the wild in short order. :)


The excitement that new knowledge brings is great to those of us who love it and derive our livelihood from it, and I could easily spend a day sharing all of the very useful information and understandings I received from attending this class. I wasn't asked not to, nor was I asked to sign any NDAs; but for the sake of honoring the information that Luis earns some of his sustenance from, I'll refrain from divulging any more details of the class. I will, however, leave you with this thought...


If you are doing Coldbox development and you have not attended Luis' Coldbox 101 training class, I guarantee you that what you are writing is not all that it could be. It won't be architected as well as it could be, it won't be leveraging all of the shortcuts and features that it could be, and it won't be top of the line. That isn't to say that you aren't capable of writing a good Coldbox app, but without the in-depth understandings that this class offers, your app will very likely not be a great Coldbox app.

It's challenging to get department heads to budget in training sometimes, but my fellow developers, if your shop is or is considering being a Coldbox shop, then it is worth the effort to justify this training to them. I typically ask myself the question, "if it was my  money, would I spend it on this?", and regarding this class the answer is an unequivocable YES! If you would like any additional input to help justify the training, I and I'm sure anybody else who attended this training class would be more than happy to provide our own personal feedback and testimonial.

If you are serious about writing solid, load-bearing apps in Coldbox, do this for yourself: attend Coldbox 101 training.

Doug out.

Coldbox Certification

Posted by dougboude at 10:33 AM | PRINT THIS POST! | Link | 2 comments



11 June 2009
My Twelve Steps to a Coldbox App

Yesterday I had the privilege of providing my first consultation job as an instructor on the subject of Coldbox. The team that hired me are at the stage in their project where they're ready to start writing code (database is designed, UML and usecase diagrams are complete, mockups have been created), and they wanted to "experience" my thought processes, the questions I ask myself, my approaches to implementing functionality, and how I troubleshoot as I make my way from an idea to a working app in Coldbox. So, we spent the entire day together building an app from scratch, using Coldbox.

In preparation for the task, I spent the past week investing a LOT of time re-reading the Coldbox docs, experimenting, building out a small sample application that the team and I scoped out beforehand, and making notes about my thought/decision-making processes along the way.

Taking notes about your own personal development process is actually quite interesting and insightful, and since my audience found it useful and enlightening, I thought I'd share a high level view of it with the rest of the community as well. What follows is a bullet point list of how I personally generally go from idea to app.

Let's take a walk in my head, shall we? Don't be afraid....

1. let's get the current version of the coldbox framework (www.coldboxframework.com)


2. grab copy of the skeleton app template from the Coldbox framework download (coldbox\ApplicationTemplate)


3. make sure I have a mapping to Coldbox (mine is in application.cfc using this.mappings[...)


4. browse to the skeleton app and make sure that I get the pre-canned "You are now running Renew version 2.6.3..." home page.


5. Now, let's make a grocery list! What do I mean?....
The Coldbox framework (really more of a robust toolkit) does a LOT...which aspects, features, abilities do we want to incorporate into OUR application? Until you know what Coldbox has to offer, you can't answer this question. So, read through the online wiki my friend; open the ColdboxCheatSheet.pdf that comes in the framework's 'install' directory and take a good gander at all of the methods, plugins, interceptors, and other items listed there; Open up the Coldbox.xml.cfm file and just read the different settings that are listed there; Peruse the framework's directory structure, its config files, plugins, interceptors, autowiring, model management, views and layouts, usage of convention. As you learn about each new aspect, think about the app you are building and decide if it is one of the features you want to be sure and incorporate.


For this particular application, here's the grocery list I came up with when considering app features with framework features:

  • I will use the built in Coldbox model management as opposed to a third party IOC framework;
  • I will not use an  ORM framework for this app (though if the project warranted one, I most definitely would!);
  • I will be using Ajax so I'll want to take advantage of Coldbox's multiple layout ability;
  • I will be using my own security interceptor instead of the one that comes with Coldbox;
  • I will not use the environments interceptor;
  • I will be sure and enable the sidebar debugger;
  • I will make exclusive use of the MessageBox plugin for all system messaging

6. Okay, now I'll take a first pass through my config.xml.cfm file and alter the settings I know about at this point.

  • Enable model setter injection;
  • add custom settings for my javascript library path, my css path, my images path;
  • define the datasource object;
  • and enable the sidebar interceptor.

7. At this point, I feel the need to go ahead and create my database table structure and get that to a solid starting point.

8. Let's tweek the default layout and make placeholders for the different components it must organize (header, footer, nav, content, messages, dynamic content area, etc.).

9. Now i want to replace the pre-canned default view with one of my own...my app's own "you are here" landing page. On that page I'm now going to take the time to output lots of global data that I'll likely need to know how to get at later, like the datasource name, the name of the current event, etc. I will use this output as a cut-and-paste reference later.

10. Let's add some security with simulated authentication! Get a basic "login/logout" form to work without actually doing any database calls. The objective here is to just get the appropriate session variables in place, get them added to the Event object at the appropriate time, make sure our events are being intercepted and examined by security code at the appropriate places, and that rendered output is based on authentication status where it makes sense. Once it all works in simulation, add in the needed model objects to perform actual authentication and replace all simulation code.


11. Now I will focus on navigation (if it's anything but simple nav, such as database-driven, hierarchical nav) and ensure that all code is in place to create and output navigation appropriate for the user and state.


12. From here on out, I simply start going down the list of remaining functionality, typically ordered from "least specific" to "most specific", giving all end-user functionality priority over administrative functionality. The easy way to do this is to simply work my way through my navigation items, building out one at a time. To illustrate in more detail how I build out a specific piece of functionality (we'll say a CRUD operation), here's the check list:

  • build the handler with basic event functions to handle our nav links
  • build out the view page for managing an item
  • add a function for saving an item that saves then returns to the calling event
  • create a model service for the item...wire it up(using Coldbox's Model Integration feature
  • add the model service to the modelmapping.cfm
  • wire up the handler, finish up the handler functions that call the corresponding/appropriate service object's methods


In a nutshell, this is my general thought process as I build an app, with emphasis on building a Coldbox app. How does my "to do" list match up with everybody else's? I'd be interested to know!

Posted by dougboude at 10:58 AM | PRINT THIS POST! | Link | 5 comments
04 February 2009
Presented My First Talk on ColdBox Tonight!

In case anybody wanted to attend but was unable, I just wanted to make it known that I did present at the Alamo Area ColdFusion Usergroup meeting tonight on the topic of the ColdBox framework. I was given 45 minutes, and finished precisely within my time limits, except for a few questions at the end that I had to field.

A link to the recorded presentation can be found at UG TV ( http://www.carehart.org/ugtv/ ), and is titled "A 45 Minute Discourse on the Subject of ColdBox". You'll have to try and overlook the slightly skewed sound and video tracks...just something we seem to have to live with when using Connect.

I am very interested to know what people think of the content, and any critiques that might help me improve my presentations in the future. With broad topics and limited time, it's hard to know where to focus sometimes.

Thanks!

Doug  :0)

Posted by dougboude at 12:41 AM | PRINT THIS POST! | Link | 1 comment
22 January 2009
Special Character/Unicode Issue in Ajax Data Retrieval

My most recent project has caused me to have to be "unicode aware" at times (something I've never had to do before), and so I am learning a lot about encoding and display of special characters as I go along. My latest challenge related to this topic involved a User Manager section I created, wherein the users could very well have names that contain special characters (foreign names). This particular section performs its updates, deletes, and inserts via Ajax calls and client-side JS manipulation of a JSON data set. My Ajax is performed via the Prototype library, my code is all ColdFusion living within the Coldbox framework, I'm using Coldspring to manage my object relationships, Transfer is my ORM, and my backend database is MSSQL 2005.

The Challenge: Data that contained special characters was being successfully inserted/updated via my Ajax calls, but the JSON data set returned via those calls did NOT contain those special characters (or contained an incorrect interpretation of them, like numbers, question marks, etc.). A quick check of the database verified that the data was indeed stored in the tables properly.

Setting the Stage
At this stage in the game for me, the smorgasbord of terms, acronyms, and concepts revolving around properly handling unicode is a bit foggy for me. (On a side note, I WISH someone who has the full understanding would put together a simple "checklist" of "Things you need to do in order to handle special characters in ColdFusion"!) From what I currently understand, the physical template you write has to be "encoded" properly (set within the IDE you are using); The database you are using has to have the proper encoding(called Collation in MSSQL 2005); The fields in your table have to be of the proper type to store unicode text(ie: 'nVarchar' instead of 'Varchar', etc.); your browser has to have the proper languages associated in order to display certain sets of special characters(Tools/Internet Options/Languages in IE); your JS functions, if living in a separate file, must have the page encoded properly(again, via your IDE); your ColdFusion datasource has to have the checkbox for "Enable High ASCII characters and Unicode for data sources configured for non-Latin characters " checked; and to top it all off, after having handled all of that, your JS functionality still yet needs to have ITS encoding types set in the proper place.

That sounds like a LOT of fiery hoops just to be able to deal with special characters, right? Well I'm with ya...it's on the verge of being a nightmare for someone who's never had to deal with it. And I do realize that for some of you reading this, the first time YOU tried to deal with special characters, everything just frickin "worked right out of the box" and you probably didn't have to do but one or two of those things, at the most. I say that you got lucky that things were configured just so for the particular character set you were dealing with, and even though from your perspective it didn't seem like that big of a deal, the fact is having an understanding of what's going on behind the scenes can be pretty doggone important anyway, just in case you suddenly get the directive to start storing characters from some other encoding scheme that you AREN'T prepared for out of the box.

Okay, so back to my challenge. Here's the nutshell of how my process flows:

My initial page load is provided with a query of all of the users in the system. That query is then translated to a JS object using a line in my template like the following:

<script>
 //make our initial data set available to JS...
 var objUsers = <cfoutput>#serializeJSON(qryUsers,true)#</cfoutput>;
....

When a user is chosen for edit, I load up the values for that user from the client-side data set into form fields, allow them to be edited, then submit the form values back to Coldbox via an Ajax call where the record gets updated. After the update occurs, my event grabs a fresh copy of the user query (which now contains the updated record), serializes it, and returns it as a JSON string to the Ajax call. Here's the line of JS that performs the Ajax call:

new Ajax.Request(saveURL,{parameters: myparams, method:'post',onCreate:showWorking,onComplete:postSave});

Here is the line in my handler(controller) that returns the data to the call:

<!--- grab a fresh copy of the users to pass back as json to the call, sans a view --->
<cfset arguments.event.renderData(type="plain",data=serializeJSON(variables.userService.getAllUsers(activeOnly=false),true),contentType="application/json) />

 

Bear in mind that the "getAllUsers" method call you see is the exact same method call being used during the initial page load to retrieve the data, which DOES contain the special characters as it should.

So here is where the problem manifests itself. The JSON string that the "postSave" method is provided with has the special characters stripped out! Poof, they are just gone. Okay, so let me go and investigate some of the optional parameters that Prototype provides for its Ajax.Request method and see if any of them might apply in this situation....  Ah, here are a few! 'encoding', 'evalJSON', 'sanitizeJSON'. Well, playing with all three of these resulted in zero changes to the symptoms. Sheesh, I've encoded everything I can possibly think to encode...what else is there? After a lot of google time, skimming page after page of semi-related (but not directly relevant) info, I came across a tiny little tweek to the contentType being returned that I tried, and lo and behold it frickin worked! Here is the new line that returns a CORRECT data set to my Ajax call:

<cfset arguments.event.renderData(type="plain",data=serializeJSON(variables.userService.getAllUsers(activeOnly=false),true),contentType="application/json; charset=UTF-8") />

The difference: adding in "charset=UTF-8" to the contentType of the data being returned. Apparently THAT'S what JS was looking for all along.

I hope this helps someone else avoid a huge loss of time. And again, for those of you out there who know this stuff inside and out and can actually visualize how it all works in your head, it sure would be an assett to the community if you could put that info into a kind of "checklist" a person could use to make sure they have all of their Unicode ducks in a row when trying to deal with special characters! Pretty please?

Doug out.

Posted by dougboude at 10:54 PM | PRINT THIS POST! | Link | 6 comments
10 December 2008
Using Database-Driven Configuration Settings in Coldbox
My approach

The Scenario: My Coldbox/Coldspring/Transfer application has settings that I want to make maintainable via a user interface. Now, the Coldbox.xml.cfm file does make provision for me to add as many custom settings as I want to my app, but I don't want to require the end user to know how to edit this xml file (nor would I trust them to). I came up with a solution to this challenge that I'm really happy with, so I thought I'd share some snippets in case it proves useful to others.

Step 1 is to ensure that you have a table designed to hold these configuration settings. My database has a table named "configuration", with the fields ID, settingName, and settingValue.

CREATE TABLE [dbo].[configuration](
 [id] [int] IDENTITY(1,1) NOT NULL,
 [settingName] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
 [settingValue] [varchar](250) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
 CONSTRAINT [configuration_PK_UC1] PRIMARY KEY CLUSTERED
(
 [id] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF

Of course, make sure this table is registered in your Transfer.xml.cfm file:

<package name="configuration">
 <object name="configuration" table="configuration" >
  <id name="id" type="numeric" generate="false"/>
  <property name="settingName" type="string" column="settingName" nullable="false"/>
  <property name="settingValue" type="string" column="settingValue" nullable="false"/>
 </object>
</package>

Next, I had to find the equivalent of the "onApplicationStart" within my Coldbox app because it is on application start that I wish to read in and load my settings. In Coldbox, this is an interception point named "afterAspectsLoad", which runs during application startup just after all plugins are created (important in my case especially since I'm relying on the "ioc" plugin to get Transfer for me). In order to leverage this interception point,  you'll need to create an interceptor and drop it in to your "interceptors" directory. Here is the interceptor I created that loads up my app's settings:

<cfcomponent name="customConfig"
    hint="I load settings stored in the database"
    output="false"
    extends="coldbox.system.interceptor">
   
 <cffunction name="Configure" access="public" returntype="void" hint="" output="false" >
  <cfset instance.ioc = getPlugin("ioc") />
 </cffunction>
 
 <cffunction name="afterAspectsLoad" access="public" returntype="void" output="false" hint="I load in any application settings from the database">
  <cfargument name="event" required="true" type="coldbox.system.beans.requestContext" />
  <cfset var qryConfigSettings = instance.ioc.getBean("transfer").list("configuration.configuration") />
  <cfset var tmpVal = "" />
  <cfloop query="qryConfigSettings">
   <cfif isJSON(settingValue)>
    <cfset tmpVal = deserializeJSON(settingValue) />
   <cfelse>
    <cfset tmpVal = settingValue />
   </cfif>
   <cfset setSetting(settingName,tmpVal) />
  </cfloop>
 </cffunction>
</cfcomponent>

As you may have noticed, I'm allowing my setting values to be JSON strings if needed, just in case I want to pass in an array or structure of values for a particular setting.

 

 

The last item of business is to let Coldbox know that our interceptor exists. We do this by registering it in the <Interceptors> section of Coldbox.xml.cfm, like so:

<Interceptor class="myapp.interceptors.customConfig" />

That's it! re-initialize your application (&fwreinit=1) and all of your settings will be available anywhere within your app.

 

Posted by dougboude at 3:54 PM | PRINT THIS POST! | Link | 0 comments
07 December 2008
Coldbox Interceptors and Custom Interception Points Demystified

Although ANYTHING you could ever want to know about coldbox is definitely in its documentation, I find that it is still necessary sometimes to have to piece things together bit by bit..."...line upon line, line upon line; here a little, and there a little:...." as the prophet Isaiah said... in order to fully comprehend them. In this post I'd like to share what I have distilled from the docs regarding the subject of using interceptors and custom interception points in Coldbox.

The Scenario:  you are building your first Coldbox app and you decide that you want to generate your navigation from your database only after the user has authenticated (before that, they get no nav). How we retrieve and/or create our navigation isn't relevant to this post (I'll likely be sharing my navigation interceptor in another post), but WHEN and WHERE creation occurs IS, so allow me to break it on down as I have come to understand it.

If you weren't already aware, an interceptor in Coldbox is simply a CFC with methods that can be executed anywhere in the Coldbox request lifecycle, independent of the logic you already have "hard coded" to run for that event. In terms that we can all probably relate to, think of interceptors in EXACTLY the same way that you think of application.cfc. You know that code within that CFC will run "just because the file exists" and you don't have to explicitly call it in your app. Methods like  "onRequestStart" or "onRequestEnd"...their code automatically gets executed at specifically defined moments in the request's lifecycle simply by virtue of their name and the CFC they happen to live in. Same with a Coldbox Interceptor, except unlike application.cfc, you have to tell Coldbox that your interceptor exists first.


It's important at this point that we briefly talk about interception points in Coldbox. You know what "onSessionStart" is, you know what "onRequestEnd" is, you know what "onError" is, etc. ... well take a gander at "preRender". This is an interception point built in to Coldbox that "happens", or is announced, before content is rendered as viewable. (On a side note, there are a whole lot of OTHER interception points that exist natively in Coldbox; you should take the time to read through the list of them in the docs before you start developing.) In order to take advantage of "preRender" then and make something happen at that point, I have to do three things:

1. Create an interceptor CFC and drop it into my "interceptors" folder in my Coldbox app;
2. Create a method in that interceptor called "preRender";
3. Register my interceptor in Coldbox's "coldbox.xml.cfm" file so that the framework knows it exists.
how to register a custom interceptor...

<Interceptors>
 <Interceptor class="myapp.interceptors.navigation" />
...

Voila! Now every time a request is made, whatever code I have in my "preRender" method in my interceptor will execute! In my case, "preRender" in my navigation interceptor is going to generate and make my navigation available to the rest of the app.

Okay, I said in my scenario description that I only wanted my navigation created IF the user was authenticated. Of course you can guess that my "preRender" method does a check to see if that has occurred or not, and acts accordingly. But, what about the actual login event itself? If you trace the process, the user will be authenticated AFTER the preRender event, and so even though they have successfully logged in, will NOT see the navigation! Not kosher.

I dealt with this by using one of Coldbox's "Custom Interception Points". At first, it seemed like such a  foreign idea that little ol' me would have the power to add an interception point within the request lifecycle. After all, weren't things such as "onRequestStart" items that only the framework or the CF Server itself had the privilege of manipulating? But here's where the mystique of interception points, even those used in application.cfc, went away for me. (Hopefully you are familiar with the children's game "Red Light/Green Light", because my understanding and explanation of an interception point is based on it). An interception point is nothing more than a framework or CF Server playing a game of "red light/green light" with our app. The call to the app (http://myapp/?event=index) is the little kid, running forward like he was told to do. At certain points, though, the framework calls out "greenlight!", and any bit of your code that you had set up listening for "greenlight" (containing a method called "greenLight()") executes, right there and then, independent of the code associated with the event being called, and even before the request itself has completed. In my case, then, all I did was write a bit of code in my login process that called out "onLogin!" as soon as authentication occurred. I then added a method to my navigation interceptor called...what do you think? Yep, "onLogin", that made sure the navigation was available for the user at the end of the request. Here, then, are the steps to adding and using your own custom interception points in Coldbox:

1. Tell Coldbox that you would like to use an interception point called "onLogin", or "onLogout", or whatever you want to call it;
2. Add a line anywhere in any of your handlers (controllers) that ANNOUNCES your custom interception point;
3. Create one or more methods in any of your interceptors named the same as your custom interception point.

That's it! Here are some code snippets of where and how to do each of the three steps above:

1. In coldbox.xml.cfm, in the "Interceptors" section, add a line like the following:

<CustomInterceptionPoints>onLogin,onLogout</CustomInterceptionPoints>

 

2. In your handler (controller), announce your interception point like so:

<cfset announceInterception('onLogin', icData) />

 

(icData is a structure containing whatever values I want to pass along to my interception methods)

3. In your interceptor(s), create a method like so:

<cffunction name="onLogin" access="public" ....

 

That's it! Hope it wasn't TOO confusing...it took me a while to really wrap my head around the flow of it all, but once you do it's not so bad. If anybody has anything to add, or if I've left you scratching your head, please feel free to comment.

Doug out

Posted by dougboude at 2:32 AM | PRINT THIS POST! | Link | 3 comments
15 November 2008
My First Excursion into using Ajax with Coldbox
opinions solicited

I had my first successful forray using Ajax in Coldbox today! After reading a LOT, checking out some examples, and then letting my natural developer's instinct take its course, I ended up with a design that left me really wondering if I had taken a wrong turn or had a moment of brilliance . As I said, it works great, but is it an ideal pattern to use or did I cross one of those invisible "development ethics" lines with my approach? I'll let you be the judge. Before I share the details, I am assuming that the reader already has at least a basic working knowledge of Coldbox views, viewlets, handlers, and Ajax. That said, here be the details...

The Scenario

Upon arriving at my Coldbox app, the visitor will be dropped off at my default layout. Here's how it's organized:
basic coldbox layout

We'll be focusing on the "login/logout" area since that is the portion I applied Ajax to. As you can see, in that area of the layout I am rendering by name the view named "login". My login.cfm view is actually a Coldbox "viewlet", meaning that it performs a private Coldbox call whenever it is loaded. Here's the pseudo-code of my login viewlet:

run the 'authentication.login' event;
grab the current user's login status;
if the user is not logged in
     show the login form
else
     show the logout form
end if

and here are the two faces that the viewlet can show:
face 1 of the login view

 

The execution of the private call to "authentication.login" (as seen in the pseudo code) is for the sole purpose of gathering current user status so we can control the flow and output of the login viewlet. Here's the actual handler method being called that accomplishes this:

<cffunction name="login" access="public" returntype="void" output="false">
 <cfargument name="Event" type="any">
 <cfset var oSession = getPlugin("sessionstorage") />

 <cfif not oSession.exists("loggedin")>
  <cfset oSession.setVar("loggedin",false) />
 </cfif>
 <cfset arguments.event.setValue("loggedin",oSession.getVar("loggedin")) />
 <cfif not oSession.getVar("loggedin")>
  <cfset arguments.event.setvalue("xe.frmAction","authentication.doLogin") />
  <cfset arguments.event.setValue("User",oSession.getVar("User")) />
  <cfif oSession.exists("sec_message")>
   <cfset arguments.event.setValue("loginmessage",oSession.getVar("sec_message")) />
   <cfset oSession.deleteVar("sec_message") />
  </cfif>
 <cfelse>
  <cfset arguments.event.setvalue("xe.frmAction","authentication.doLogout") />
  <cfset arguments.event.setValue("User",oSession.getVar("User")) />
 </cfif>
</cffunction>

Now, here's where the Ajax comes in to play. Both the login and logout form have as their submit action a call to a Prototype Ajax.Updater. Here are the actual functions:

// functions for the login view
function logmein(thisURL){
 var loginParams = $('loginform').serialize(true);
 new Ajax.Updater('loginlogout',thisURL ,{parameters: loginParams, method:'post'});
}
function logmeout(thisURL){
 new Ajax.Updater('loginlogout',thisURL,{method:'post'});
}

The nutshell is that the Updater will execute the call to 'thisURL' and place the result, whatever it is, into the target 'loginlogout' div.

 

 

Okay, I have two more relevant methods in my Authentication handler: doLogin and doLogout. Here is the pseudo-code for 'doLogin':

attempt to authenticate using the credentials supplied...
if login failed
   set session.loggedin = false;
   set session.securityMessage = "Login failed. Try again.";
else
   set session.loggedin = true;
end if
//actual code used to perform the following...
<cfset arguments.event.renderData(type="plain",data=getPlugin('renderer').renderView('authentication/login')) />

This last line is taking advantage of two cool things Coldbox allows:

 

1. The ability to render a view (or viewlet) within the handler and create a content variable;
2. The ability to render Data back to the caller rather than a view.

In my case, since "login" is a viewlet (making its own private call to the 'login' method in order to get current state variables), I'm simply rendering it inline. Then, I'm feeding the results of that rendering (a blob of HTML) to the renderData method, which is then passing that blob of HTML back to my Ajax call. Who, in turn, updates my target div with it; in this case, either a login form with a message displayed, or a logout form and a welcome message. Here is my rendition (in pictures) of how it flows:

'doLogout' performs almost identical actions...setting the session variables appropriately and then returning the HTML results of rendering 'authentication/login' inline.

As I said, it works great. But I am very interested in the opinion of others as to how they feel about the appropriateness of this approach. Any thoughts?

Posted by dougboude at 1:22 AM | PRINT THIS POST! | Link | 1 comment
10 November 2008
Using Variables in Coldspring.xml with Coldbox

As you can tell from  my last two posts, I am getting pretty deep into Coldbox used in conjunction with Coldspring. One of the things that Coldbox does for us is pass in our configuration settings to the Coldspring bean factory when it initially loads our beans (from Coldspring.xml), thus allowing us to use configuration variables, like so:

<bean id="transferFactory" class="transfer.TransferFactory" singeleton="true">
 <constructor-arg name="datasourcePath">
  <value>${TransferSettings.datasourcePath}</value>
 </constructor-arg>
  ....

The only shortcoming with this is that because we're using Coldspring's DefaultXmlBeanFactory.cfc, it will only do variable replacements when they are found within <value> tags. Brian Kotek addressed this issue with a CFC found in his Coldspring Utilities collection (http://coldspringutils.riaforge.org/ ) , specifically with a CFC called "DynamicXMLBeanFactory.cfc". Using this instead of Coldspring's default bean factory allows you to place variables in other places within your Coldspring.xml. I've used this CFC before in another project, so now my challenge was to figure out how to implement it in Coldbox so that I could make my Coldspring.xml file more dynamic.

 

After a couple hours of tinkering around, AND having to make a minor modification to DynamicXMLBeanFactory.cfc to account for it being used in the Coldbox environment, here are the steps:

1. Place a copy of the modified version of DynamicXMLBeanFactory.cfc in the Coldbox/System/Extras/Coldspring folder. The Coldspring folder won't exist, so go ahead and create it;

2. Add a setting to your Coldbox.xml.cfm file like so:

<YourSettings>
 <Setting name="ColdspringBeanFactory" value="coldbox.system.extras.coldspring.DynamicXMLBeanFactory" />
  ....

That's it! If you want to make sure it's working, create a Coldbox.xml setting such as this:

<Setting name="modelRoot" value="myapproot.model" />

and then add that variable to your Coldspring.xml as part of a bean's class path, for instance:

<bean id="authenticationService" class="${modelRoot}.services.authenticationservice">

If the app fires up without error, you're in business!

 

 

 

Hope this helps someone.  :0)

 


P.S.
If anybody is interested in the changes I made to DynamicXMLBeanFactory and why, they are as follows:

1. Since DynamicXMLBeanFactory extends Coldspring's DefaultXmlBeanFactory, and since Coldbox is hardwired in its "ioc" plugin to interact with DefaultXmlBeanFactory's interface (specifically calling the method "loadBeansFromXmlFile", which does not exist within DynamicXMLBeanFactory), I had to overload that method in DynamicXMLBeanFactory like so:

<cffunction name="loadBeansFromXmlFile" returntype="void" access="public" hint="I am overloading this super class method">
  <cfargument name="beanDefinitionFile" type="string" required="true" hint="I am the location of the bean definition xml file"/>
  <cfset loadBeansFromDynamicXmlFile(arguments.beanDefinitionFile,getDefaultProperties()) />
</cffunction>

2. DynamicXMLBeanFactory was executing an "expandpath" on an already expanded path (Coldbox is already passing in the fully expanded path to the Coldspring.xml file ), resulting in an error of "file not found". Because of this, I had to comment out line 98 in the "getReplacedColdSpringXML" method.

Perhaps there was a more elegant method for implementing Brian's CFC, but the only two choices I saw was to either modify the framework (NO! BAD MAN! NO!), or the CFC. I opted for the CFC. What I was thinking, though, is that perhaps it would be good if Coldbox allowed not only the path to the IOC's beanfactory class to be a setting, but also the name of the bean loader method that should be called (after init)? Just a thought.

Doug out

Posted by dougboude at 5:00 PM | PRINT THIS POST! | Link | 1 comment
09 November 2008
Basic Security in Coldbox using Transfer and Coldspring - Part II (of II)
wiring and autowiring

In the first post on this subject, I shared my 10,000 foot view of what pieces needed to go where in a Coldbox app in order to implement security. A lot of the code in that post was probably unfamiliar looking to many people (I know it would have been to me a couple of days ago!). So in this post I want to share how I implemented Transfer and Coldspring in the security process, clarify what some of that code was doing, and whatever other details about it I may have also come to know.

I'm assuming that you already know what Coldspring is, what Transfer is, and the basics of an event-driven framework. For example, Coldspring is a framework that allows you to manage the relationships between your CFCs (CFC A needs a configuration bean injected into it, the configuration bean needs to be initialized with parameter X, etc.); Transfer is an ORM framework that stands between you and your database (in a good way) and lets you write your code "sans SQL"; and the basics of event-driven frameworks is that they have core components built into them that allow you (via the methods they expose to you) to reach into their very soul and leverage "core functionality", like retrieving global configuration settings, retrieve system beans, etc. Okay, all that having been said, here's the dialogue I had with my frameworks in order to get them all on the same page of the workbook (Personification to be followed up with real code snippets, don't worry. It just helps me simplify things when I animate the inanimate.  ):

1. "Hey, Coldbox;(via Coldbox.xml.cfm...) I'm using Coldspring to manage my components for me. What's that? You'll make your configuration settings available to Coldspring to use in its XML configuration file? When you fire up Coldspring you'll go through its configuration XML and replace any curly-braced variables with matching values from your own configuration BEFORE you initialize Coldspring? Aw, that's sweet. Thanks!"

2. "Hey Coldspring;(via Coldspring.xml.cfm...) I'm gonna need you to produce a few objects from our host MVC framework's core, Okay? What objects would those be? Oh, well, I'll be needing the Coldbox factory object since that's how you'll need to retrieve the rest of the framework's objects for me. I'll need you to ask the Coldbox Factory for an instance of the Coldbox framework itself since it has all the core methods that many of my model object will need; and I'll want a discrete instance of the Coldbox SessionStorage plugin as well so that my model objects can access my app's persistent scope. Let's see, what else...oh yes, I'll be using an ORM, so i'll need you to produce an instance of Transfer via its own factory. And then I'll just tell you about my model objects as I get them added to the app. Thanks, Coldspring! You're a real bud."

3. "Hey Transfer,(via Transfer.xml.cfm...)  I got a whole BOATLOAD of database objects I need you to make available to me. It's a very, very long list, so just read through it at your leisure."

My frameworks are very good listeners and so once I figured out exactly how to say what I wanted to say, they complied fully with my desires. So let's look at how I made them understand, shall we?

Dialogue 1
Within the Coldbox.xml.cfm file, in the <Settings> section, I made sure the following settings were like so:

<Setting name="IOCFramework" value="Coldspring" />
<!--IOC Definition File Path, relative or absolute -->
<Setting name="IOCDefinitionFile" value="config/coldspring.xml.cfm" />
<!--IOC Object Caching, true/false. For ColdBox to cache your IoC beans-->
<Setting name="IOCObjectCaching" value="false" />

 

As far as the rest of that dialogue, as long as I make sure the setting exists within the Coldbox.xml.cfm file, Coldbox will automatically replace any curly-braced variables in my Coldspring.xml.cfm file for me. For instance, in my Coldbox.xml.cfm file in the <YourSettings> section, I have the following Transfer values:

<YourSettings>
 <Setting name="TransferSettings.datasourcePath" value="/emailmanager/config/datasource.xml.cfm" />
 <Setting name="TransferSettings.configPath" value="/emailmanager/config/transfer.xml.cfm" />
 <Setting name="TransferSettings.definitionPath" value="/emailmanager/model/definitions" />
</YourSettings>

You'll see in the Coldspring snippets below the variable placeholders that correspond to these settings; it'll make more sense there. 

 (Note: presently, only curly-brace variables found within a <value>${myColdboxSetting}</value> tagset will be replaced. Don't put curly-brace vars in your bean's class path, for instance, and expect them to work. There is a way to do this which I'm investigating now, but by default it does not.)

Dialogue 2
We informed Coldspring of several different things, so let's look at them a chunk at a time.
"...produce a few objects from our host MVC framework's core...Coldbox factory object...Coldbox framework itself...a discrete instance of the Coldbox SessionStorage plugin...." Here is the XML to accomplish the previous:

<bean id="ColdboxFactory" class="coldbox.system.extras.ColdboxFactory" />
<bean id="Coldbox" factory-bean="ColdBoxFactory" factory-method="getColdbox" singleton="true" />
<bean id="oSession" factory-bean="ColdBoxFactory" factory-method="getPlugin" singleton="true">
 <constructor-arg name="plugin">
  <value>sessionstorage</value>
 </constructor-arg>
</bean>


If you aren't familiar with Coldspring's ability to produce a bean by executing a method referenced on a previously defined bean, well, you're looking at it in action (note the 'factory-bean/factory-method' attributes). I myself wasn't aware of this ability, so was quite happy to have discovered it. First we're defining Coldbox's object factory; then we're creating an instance of Coldbox by calling the Factory's "getColdbox" method; lastly I'm grabbing an instance of Coldbox's built-in "sessionStorage" plugin the same way, calling the generic "getPlugin" method and passing in the name of the plugin I want.

Next portion of that dialogue...
"...I'll be using an ORM, so i'll need you to produce an instance of Transfer via its own factory...." Here's that code (notice the curly brace vars; each one equates to a setting created in the Coldbox.xml.cfm file): 

<bean id="transferFactory" class="transfer.TransferFactory" singeleton="true">
 <constructor-arg name="datasourcePath">
  <value>${TransferSettings.datasourcePath}</value>
 </constructor-arg>
 <constructor-arg name="configPath">
    <value>${TransferSettings.configPath}</value>
 </constructor-arg>
 <constructor-arg name="definitionPath">
    <value>${TransferSettings.definitionPath}</value>
 </constructor-arg>
</bean>

<bean id="Transfer" factory-bean="TransferFactory" factory-method="getTransfer" singleton="true" />
<bean id="Datasource" factory-bean="TransferFactory" factory-method="getDatasource" singleton="true" />

Defining Transfer's own factory, then defining the other Transfer beans as results of executing Transfer Factory methods. Noice, eh?

 

 As far as the model, so far I have only one bean defined, and that is my "authenticationService" bean. Here's that Coldspring.xml:

<bean id="authenticationService" class="emailmanager.model.services.authenticationservice">
 <constructor-arg name="transfer">
  <ref bean="Transfer" />
 </constructor-arg>
 <constructor-arg name="oSession">
  <ref bean="oSession" />
 </constructor-arg>
</bean>

As you can see, I'm passing in an instance of Transfer and Coldbox's session manager upon instantiation. By doing so, my service object can perform database calls and manage persistent values.

 

 Dialogue 3
Transfer relies on an XML file to get its information about your database entities (tables) and their relationships to one another. I won't share the entire Transfer.xml.cfm file here since I've already got most of my entities defined for my app (long list), but here are the tables relevant to security:

<objectDefinitions>
 <package name="user">
  <object name="user" table="appuser" >
   <id name="id" type="string" generate="false"/>
   <property name="username" type="string" column="username" nullable="false"/>
   <property name="password" type="string" column="password" nullable="false"/>
   <property name="firstname" type="string" column="firstname" nullable="false"/>
   <property name="lastname" type="string" column="lastname" nullable="false"/>
   <property name="email" type="string" column="email" nullable="false"/>
   <property name="isActive" type="boolean" column="isActive" nullable="false"/>
   <property name="timezone" type="string" column="timezone" nullable="true"/>
   <onetomany name="usergroup" lazy="true">
       <link to="usergroup.usergroup" column="userid"/>
       <collection type="array">
         <order  property="id" order="asc"/>
       </collection>
   </onetomany>
  </object>
 </package>
 <package name="usergroup">
  <object name="usergroup" table="usergroup" >
   <id name="id" type="string" generate="false"/>
   <property name="groupid" type="string" column="groupid" nullable="false"/>
  </object>
 </package>
</objectDefinitions>

Now that I have all of that set up, let me share again the code from my authentication handler(controller) and authenticationService cfc (that I shared in my previous post on this topic)  that peform the login function.

 

 authentication handler

<cffunction name="doLogin" access="public" returntype="void" output="false">
 <cfargument name="Event" type="any" />
 <cfset var loggedin = "" />
 <cfset var rc = arguments.Event.getCollection() />
 <!--- attempt to authenticate using the credential supplied... --->
 <cfset loggedin = variables.authenticationService.login(username=rc.username,password=rc.password) />
 <cfif not loggedin><!--- login failed...send them back --->
  <cfset arguments.event.setValue("loginmessage","Login Failed. Please try again.") />
  <cfset arguments.event.setValue("loggedin",false) />
  <cfset arguments.event.overrideEvent("login") />
 <cfelse>
  <cfset arguments.event.setValue("loggedin",true) />
 </cfif>
 <cfset arguments.event.setvalue("xe.frmAction","authentication.doLogout") />
 <cfset arguments.event.setView("login") />
</cffunction>

The line to home in on is the one that begins "<cfset loggedin = ...". It is there that, from within my handler, I am accessing the authenticationService bean we defined in Coldspring.xml.cfm. You'll recall though that I did NOT define a relationship between my handler and authenticationService. How then did my handler get access to it?

In Model-Glue, we would have provided our controller a setter and getter for the authentication service, then Model-Glue itself would have auto-injected that bean for us. Well, Coldbox to the rescue, we have the same type of functionality available to us! Coldbox actually allows two ways to auto-inject Coldspring-defined beans: via setters and getters, as we are probably accustomed to; and via the <cfproperty> tag. Now, typically cfproperty doesn't really do much for us at all; but in a Coldbox handler it's a big ol' flag that says "inject it here! inject it here!. Okay, here's the additional line in our authentication handler that performs this magic:

<cfproperty name="authenticationService" type="ioc" scope="variables" />

I opted NOT to go the setter and getter route because of the fact that if I happened to have created a setter and getter that had the same name as a bean i defined, it would attempt to auto-inject whether I really wanted it to or not. In order to avoid this, I am choosing to explicitly name my auto-injections via the <cfproperty> tag. 

Okay, so our doLogin event is calling the authenticationService's 'login' method, passing in the username and password supplied; Let's look at authenticationService now.

<cffunction name="init" access="public" output="false" returntype="any">
 <cfargument name="transfer" type="any" required="yes" />
 <cfargument name="oSession" type="any" required="yes" />
 <cfset variables._transfer = arguments.transfer />
 <cfset variables._session = arguments.oSession />
 <cfreturn this />
</cffunction>

<cffunction name="login" access="public" returntype="boolean">
 <cfargument name="username" type="string" required="yes" />
 <cfargument name="password" type="string" required="yes" />
 <cfargument name="isActive" type="boolean" required="yes" default="true" />
 <!--- create a transfer user bean, passing in the username and password, then give it back --->
 <cfset var objUser = variables._transfer.readByPropertyMap("user.user",arguments) />
 <cfset retval = false />
 <cfif objUser.getID() IS "0"><!--- login failed... --->
  <cfset variables._session.setVar("loggedin",false) />
 <cfelse>
  <cfset variables._session.setVar("loggedin",true) />
  <cfset variables._session.setVar("user",objUser) />
  <cfset retval = true />
 </cfif>
 <cfreturn retval />
</cffunction>

 We instructed Coldspring to inject Transfer and oSession into this bean upon creation, and made provision for it via the init method. Pretty standard Coldspring stuff. In our login method, we call upon Transfer to create for us the "user.user" bean, giving it our argument collection to use as criteria (my arguments are named exactly as fields in that table). Also, in case you're wondering why we didn't ask Transfer for the "user" bean instead of the "user.user" bean, it's because in our Transfer.xml.cfm file we defined a package called "user" and within that package an object named "user". Hence the path "user.user".

 

 So, Transfer will create the requested bean and, if it matched the criteria passed in, the bean will be populated; otherwise it'll be empty, with a zero value for the primary numeric key ("ID"). After Transfer gives it back, I check the primary key. If it's zero, login obviously failed. If it's not, success! Stuff the user bean into session and return a boolean to the handler (controller).

I do hope you were able to follow this okay. I can't tell you how long I had to bang my head against it for it to sink in and gel, so I hope it saves someone else a few hours of digging through docs.

Doug out.

Posted by dougboude at 1:58 AM | PRINT THIS POST! | Link | 7 comments
Basic Security in Coldbox using Transfer and Coldspring - Part I

There's no denying it: the plethora of documentation and examples that exist for the Coldbox framework is invaluable and undoubtedly covers every possible scenario to one degree or another. As I press on into a project where the use of Coldbox, Coldspring, and Transfer has been mandated to me, I'm finding that, despite the documentation and tutorials, I'm still having to search around and piece together for myself some of the information I seek. So, to complement the existing volumes of Coldbox-centric information and for my own personal reference purposes, I'm going to post some of what I'm learning as it becomes coherent to me.  Tonight's post is focused on an overview of how to implement basic security in Coldbox using Transfer and Coldspring. I won't delve much into Transfer or Coldspring with this one (that post will follow soon) so that we can focus on the 10,000 foot view first.

As I mentioned, the first "block of functionality" I tackled was the implementation of security, so let's use that as our illustration. There is a lot of documentation on the subject and even a few example apps I found. But, the documentation left me with too much gray area as I attempted to relate what I read with my current level of understanding of the framework, and the example apps, though they worked wonderfully, didn't explain to me HOW they were working. I had to dissect the apps using my limited knowledge of Coldbox and painstakingly trace the event flow from one area of the framework and application to the next, referring to the documentation and even examining some of the framework's CFCs internally. The end result for me was the foundation for my own "flavor" of basic security implementation, and it this that I share.

First, my own visualization of the Coldbox event lifecycle. I realize that the true lifecycle in all its detail could consume many a diagram page, but for the purposes of someone simply desiring to add in a bit of functionality, I do believe I've illustrated all that is required.

The Coldbox event lifecycle highly simplified

A request comes in and right away Coldbox executes the interceptors it knows about. Interceptors are very aptly named, as they do just that: intercept the request before it gets too far and executes whatever methods are appropriate. In our case, we're interested in knowing right up front if the user attempting to fulfill an http request is authenticated or not. Intercept them, then, we shall.

Coldbox comes out of the box with a pre-built security interceptor (a CFC that is executed at what we typically know as the "onRequestStart" point), but being at the newbie point I am, I found it a bit too deep to assimilate. In order to understand how things really work, therefore, I created my own. I called it "security.cfc" and dropped it into my app's "interceptor" directory. The next step was to tell Coldbox that I have an interceptor in place. This is done in config/coldbox.xml.cfm, in the <Interceptors> section. Mine looks like this:

<Interceptors>
 <Interceptor class="interceptors.security" />
</Interceptors>

I eluded to it, but now is a good time to talk about one of the things that makes Coldbox unique among the MVC frameworks we CFers have to choose from. If you've not used any framework before that relies on what's typically referred to as "convention" (Ruby on Rails, Fusebox 5.x sans xml), then you are used to defining your events down to every last detail: what messages to broadcast, what methods to execute in what CFC, etc. But Coldbox rather follows the "convention" approach, which is simply naming your CFCs and methods in such a way that the framework knows what to do all by its lonesome. This really isn't a new thing to you at all though, because if you've used Application.cfc in any of your apps, then you have practiced this very thing! Consider this...did you ever have to TELL your app to run application.cfc? Nope, because of the fact that you NAMED it application.cfc, the framework (in this case, CF Server) looked for it and executed it. Inside of your application.cfc you had some methods, right? You had one named "onApplicationStart", one named "onRequestStart", one named "onSessionStart", etc. Why? Because you knew that if you named your method appropriately, the "framework" would execute it at just the right time in the http request lifecycle. This is absolutely no different in Coldbox. Inside our interceptors we create methods that are named according to when we want Coldbox to execute them. Granted, Coldbox uses a few names of its own to indicate a specific point in the event lifecycle (such as 'PreProcess'), but they boil down to the same concept as what we're accustomed to with Application.cfc. So, I told Coldbox "hey, I have an interceptor I want you to execute for me at the beginning of the event lifecycle", then within that interceptor I created methods to run at specific points in time. In the case of my Security.cfc interceptor, I have create a single method called "preProcess" (guess when it's going to be executed). Within this method I am checking to see if the current user is logged in. If yes, do nothing; if no, redirect to my login event. In my simple scenario, "logged in" means i found session.loggedin and it was set to 'true'. Here's my interceptor method:

 

 

<cffunction name="preProcess" access="public" returntype="void" output="false">
 <cfargument name="event" required="true" type="coldbox.system.beans.requestContext" />
 <cfset var loggedin = false />
 <cfset oSession = getPlugin("sessionstorage") />
 <cfif (NOT oSession.exists("loggedIn") OR NOT oSession.getVar("loggedIn")) AND listfind("authentication.doLogin",arguments.Event.getCurrentEvent()) eq 0>
  <cfset arguments.Event.setValue("loginmessage","Please Log In") />
  <cfset arguments.Event.overrideEvent("authentication.login") />
 <cfelse>
  <cfset arguments.Event.setValue("message","we're logged in!") />
 </cfif>
</cffunction>

 

 

 

Let's assume the user is NOT logged in. My interceptor resets the current event to my login event, "authentication.login", and processes it. POP QUIZ: Based on the event name "authentication.login", what CFC and method within that CFC do you suppose Coldbox will be executing? I'm sure you guessed right, so let's go look at the 'Login' method within my 'authentication.cfc'.

Hmmm, where IS my authentication.cfc? It's not an interceptor...where is it? Ah, it's where Coldbox ASSUMES it will be (there's that "convention" thing again!): sitting in the "handlers" directory. We Model-Gluers and other MVC framework people might think of it more commonly as "controllers", but the Coldbox vernacular is "handlers", as in "event handlers". Here is the Login method:

<cffunction name="login" access="public" returntype="void" output="false">
 <cfargument name="Event" type="any">
 <cfset arguments.event.setvalue("loginmessage","You need to log in!") />
 <cfset arguments.event.setValue("loggedin",false) />
 <cfset arguments.event.setvalue("xe.frmAction","authentication.doLogin") />
 <cfset arguments.event.setView("footer") />
 <cfset arguments.event.setView("login") />
</cffunction>

 

 

 

Being the good controller/handler that it is, it's doing next to nothing except setting a few values in the event bucket and tossing some views into the view collection so that we can properly give the user a login form.

Now we see the login form, nothing fancy, just a prompt for a username and password. The user enters it, hits the submit button, and off we go to our form's action: authentication.doLogin .

<cffunction name="doLogin" access="public" returntype="void" output="false">
 <cfargument name="Event" type="any" />
 <cfset var loggedin = "" />
 <cfset var rc = arguments.Event.getCollection() />
 <!--- attempt to authenticate using the credentials supplied... --->
 <cfset loggedin = variables.authenticationService.login(username=rc.username,password=rc.password) />
 <cfif not loggedin><!--- login failed...send them back --->
  <cfset arguments.event.setValue("loginmessage","Login Failed. Please try again.") />
  <cfset arguments.event.setValue("loggedin",false) />
  <cfset arguments.event.overrideEvent("login") />
 <cfelse>
  <cfset arguments.event.setValue("loggedin",true) />
 </cfif>
 <cfset arguments.event.setvalue("xe.frmAction","authentication.doLogout") />
 <cfset arguments.event.setView("login") />
</cffunction>

Ignore the details for a moment, and let it suffice to say that we are grabbing the credentials out of the event bucket, submitting them to our model for authentication, and getting back a boolean value indicating success or failure. Again, our controller/handler method is doing next to nothing (as it should), except setting some values for the view to use.

 

 

Wanna see the "login" method in the model?

<cffunction name="login" access="public" returntype="boolean">
 <cfargument name="username" type="string" required="yes" />
 <cfargument name="password" type="string" required="yes" />
 <cfargument name="isActive" type="boolean" required="yes" default="true" />
 <!--- create a transfer user bean, passing in the username and password, then give it back --->
 <cfset var objUser = variables._transfer.readByPropertyMap("user.user",arguments) />
 <cfset retval = false />
 <cfif objUser.getID() IS "0"><!--- login failed... --->
  <cfset variables._session.setVar("loggedin",false) />
 <cfelse>
  <cfset variables._session.setVar("loggedin",true) />
  <cfset variables._session.setVar("user",objUser) />
  <cfset retval = true />
 </cfif>
 <cfreturn retval />
</cffunction>

We're passing it two parameters and setting a third by default, then passing those parameters to Transfer to see if it can successfully populate a User bean with them. If yes, store the user bean to session so we can utilize it other places and return a boolean 'true'; if not, store the empty user object to session (in case we still want to use it somewhere else...at least we know it will exist) and return a boolean false.

 

 

That's it in a nutshell! If I were me looking at this post two days ago, however, I would be very hungry to know the details of how I set up Coldspring and Transfer to play together, and how it is I am accessing my session scope and Transfer from within my model. In order to encapsulate the purpose of this post (give an overview of basic application flow for the purpose of implementing security), however, I shall end it here. But my next post will be the nitty gritty of how I wired all these pieces together.

Hope this helps someone, and do feel free to contribute your own wisdom to the comments!

Posted by dougboude at 12:30 AM | PRINT THIS POST! | Link | 3 comments