Nested Layouts in CFWheels

Must confess I’ve been struggling with this one today, even with the great documentation at cfwheels.org.

Thanks to the cfwheels list, I’ve cracked it though (blogging the solution for posterity!).

I wanted to created a truly nested layout, i.e a master layout with <html> & <body> tags, and then a sub layout in my /views/main/ (with main being the controller name in this instance) which then included the default content on /views/main/index/. The problem was that when you add a layout file in /views/main/layout.cfm it overrides the default layout in /views/layout.cfm.

Turns out you need to call the parent template in the sub template and inject the content using contentFor() before you make the call.

So in my /views/layout.cfm

<html>
<body>
<div id="master-wrapper">
<cfoutput>#includeContent("mainbody")#</cfoutput>
</div>
</body>
</html>

and in my /views/main/layout.cfm

<cfoutput>
<cfsavecontent variable="mainbody">
<div id="controller-layout">
#includeContent()#
</div>
</cfsavecontent>
<!--- Inject the above variable into the parent layout --->
<cfset contentFor(mainbody=mainbody)>
<!--- Include the parent layout --->
#includeLayout("/layout.cfm")#
</cfoutput>

and in my standard view of /views/main/index.cfm

<div id="main-view">
<p>I am the content in the main index file</p>
</div>

Results in…

<!-- Master Layout Start -->
<html>
<body>
<div id="master-wrapper">
  <!-- Sub Layout Start -->
  <div id="controller-layout">
    <!-- Standard View Start -->
    <div id="main-view">
    <p>I am the content in the main index file</p>
    </div>
    <!-- Standard View End -->
  </div>
  <!-- Sub Layout End -->
</div>
</body>
</html>
<!-- Master Layout End -->

Notes: this means you’d need a layout.cfm in every views folder just to set the mainbody variable, otherwise your content never gets called by the top level layout. I’ve tried getting this working with the default body variable but haven’t had much success yet.

Incoming search terms:

  • blackboard wallpaper
  • cfwheels database design
  • cfwheel table sort
  • cfwheels proxy controller
  • cfwheels redirectTo hash
  • cfwheels user roles management
  • nested templates in cfwheels
  • suit texture

cfWheels – Active Directory / LDAP authentication

Adding login and administrative features to your cfWheels apps has cropped up on the mailing list a few times, so I thought I’d just pull together a simple example for Active Directory / LDAP authentication.

In this particular snippet, I want to establish a user a) has credentials on the server, and b) belongs to a group called ‘ContactsDatabaseUsers’.

This method has the benefit of a) not having to store user’s passwords in your application database and b) allowing system administrators to control access to the application.

This example assumes you have a login form at /main/login/ which posts params.username and params.password to /main/dologin/

I also would have a local users model where I could add additional application specific options: i.e, when they last logged in, etc.

This information would then be copied to the session scope. ie. session.currentuser

When the users first login, this local entry needs to be created, or copied to the session scope from the existing entry.

<--------Main.cfc----------->
<cffunction name="init">
<cfscript>
// AD Auth, application wide: if I wanted to only restrict a subset a pages, I could use 'only' instead of except below.
// This filter call should be in every controller you wish to protect
filters(through="loginRequired", except="login,dologin");
// Login
verifies(only="dologin", post=true, params="username,password", error="Both username and password are required", handler="login");
</cfscript>
</cffunction>

<cffunction name="dologin" hint="Logs in a user via LDAP">
  <cfset var ldap=QueryNew("")/>
  <!--- The LDAP Server to authenticate against--->
  <cfset var server="ldap.server.domain.com">
  <!--- The start point in the LDAP tree --->
  <cfset var start="OU=People,DC=domain,DC=com">
  <!--- Scope --->
  <cfset var scope="subtree">
  <!---- Name of group --->
  <cfset var cn = "ContactsDatabaseUsers">
               <cftry>
                   <!--- Check the user's credentials against Windows Active Directory via LDAP--->
                   <cfldap
                       server="#server#"
                       username="DOMAIN\#params.username#"
                       password="#params.password#"
                       action="query"
                       name="ldap"
                       start="#start#"
                       scope="#scope#"
                       attributes="*"
                       filter="(&(objectclass=*)(sAMAccountName=#params.username#))"/>

                       <!--- Get the memberOf result and look for contactsDatabase--->
                       <cfquery dbtype="query" name="q">
                       SELECT * FROM ldap WHERE name = <cfqueryparam cfsqltype="cf_sql_varchar" value="memberOf">
                       </cfquery>

                       <cfscript>
		if (q.value CONTAINS "CN=#cn#")
			{
			params.name=params.username;
			// Check for the local user profile
			if( model("user").exists(where="username = '#params.username#'"))
			{
				// User found, copy user object to session scope
				user=model("user").findOne(where="username = '#params.username#'");
				user.loggedinAt=now();
				user.save();
				session.currentuser=user;
				flashInsert(success="Welcome back!");
			}
			else
			{
				// No Local Entry, create user as LDAP Auth has been successful
				user=model("user").create(username=params.username, name=params.name, loggedinAt=now());
				if (NOT user.hasErrors()){
					user.save();
					session.currentuser=user;
					flashInsert(success="Welcome - as this is the first time you've logged in, your account has been created.");
				}
				else
				{
					flashInsert(error="Error creating local account from successful Active Directory authentication");
					StructClear(Session);
					renderPage(action="login");
				}
			}
			redirectTo(route="home");
		}
		else
		{
		 redirectTo(route="home");
		 }
      </cfscript> 

   <cfcatch type="any">
      <cfscript>
           if(FindNoCase("Authentication failed", CFCATCH.message)){
            // Likely to be user error, e.g. typo, wrong username or password
             flashInsert(error="Login Failed: please check your username and password combination");
             renderPage(action="login");
            }
            else {
            // Likely to be LDAP error, e.g. not working, wrong parameters/attributes
            flashInsert(error="Login Failed: Please try again.");
            renderPage(action="login");
            }
       </cfscript>
   </cfcatch>
 </cftry>
</cffunction>
</cfcomponent>

<-----------Controller.cfc------------->
<cffunction name="loginRequired" hint="This function will prevent non-logged in users from accessing specific actions">
<cfif NOT isloggedin()>
	<cfset redirectTo(controller="main", action="login")>
</cfif>
</cffunction> 

<cffunction name="isloggedin" hint="Checks for existence of session struct">
<cfif NOT StructKeyExists(session, "currentUser")>
   <cfreturn false>
<cfelse>
   <cfreturn true>
</cfif>
</cffunction>

Incoming search terms:

  • cfwheels access db
  • cfwheels authentication
  • notice main::do_login() authentication failed
  • railo active directory password

Don’t forget nested functions in CFWheels

Occasionally I forget clever little things, like using functions as argument values.

Here’s a small snippet which I ended up using when doing a blog front page, where I wanted to strip the tags out of the blog body, truncate it to 300 characters, and then add a ‘read more’ link at the end of the paragraph with an ellipsis.

There’s a load of nice little functions in CFWheels for this – stripTags(), truncate() and linkTo().

Here’s what the helper in the loop ended up as:

#truncate(
  text=stripTags(body),
  length=300,
  truncateString="#linkTo(
      text='... Read More &raquo;',
      route='blog',
      action='view',
      key=id)#"
)#

Password Hashing and Salting

As I’m (hopefully) going to release my first open source project soon, I thought it would be a good time to revisit application security, specifically password hashing, salting and general encryption. If you’ve not come across Jason Dean’s blog, I’d recommend that as a first port of call, as most of what I’m referring to is explained there.

Password security is one of those never ending journeys; you have to assume that it’ll never be perfect, and that there’s always room for improvement. I’d recently had an idea to improve password hashing a little, which someone might find useful in principle.

Let’s take a simple password, like ‘myPassword1′ (incidentally, if you actually have any passwords like that, please go and get lastpass or password1 immediately and wash your keyboard out with soap).

‘myPassword1′ stored in a database in plain text is obviously a very, very bad idea. Most people’s gut reaction would be to hash it, which at least is one way.

<cfset thePassword='myPassword1'>
<cfset passwordHash=Hash(thePassword, 'SHA-512') />

But wait!, let’s assume 90% of our users are stupid, or at least, uninformed. If two people have the same password, their hashes will appear identically in the password column. On top of that, if I hash the password again, I’ll get exactly the same resultant string. So a hashed (unsalted) password is really not that useful. You could perform a ‘rainbow table’ attack: password hashes from your database can be compared against a table of known hashes (such the hash for myPassword1), et voila, password revealed.

So the next step is to introduce salting: that is, appending or prepending a unique string to the password to make the resultant hash unique, and make rainbow table attacks much much harder.

<cfset salt=createUUID() />
<cfset passwordHash=Hash(thePassword & salt, 'SHA-512') />

This has the advantage that attacks require the salt to even attempt some sort of dictionary/rainbow table attack. Usually the salt is stored alongside the hashed and salted password in the database.

This is the part which I think could be improved by a relatively small step. By storing the salt alongside the password in the users table, we’re essentially giving a hacker the necessary ammunition to attempt to compromise the password. Let’s make it a little harder. Let’s encrypt the salt itself using another key, stored outside the webroot. So I might have a folder called ‘auth’ on the same level as my ‘webroot’ or ‘html’ folder. In there, I’m going to store a text file with a UUID inside. When I want to compare a password hash, I now have to decrypt the salt before I can use it (as I acutally need the salt value, I’m not hashing it, as that’s one way) using the key read in via CFFILE outside the webroot.

<!---Get the key--->
<cfset authKeyLocation=expandpath('../auth/key.txt')>
<cffile action="read" file="#authKeyLocation#" variable="authkey">
<!--- New password hashing --->
<!--- Generate a salt, this is never stored in it's plain form--->
<cfset theSalt=createUUID() />
<!--- Hash the password with the salt in it's plain form--->
<cfset passwordHash=Hash(thePassword & theSalt, 'SHA-512') />
<!--- The encrypted salt to store in the database,
using the authKey--->
<cfset salt=encrypt(theSalt, authKey, 'CFMX_COMPAT')>

So why do this? If your database is compromised, you at least have an additional key to the puzzle missing for the attacker: they would have to do both a compromise of the filesystem AND the database to get anywhere. Additionally, you could quickly invalidate all the passwords in your application by deleting/regenerating the ‘master key’ at a file system level (this also assumes you have a half decent ‘forgotten/reset password’ feature in your application), which could be useful is you suspect the database and file system to be compromised at any point.

Whilst this is hardly comprehensive explanation, by adding additional layers and parts to the process, we’ll hopefully make any attackers lives a little more difficult. Jason Dean adds another step I like looping the hashing process over 1000 times – if you made this number completely arbitary between 1000 and 1500, you’d have another variable the attacker would have to get, and this one would be stored in the source code of the application itself.

So assuming you added all the above measures, you’d have to:

  1. compromise the database (for the password hash, and the encrypted salt),
  2. the source code for the application (to get the number of hashing loops), and
  3. get the facility to navigate outside the (potentially) locked webroot (to get the master key to decrypt the encrypted salt).

Incoming search terms:

  • 72 character hash
  • coldfusion hash password salt
  • coldfusion password salt hash
  • how can i make password like hashing
  • sha512 salt encryption

cfWheels Nested Properties with One to Many and Many to Many Relationships Part 2

Right, following on from part 1…

We should at this point be getting the _emailaddress partial loaded in our form. What I want to do, is be able to add additional email addresses, and get Wheels to update the nested properties appropriately when I submit the form. Oh, I’m assuming you’re using jQuery too.

Firstly, a disclaimer. Javascript isn’t my strong suit, at all, in any way, whatsoever. The following will undoubtedly be able to be condensed down into something much more efficient. Also, this isn’t my javascript – this is unashamedly nicked from Ben Nadel (see http://www.bennadel.com/blog/1375-Ask-Ben-Dynamically-Adding-File-Upload-Fields-To-A-Form-Using-jQuery.htm). Yet again, I find myself standing on the shoulders of giants.

In order to understand what I’m doing, it’s probably best to look at the generated code which wheels makes for our email address partial.

As a reminder, here’s what the cfWheels code is:

<!---_emailaddress.cfm--->
<cfoutput>
#textField(objectName="contact", association="emailaddresses", position=arguments.current, property="email", label="Email Address", size=62, class="email")#
#select(objectName="contact", association="emailaddresses", position=arguments.current, property="type", label="Type", options="Home,Work")#
#checkbox(objectName="contact", association="emailaddresses", position=arguments.current, property="preferred", label="Preferred")#
</cfoutput>

So this creates:

<label for="contact-emailaddresses-1-email">Email Address</label>
<input type="text" value="" size="62" name="contact[emailaddresses][1][email]" maxlength="500" id="contact-emailaddresses-1-email" class="email valid">

<label for="contact-emailaddresses-1-type">Type</label>
<select name="contact[emailaddresses][1][type]" id="contact-emailaddresses-1-type">
<option value="Work" selected="selected">Work</option><option value="Home">Home</option>
</select>

<label for="contact-emailaddresses-1-preferred" class="checkboxLabel">Preferred</label>
<input type="checkbox" value="1" name="contact[emailaddresses][1][preferred]" id="contact-emailaddresses-1-preferred" class="checkbox" checked="checked">
<input type="hidden" value="0" name="contact[emailaddresses][1][preferred]($checkbox)" id="contact-emailaddresses-1-preferred-checkbox">

What we want to do is replicate this output on the fly using Javascript, and increment the counter (i.e, all the 1′s in the above example).

In order to do this, I need to do a few things. Firstly, I need to make sure I can reference the existing set of email(s), which are loaded when the page loads, then I need to be able to clone this set of fields, incrementing the count as I go, and finally, I need to be able to have appropriate add and remove buttons/handlers to deal with manipulating the DOM itself.

Once that’s done, I then can submit the form and do the update: since writing part one, I’ve come accross a small catch with this approach which requires a extra line or two of code (oh noes!) which I’ll get to later.)

Creating theDOM Template

So, in addition to my email address partial, I’m going to create another, called _emailaddressTemplate – Warning, bad code ahead…

<!--- Dynamic Email Field Template--->
<div id="email-templates" class=" clearfix" style="display: none ;">
    <div id="::FIELD1::" class="emailtemplate clearfix">
      <div class="span-11">
        <div class="field">
          <label for="contact-emailaddresses-::FIELD2::-email">Email Address</label>
          <input type="text" value="" size="62" name="contact[emailaddresses][::FIELD12::][email]" maxlength="500" id="contact-emailaddresses-::FIELD3::-email" class="email">
        </div>
      </div>
      <div class="span-3">
        <div class="field">
          <label for="contact-emailaddresses-::FIELD4::-type">Type</label>
          <select name="contact[emailaddresses][::FIELD5::][type]" id="contact-emailaddresses-::FIELD6::-type">
            <option value="Work">Work</option>
            <option value="Home">Home</option>
          </select>
        </div>
      </div>
      <div class="span-3">
        <label for="contact-emailaddresses-::FIELD7::-preferred" class="checkboxLabel">Preferred</label>
        <div class="checkbox">
          <input type="checkbox" value="1" name="contact[emailaddresses][::FIELD8::][preferred]" id="contact-emailaddresses-::FIELD9::-preferred" class="checkbox">
          <input type="hidden" value="0" name="contact[emailaddresses][::FIELD10::][preferred]($checkbox)" id="contact-emailaddresses-::FIELD11::-preferred-checkbox">
        </div>
      </div>
      <div class="span-3 last prepend-top">
        <p><a class="button negative removeemail" href="">Remove</a></p>
      </div>
    </div>
</div>

<script>
// Another bit of JS nicked from Ben Nadel.
// When the DOM has loaded, init the form link.
$(
function addemail(){
var jAddNewRecipient = $( "#addnewemail" );
  jAddNewRecipient
.attr( "href", "javascript:void( 0 )" )
.click(
function( objEvent ){
AddNewUpload();
  objEvent.preventDefault();
return( false );
}
);
}
)

$('.removeemail').live('click',function() {
    $(this).parents("div.emailtemplate:first").remove();
return( false );
});

function AddNewUpload(){
var jFilesContainer = $( "#emails" );
  var jUploadTemplate = $( "#email-templates div.emailtemplate" );
var jUpload = jUploadTemplate.clone();
var strNewHTML = jUpload.html();
var intNewFileCount = (jFilesContainer.find( "div.emailtemplate" ).length + 1);
jUpload.attr( "id", ("emailedit[" + intNewFileCount + "]") );
  strNewHTML = strNewHTML
.replace(
new RegExp( "::FIELD1::", "i" ),
intNewFileCount
)
.replace(
new RegExp( "::FIELD2::", "i" ),
intNewFileCount
)
  .replace(
new RegExp( "::FIELD3::", "i" ),
intNewFileCount
)
.replace(
new RegExp( "::FIELD4::", "i" ),
intNewFileCount
)
.replace(
new RegExp( "::FIELD5::", "i" ),
intNewFileCount
)
.replace(
new RegExp( "::FIELD6::", "i" ),
intNewFileCount
)
.replace(
new RegExp( "::FIELD7::", "i" ),
intNewFileCount
)
.replace(
new RegExp( "::FIELD8::", "i" ),
intNewFileCount
)
.replace(
new RegExp( "::FIELD9::", "i" ),
intNewFileCount
)
.replace(
new RegExp( "::FIELD10::", "i" ),
intNewFileCount
)
.replace(
new RegExp( "::FIELD11::", "i" ),
intNewFileCount
)
.replace(
new RegExp( "::FIELD12::", "i" ),
intNewFileCount
)

;

jUpload.html( strNewHTML );
  jFilesContainer.append( jUpload );
}
</script>

So what’s going on here? At the top, I’ve got a template, using Ben’s ::field:: references. Underneath I’ve got the JS to replicate the template and insert it in the appropriate place, and increment the counter.

This partial needs to be include *OUTSIDE* the form: this is important: otherwise these oddly named form fields will get into your params and cause problems.

Also note, I’ve got an anchor tag with class of .removeemail – this allows me to remove the parent div element onclick, thus removing it from the the form.

Back in my edit.cfm, I’m going to add these includes, and add another anchor tag to add the additional form fields. So it now looks something like this:

<cfoutput>
<cfif params.action EQ "add">
    <h2>Add a New Contact</h2>
    #startFormTag(class="generic", id="contact-edit", action="create")#
<cfelse>
    <h2>Editing Contact</h2>
    #startFormTag(class="generic", id="contact-edit", action="update", key=params.key)#
</cfif>

    #errorMessagesFor("contact")#
        #select(objectName="contact", property="prefix", includeBlank=true, options=application.oii_contacts.prefixes, label="Prefix", title="Optional prefix, such as Dr, Professor etc")#
        #textField(objectName="contact", property="firstname", label="First Name *", class="required", minlength="2", title="First Name, required, needs as least 2 chars")#
        #textField(objectName="contact", property="middlename", label="Middle Name", title="Middle Name, optional")#
        #textField(objectName="contact", property="lastname", label="Last Name *", class="required", minlength="2", title="Last Name, required, needs as least 2 chars")#
<!--- snip... --->

        <div id="emails">
         #includePartial(contact.emailaddresses)#
         </div>
         <a href="" id="addnewemail" class="button">Add Another Email</a>

         <!--- Categories ---->
<cfloop query="categoryTypes">
#hasManyCheckBox(label=name, objectName="contact", association="categories", keys="#contact.key()#,#categoryTypes.id#")#
</cfloop>
        #submitTag(class="edit", value="Update Contact")#
     #endFormTag()#

     <!---Hidden DOM Templates --->
     #includePartial("emailaddressTemplate")#
</cfoutput>

So important to note, my DOM template partial is outside the form.

The catch I mentioned earlier comes when updating this: as is stands, I’ve not got a way of telling wheels which email addresses to delete etc. so when I loaded the contact model in my update function (see part 1), it would update and not replace the nested entries. I’ve done the following to simply replace them: More disclaimers – I can bet there’s something I’ve missed, or a better way of doing this: cfWheels gurus please do enlighten me!!

My new update() function in Contacts.cfc controller:

<cffunction name="update">
    <cfloop from=1 to="#arraylen(contact.emailaddresses)#" index="i">
     <cfset contact.emailaddresses[i].delete()>
    </cfloop>
        <cfset contact.update(params.contact)>
        <cfif contact.hasErrors()>
         <cfset renderPage(action="edit")>
        <cfelse>
<cfset flashInsert(success="The contact was updated successfully.")>
<cfset redirectTo(action="view", key=contact.id)>
        </cfif>
    </cffunction>

This works for me, but as you can see, there’s a fair bit of tidying up to be done, especially on the JS end.

 

Incoming search terms:

  • cfwheels
  • cfoutput and jquery
  • jquery projection type checkbox demos
  • jquery count textarea minlength
  • h 264 streaming cfset
  • cfwheels one-to-many
  • cfwheels nestedproperties validation
  • cfwheels nested properties
  • cfwheels associations selectbox
  • cfwheels associations sample

cfWheels Nested Properties with One to Many and Many to Many Relationships Part 1

I’ve just started development on a new cfWheels application, and since 1.1.2 has been released, I’ve been meaning to dig down into some of the newer features and functions, such as nested properties. This isn’t a small topic, but once you get the gist of what cfWheels is doing behind the scenes, you may well sit there with your jaw on the floor for a little bit.

So, Nested Properties – where to start? Well, what’s one of the most common things about any ‘relational database based’ (try saying that three times quickly) application? Join tables: updating, deleting and adding those joins – done traditionally, it’s a bit of a chore, let’s be honest.

So how does Wheels do it?

Let’s take a simple, real world example.

You have a Contact. They have multiple email addresses, and that contact might also be classed into multiple categories.

Our email addresses are unique to each contact, so this is a ‘one to many’ relationship. One contact, multiple addresses.

Our contact may be in multiple categories, i.e this could be something like Alumni, Funder etc. the point is with these values, you don’t want to repeat them for each contact – the data is repeated: additionally, these categories are predefined, so having them as a database table is more convienient. So this is a many to many relationship: a Contact may have multiple categories, and each category can encompass multiple contacts.

So let’s look at the models and database table for what we’ve got so far.

Database Tables:

contacts (Our main contacts object)

emailaddresses (our email address storage)

categorytypes (our list of categories)

categories (our join table)

Models:

<!---Contact.cfc--->
<cfcomponent extends="Model" output="false">
<cffunction name="init">
<cfset property(name="createdBy", defaultValue=session.currentuser.id)>
<cfset property(name="updatedBy", defaultValue=session.currentuser.id)>
<cfset hasMany(name="emailaddresses", dependent="deleteAll")>
<cfset hasMany(name="categories", dependent="deleteAll")>
<cfset nestedProperties(associations="emailaddresses,categories", allowDelete=true)>
</cffunction>
</cfcomponent>

<!---EmailAddress.cfc--->
<cfcomponent extends="Model" output="false">
<cffunction name="init">
<cfset belongsTo("contact")>
</cffunction>
</cfcomponent>

<!---CategoryType.cfc--->
<cfcomponent extends="Model" output="false">
<cffunction name="init">
<cfset hasMany(name="categories")>
</cffunction>
</cfcomponent>

<!---Category.cfc--->
<cfcomponent extends="Model" output="false">
<cffunction name="init">
<cfset belongsTo("contact")>
<cfset belongsTo("categoryType")>
</cffunction>
</cfcomponent>

So now we’ve got the basics setup, let’s look at actually using these associations in a meaningful way.

 

Adding a new contact

My Contacts.cfc controller will be handling all the CRUD operations for the contact model. Because of our nested properties, when we create the initial contacts object, we need to also create the email address and categories objects *as part of* the contacts object.

<!---Contacts.cfc--->
<cffunction name="init">
<cfset filters(through="getCategoryTypes", only="add,edit,update")>
<cfset filters(through="getCurrentContact", only="view,edit,update")>
<cfset verifies(only="getCurrentContact", params="key", paramsTypes="integer")>
</cffunction>

<cffunction name="add" hint="Add a New Contact">
<cfset var newEmailaddress[1]=model("emailaddress").new()>
<cfset var newCategory[1]=model("category").new()>
<cfset contact=model("contact").new(emailaddresses=newEmailaddress,categories=newCategory)>
<cfset renderPage(action="edit")>
</cffunction>

<cffunction name="update">
<cfset contact.update(params.contact)>
<cfif contact.hasErrors()>
<cfset renderPage(action="edit")>
<cfelse>
<cfset flashInsert(success="The contact was updated successfully.")>
<cfset redirectTo(action="view", key=contact.id)>
</cfif>
</cffunction>

<cffunction name="getCategoryTypes" access="private">
<cfset categoryTypes=model("categoryTypes").findAll()>
</cffunction>

<cffunction name="getCurrentContact" access="private">
<cfset contact=model("contact").findone(where="id=#params.key#", include="emailaddresses,categories")>
</cffunction>

Where possible, I try and reuse the edit/add forms, as this means you’re not repeating yourself (hence the renderPage bit).

You’ll notice two private functions: one just gets the predefined values for Categories, and the other gets the Current Contact, and *includes* our email addresses and categories. By using a filter, I don’t need to repeat myself later when we’ve got the view functions, and I also don’t need an entry for edit or view. Additionally, I’m verifying the getCurrentContact method, checking it always has params.key as an integer.

Also, not that I’ve created newCategory and newEmailAddress as private vars (this is just to keep it self contained), but i’ve also created them as one dimensional arrays: the new Objects go in the first position in these arrays – keep this in the back of your mind.

My main edit form will end up looking something like this:

<!---Edit.cfm--->
<cfif params.action EQ "add">
    <h2>Add a New Contact</h2>
    #startFormTag(action="create")#
<cfelse>
    <h2>Editing Contact</h2>
    #startFormTag(action="update", key=params.key)#
</cfif>

#errorMessagesFor("contact")#

<!--- Basic info for contact --->
#textField(objectName="contact", property="firstname", label="First Name *", class="required")#
#textField(objectName="contact", property="middlename", label="Middle Name")#
<!--- Snip Etc…--->

<!--- Email Addresses --->
#includePartial(contact.emailaddresses)#

<!--- Categories ---->
<cfloop query="categoryTypes">
#hasManyCheckBox(label=name, objectName="contact", association="categories", keys="#contact.key()#,#categoryTypes.id#")#
</cfloop>

#submitTag()#
#endFormTag()#

Immediately, there will probably be two bits which raise an eyebrow: 1) the ‘includePartial’ call for email addresses, and 2) the HasManyCheckBox.

Let’s take the categories first as it’s a bit simpler.

The categoryTypes loop loops over each checkbox. Wheels then checks for an association named categories on the contact object. A composite key is used to look for the existence of the record in the join table. If it exists, it’ll appear checked. The best part is, that as we’re looping over the categoryTypes query, we’ve got access to the actual values of the table, so in this example, the categoryTypes.name column will appear as the label. Nice.

The Email Address includePartial:

This is ‘slightly’ more complicated, but not much. If you remember, we created the contacts object with two additional nested objects (in arrays).
The partial loops over the contacts.emailaddresses entry, and seeing an array, loops over that too. Cunning.

<!---_emailaddress.cfm--->
<cfoutput>
#textField(objectName="contact", association="emailaddresses", position=arguments.current, property="email", label="Email Address", size=62, class="email")#
#select(objectName="contact", association="emailaddresses", position=arguments.current, property="type", label="Type", options="Home,Work")#
#checkbox(objectName="contact", association="emailaddresses", position=arguments.current, property="preferred", label="Preferred")#
</cfoutput>

So the important part here is the association, and the position. The position is used by Wheels to say which iteration of the loop you’re on, and the association helps point back to the main contact model.

In Part 2 I’ll look at how we can leverage some javascript to ‘add and remove’ email addresses with this setup.

Incoming search terms:

  • cf wheels loop data
  • cfwheels associations
  • cfwheels hasmany
  • cfwheels init <cfset testcontroller = controller(
  • cfwheels joining tables
  • cfwheels one to many delete create records
  • cfwheels select() selected
  • how to save into a database in cfwheels

CFWheels in Use at the OII

Obviously, I’ve been a fan of CFWheels for a fair while now. So naturally, when the opportunity came to build a new app from scratch, there was only one real contender.

At the Oxford Internet Institute (OII) – where you can find me from 9-5 (most of the time) – we’ve primarily been a research department, with no students. This changed a few years ago, with the introduction of a DPhil programme, and more recently, an MSc programme.

Naturally, when you throw 30 students a year together, and another 20 or so tutors/lecturers, you’re going to need some sort of organisational/courseware app. For the first year or two, we tried Wikis, Sakai, trialled Moodle, kept well away from Blackboard (due to licensing) but none of them quite did what we wanted them to. They all were built for another purpose – delivering complete courses online, or managing things we didn’t need, creating a lot of UI clutter.

Enter the new CFWheels app, nicknamed ‘Plato’ (not officially called Plato, as I think there’s *already* some existing courseware with that name). Let’s call it cfPlato then.

The aim was to build a closed system which fullfilled the following:

  • To list courses, their outlines and who’s teaching them
  • For Tutors to communicate with their students via bulk email
  • A complete email archive for all courses and groups
  • File storage, with archiving, tagging and bulk downloading via .zip
  • Individual user accounts
  • File submission
  • Deadline listings with notifications
  • Threaded discussion via Forums
  • Facilities for smaller groups, similar to courses
  • Multiple user roles, such as tutor, student, administrator etc.
  • Full logging and audit trail
  • Avatar uploading and cropping
  • Ability for students to provide anonymous Course feedback

I can up with the idea that we should give access to pretty much everything to authenticated users, then allow student to ‘subscribe’ to their areas of interest; Subscribing means they get email notifications etc, and various shortcuts in the system to their courses of choice. Whilst this puts the responsibility of subscribing to courses in the hands of the students, it removes a large administrative overhead.

As this is currently a closed system, I can’t demo it properly, but I thought it might interest other people to get an idea of the sort of apps which are starting to spring up based on CFWheels.

Tonnes of screenshots here as a Flickr slideshow. Turn on Descriptions/comments for some more info:

http://www.flickr.com/photos/oxfordalto/sets/72157625977093921/show/

Built using CFWheels, CF8, BlueprintCSS, JQuery, JQuery Tools, TinyMCE, JCrop.

Comments & Questions welcome!

Incoming search terms:

  • download OxAlto Capita theme

CFWheels – logging the flash

One of the most useful features of CFWheels is ‘the Flash’.

Essentially, it’s a session scope which is designed to carry messages over page redirects, and only remove itself once it’s actually been viewed by the user.
I usually use it when a user has updated, deleted or requested something which requires some sort of feedback – like a password reset, a profile change etc.
Using filters, we can start using these flash scope messages as a basic logging device; did the user see an error message when they tried to update their profile? Was their password change successful? Were they told about it? etc.
In my Users.cfc controller, I’ve got one important line which makes this possible;

<cfset filters(through="logFlash", type="after")>

This means after any function in my Users.cfc controller is executed, logFlash() is called.

And in my Controller.cfc (where functions which may be required by all controllers live), I’ve got the two logging functions called logFlash, and addLogLine:

<cffunction name="logFlash" hint="Grabs the message in the flash scope and logs it">
<cfif structkeyexists(session.flash, "error")>
<cfset addLogLine(name=session.flash.error, type="error")>
</cfif>
<cfif structkeyexists(session.flash, "success")>
<cfset addLogLine(name=session.flash.success, type="success")>
</cfif>
</cffunction>

This function looks for struct keys of ‘error’ or ‘success’  in the flash scope and calls addLogLine() – this example creates a db row in a table called logfiles, but you could easily rewrite this to suit your needs (i.e email you, or write to a static file): I’m also logging the current userid, what type of message it was, and the calling IP address – plus I’m using createdAt in the logFiles table to automatically timestamp the entry.

<cffunction name="addLogLine" hint="Manually add a log line">
<cfargument name="name" required="yes" hint="The Actual Log Message">
<cfargument name="type" required="no" default="admin" hint="example options: public | admin | login | file | error | success">
<cfif NOT structkeyexists(session, "currentUser")>
<cfset thisuserid=0>
<cfelse>
<cfset thisuserid=session.currentUser.username>
</cfif>
<cfset logline=model("logfile").create(name=arguments.name, userid=thisuserid,
type=arguments.type, ip=cgi.REMOTE_ADDR)>
<cfreturn true />
</cffunction>

Overall, a really useful way of working out what the user is seeing, and once you’ve set up these functions, calling them from other controllers is simply a matter of adding one line!

CFWheels – Don’t forget your filter

One of the features of CFWheels which I’ve been guilty of ignoring in the past is the combination ‘filter’ and init().

In your controller model, you have two really powerful tools at your disposal. The init() function, which runs before each request to the controller, can allow you to do lots of preprocessing logic, and make sure everything is ‘good to go’ with displaying your view. The filter function, when combined with init(), means you can call specific functions for a view, and keep any recurring code out of the view function itself.

I often have caught myself doing this multiple times in a controller (in this case, users.cfc)

<!---View User--->
<cffunction name="view">
<cfset currentuser=model("user").findOneByUsername(session.currentUser.email)>
</cffunction>

<!---Edit User--->
<cffunction name="edit">
<cfset currentuser=model("user").findOneByUsername(session.currentUser.email)>
</cffunction>

etc.

All I want is the current Users data, and often I’d want it for the view, list, edit functions etc. If you find that you’re repeating code in wheels, stop and ask your self whether there’s a better way.

Using the init function and filter, we can replace each call by simply writing the function once, and popping the call to the function in the filter. So:

<!--- init --->
<cffunction name="init">
<!---Filters--->
<cfset filters(through="getCurrentUser", only="view,edit")>
</cffunction>

<!--- My new private function --->
<cffunction name="getCurrentUser" access="private">
<cfset currentuser=model("user").findOneByUsername(session.currentUser.email)>
</cffunction>

This means our object of currentuser is now available to the view and edit functions. We could also add some checks to see whether the session is valid, the object is returned, etc. Your resulting function might look something like this:

<cffunction name="getCurrentUser" access="private">
<cfif structkeyexists(session.currentuser, 'email')>
<cfset currentuser=model("user").findOneByUsername(session.currentUser.email)>
<cfif NOT isObject(currentuser)>
<cfset userError()>
</cfif>
<cfelse>
<cfset userError()>
</cfif>
</cffunction>

<!---An additional private function to throw a user related error--->
<cffunction name="userError" access="private">
<cfset flashInsert(error="User Not Found")>
<cfset redirectTo(back=true)>
</cffunction>

Once you get your head round this relatively simple principle, it makes creating your controllers very quick indeed..!