The joy of LESS

I’ve been using LESS quite a lot of the last few years, especially combined with Bootstrap. Writing bog standard CSS feels quite long winded and painful now. Today I was reminded quite how much I like it when doing some categorisation for a site. You know the problem, lots of very similar lines of CSS with one differing attribute (in this case, colour). In standard CSS, you’d end up hard coding category names in multiple places, i.e:

.myDiv .my-category-one a, .myDiv .my-category-one h1 {color:red;}
.myDiv .my-category-two a, .myDiv .my-category-two h1  {color:blue;}
.myDiv .my-category-three a, .myDiv .my-category-three h1 {color:green;}

etc..

Behold, the mixin. Save yourself some time.

// Category Mixin
.category(@name, @colour){
  .myDiv [email protected]{name} {
    a {color: @colour}
    h1 {color: @colour}
  }
} 
// Create Categories
.category(my-category-one, red);
.category(my-category-two, blue);
.category(my-category-three, green);

Why is this better? Well, to add another Category is simply a case of adding

.category(my-next-category, @brand-primary);

Anything from my Mixin, like H1 and a CSS selectors get auto applied. Make the change once, done. Need to add more selectors to your category? Add them to the mixin. Simples. Note the variable output of “name” – [email protected]{name} which will create .my-category-one (or whatever you’ve passed in).

Setting up HTTPS on Elastic Beanstalk

Edit: AWS now have a certificate service which may be more useful to you, depending on your setup.

The fundamental difference in setting up https on Elastic Beanstalk is that your certificate is installed on the load balancer/environment itself, *not* the individual instances. This actually makes life a lot easier: you don’t need to worry about doing anything to the instances themselves as the SSL terminates at the load balancer. This assumes you’re using multiple availability zones, not a single zoned instance.

Also, remember this won’t ever work on myenvironment.beanstalk.com, only on a CNAME entry for yourdomain.com which you’ve aliased to the beanstalk environment URL, as obviously, AWS don’t hand out certs for beanstalk.com 🙂

Step 1:
Get your certificates and ensure they’re in the right format.

I used sslmate.com, which I found very easy to use for a simple DV (Domain validated) certificate – you don’t actually need *more* than DV unless you want to do things like have “yourcompany” in the URL bar alongside the padlock sign. Wildcards are only really cost effective if you want more than 8-9 subdomains certified.

So once sslmate is installed locally, do:

$ sslmate buy www.example.com

and follow the prompts. Note, I didn’t add any of the ‘optional’ fields as I remember that can cause problems (unfortunately I can’t remember exactly *where* I read that).

Once you’ve successfully made the purchase, you should now have a bunch of files in the directory you ran the command. The good part is these certificates are in the correct format, and also are signed. The PEM format (as opposed to DER) is actually not necessarily to do with the file extension: i.e, .crt can be in PEM format, so don’t feel you need to convert these certificates just because they haven’t got a .pem extension. I only mention formats as this is the ‘other’ format which AWS uses for S3 key pair URL signing, so if you’re only exposure to that sort of thing is S3, this is the other one.

You should have:

mydomain.com.key
mydomain.com.crt
mydomain.com.chain.crt
mydomain.com.chained.crt

Step 2:

Make sure you’ve got AWS command line tools installed. This isn’t the Elastic Beanstalk CLI tools – this is in addition to those. You can get them here: http://aws.amazon.com/cli/ – although as I had pip installed from installed the EB CLI, I just did

$ pip install awscli

After which, setup using:

$ aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-west-1
Default output format [None]: json

Step 3:

Time to upload our certs to a AWS IAM profile:

$ aws iam upload-server-certificate --server-certificate-name certificate_object_name --certificate-body file://public_key_certificate_file --private-key file://privatekey.pem --certificate-chain file://certificate_chain_file

So this should equate to something like:

$ aws iam upload-server-certificate --server-certificate-name myDomainSSL --certificate-body file://mydomain.com.crt --private-key file://mydomain.com.key --certificate-chain file://mydomain.com.chain.crt

The Chain file is important for browser acceptance.

If successful, you should get something like:

arn:aws:iam::123456789012:server-certificate/cert

returned at the prompt.

Step 4:

Attach cert to the load balancer:

Login to the web GUI at aws.amazon.com, select Elastic Beanstalk and click on the configuration section for the environment you want to apply the certificate to. Under Network Tier > Load Balancing, click on the cog to configure.

You should be able to see “myDomainSSL” as a dropdown under SSL Certificate ID – if you don’t, the certificate isn’t in IAM or something so you need to go back a few steps and check.

So at this point, you need to do something like:

Listener Port: 80
Protocol: HTTP
Secure Listener Port: 443
Protocol: HTTPS

and ensure your certificate is selected.

Saving will restart your instance, and all going well, https://www.mydomain.com should work. Except if you’re using VPC, you need to do one last step, which is open up 443 on the security group; (Go to EC -> Security Groups, select Load Balancer Security Group, add an inbound rule for HTTPS/TCP/443 on 0.0.0.0/0)

Step 5:

Read something like This for some nice pointers/reminders about running an app over https.

Installing Jekyll on OSX Mavericks

Came across a bizarre error which I thought I’d share the fix for in case anyone else comes across it: whilst trying to do a bundle install as per the gitHub pages guide, I hit this when trying to do:

tom$ bundle install

An error occurred while installing nokogiri (1.6.2.1), and Bundler cannot continue. Make sure that `gem install nokogiri -v ‘1.6.2.1’` succeeds before bundling.

Turns out you need xcode command line packages updated: check you’ve got xcode:

tom$ xcode-select –version

xcode-select version 2333.

tom$ xcode-select –install

xcode-select: note: install requested for command line developer tools

After which all plays nicely.

Quick report from SOTR2014

A quick report from SOTR2014:

A massive thanks to Fuzzy Orange for putting on a fantastic conference; the food, organisation, timings, coffee etc were perfect. Special hats off to Andy Allan for taking quite so much general abuse over the last two days.

Some great talks from Matt Gifford (on Grunt & Gulp), Kitt Hodsdsen (automation in front-end development), David Boyer (NodeJS), Rob Dudley (Bower, Grunt, Yeoman), Mark Drew (Deploying to the Cloud), Anna Shipman (Automating government), Simon Wood (Static Site generation).

There was a noticeable move towards ‘tools’ as opposed to just programming. Automation in the front end is basically essential (or at least, that’s how I’m feeling immediately post conference). As I was already using Grunt, Bower & Yeoman, there was a fair bit of repetition, but each speaker had their own techniques, explanations and way of doing things which was enlightening. Kitt’s automation talk covered a vast number of tools (something like 350+ slides in under 2 hours), so that’s a whole area I need to go and revisit – live reload etc, ‘live’ style guides – some useful bits.

I still must confess, that even with the enthusiastic words of David Boyer, I don’t have a use-case scenario for using node.js in my head yet – on a dev server, sure, essential – but haven’t got a project where it would work on a production server yet. One to keep in mind though.

As always, Mark Drew shows he’s much cleverer than the rest of us, with demonstrating rapid AWS & Cloudbees deployments – I really must checkout Cloudbees. “cloudbees deploy” may just be my new favourite command line command.

Simon Wood spoke passionately about static site generation – Jekyll/Markdown etc – I use this to a degree already, but it’s prompted me to revisit how I do it, and look for improvements. It’s not going to replace 90% of my sites though. Sorry Simon.

The real plus of the conference was meeting a tonne of interesting people, many I’ve had on twitter for ages, but never met IRL. Being able to talk about pretty complex technical things with others who have the same (or greater!) enthusiasm is refreshing. And hearing about how some people are using the existing tech out there in terrifyingly clever and large setups is pretty inspiring sometimes.

Great stuff!

#sotr14

Scaling your hosting environment with free services

There comes a point in life when your current hosting environment needs to scale up, and quickly. It might just be that one of your clients gets very popular very quickly, or the gradual adding of sites and resources starts to make services fail. Thankfully there are a few tricks to make life a little bit easier. The less services you can run on your VPS, the more of that precious RAM can get put back into mySQL/Apache etc.

Free Domain based email (https://domains.live.com)
By ‘Domain based email’ I’m referring to sending mail as me@mydomain.com – and not having your webserver or mail client send it ‘on behalf of’, which I think doesn’t look massively professional (and indeed, is a lot more likely to set off the spam filters). Up until a year or two ago, I’d use Google Apps – annoyingly they’ve cut off their free tier; so now the only really free alternative is outlook.com – see https://domains.live.com/signup/signupdomain.aspx – you can do IMAP mail too, so it’s a very useful service. Saves you running your own mail service!

Free Transactional email with Mandrill (http://mandrill.com/
Speaking of sending email via your webserver – ever had mail from your webserver go into people’s junk mail? Ever had your server blacklisted for sending too much mail? Or just been rate limited by Google et al? These are fairly common complaints when running your own VPS: I’ve recently setup Mandrill to deal with all my server-side mail – so far I must say I’m rather impressed. There’s two main ways to do this – either setup your app itself (be it WordPress/Drupal etc) or make your server’s MTA (such as sendmail/postfix) send via the Mandrill SMTP service (or indeed via the Mandrill API directly from your code). Mandrill is free for the first 12,000 mails per month, which will probably be enough for most people I’d wager.

Free Email Troubleshooting (http://mxtoolbox.com/)
This is a stupidly useful tool for checking all things email related, actually, domain related too – try the domain health tool, should hopefully flag up any issues (although some of the warnings are a little strict).

Free AntiSpam service (http://mollom.com/)
It’s not flawless, but if you run a low traffic blog (sub 50 legit posts a day) it’s better than nothing. Has the requisite Drupal/Wordpress modules too.

Free CDN and DNS from CloudFlare (www.cloudflare.com)
This is a bit of a no brainer if you’re not using SSL – change your nameservers over to cloudflare, and you can take advantage of some basic threat management, and a free automatic CDN for static assets (not HTML). Just remember to install the apache/wordpress/drupal plugin to get the referring IP address back from CloudFlare – this is a reverse proxy, so if you don’t understand the implications, do a bit of reading first. You get some (very) basic analytics too, which leads me to..

Free webstats via Google Analytics (www.google.com/analytics)
OK, so Google Analytics has been around for ever, and although Google probably uses your data for everything, it’s still a very useful service indeed. Stats, stats and more stats. Saves you running a serverside stats package (unless of course, you need to track things like direct access PDF downloads – although there are some tricks via URL rewriting which can get around that).

Free Web page testing (http://www.webpagetest.org/)
This is really useful when you’ve setup a site on cloudflare, and you want to test the reverse proxy – but just generally, it’s one of the best site insight tools I’ve seen in terms of loading of assets, working out where they’re coming from etc.

So it’s amazing what you can get for nothing these days: naturally, all these services have their limitations – i.e, 12k emails a day from Mandrill, no SSL for the free version of CloudFlare, etc, but for a lot of websites out there this won’t be an issue.

A response to ‘What does your VLE say about your institution?’

In response to “What does your VLE say about your institution?” on the TALL blog at Oxford University – they pose the statement:

The evolution of an institution’s VLE is the narrative of that institution’s attitude towards, and relationship with, learning technologies.”

To me, VLE design usually falls into two categories: 1) an open source solution, such as Moodle/Sakai, and 2) bespoke applications, usually written in house.

Out the box, OSS solutions often don’t fufil the required criteria from a VLE: they all require customisation, setting permissions/roles, branding etc. Even then, the chances of them doing what you want them to do without a fair bit of work is fairly slim.

It’s a classic – “here’s a potential solution, how can we manipulate it to our needs to solve our problem” – approach.

From the other end, bespoke solutions reverse this thinking  – “we have a problem, how do we solve it, let’s make a solution specific to our needs.”

The latter approach usually requires more money, but can ultimately save time and effort by not trying to manipulate an existing system – to me this shows a more mature approach to online learning: a badly configured OSS solution (such as found in the majority of Schools – less so in Higher Edu) just represent either underinvestment, or a lack of knowledge in what is required from a website, let alone a fully fledged VLE.

I do believe VLEs are pretty much representative of an institution’s relationship & attitude to online learning (and indeed the web as a whole), but a good VLE deployment requires more than just good intentions, and needs investment, and more importantly – continual development – which is where most of them fall short.

Creating an Ajax mySQL Full Text Search in cfWheels

I’m quite a fan of mySQL full text search – don’t get me wrong, it’s not exactly Google, and it has some annoying limitations, but for a quick and easy search function, it’s not half bad. It’s particularly useful for intranets, or just listings in admin section when you might have to delve through a few thousand records otherwise.

This example uses cfWheels, and most importantly, the cfWheels plugin Remote Form Helper.

Step One: Set up an index on your Table in MySQL.

Note: you can only do this on myISAM tables, not innodb.
I usually do this is navicat, where it’s very painless to do indeed. (simply right click a table, select Design table, then select indexes: give the index a name, and select the columns in the table you want to search: make sure the index type is set to fulltext).

For the ‘navicat-less’, try something like this:

CREATE TABLE `articles` 
(
  `id` int(11) NOT NULL default '0',
  `title` varchar(125) default NULL,
  `topic` varchar(25) NOT NULL default '',
  `author` varchar(25) NOT NULL default '',
  `createdAt` datetime NOT NULL default '0000-00-00 00:00:00',
  `body` text NOT NULL,
);

Then add the index:

ALTER TABLE articles ADD FULLTEXT(title,body);

The attributes in the Fulltext() function are the column names we wish to search (I believe there’s a limit of 16 columns).

In your mySQL conf file, add this line under the [mysqld] section:
ft_min_word_len=3

This sets mySQL to search for words of 3 characters or more, rather than the annoying default of 4.

Step 2: cfWheels setup
Download and install/activate/configure the Remote Form Helper plugin.
Make sure you remember to add the line:

addFormat(extension="js", mimeType="text/javascript");

To your config/settings.cfm file as mentioned in the plugin documentation, and also include the wheel.jquery.js file distributed with the plugin. (oh and jQuery itself, obviously).

Step 3: Search Views & Controllers

We need to create at least three files.

Firstly, our controller, /controllers/Search.cfc:

<cfcomponent extends="controller">
<cffunction name="init">
<cfscript>
// This provides bit is essential!
 provides("html, json, js"); 
</cfscript>
</cffunction>

<cffunction name="q" hint="The Main Search Router">
<cfscript>
// If search terms is incoming, and is an ajax request
if(structkeyexists(params, 's') AND len(params.s) GTE 3 AND isAjax())
{   
// searchArticles() function returns our HTML directly to the data attribute 
renderWith(data=searchArticles(params.s), template="articles");
}
else
{ renderNothing() };
</cfscript> 
</cffunction>

<cffunction name="searchArticles" access="private" hint="Our FullText Search">
 <cfargument name="s" required="yes" type="string" hint="The Search Term">
 <cfquery name="q" datasource="#application.wheels.datasourcename#" maxrows="50">
 SELECT id, title, topic, author, body, createdAt FROM articles
 WHERE MATCH (title,body) AGAINST (<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.s#">);
 </cfquery>
 <cfloop query="q">
  <cfset q.content[currentrow]=formatResult(string=content, highlightT=arguments.s)>
 </cfloop>
 <cfreturn q />
</cffunction>

<cffunction name="formatResult" access="private" returntype="string" hint="Returns a shortened teaser of the main content, with highlighted search terms">
 <cfargument name="string" type="string" hint="The String to truncate">
 <cfargument name="highlightT" type="string" hint="The term to highlight">
  <cfset var newString="">
  <cfset newString=highlight(text=truncate(stripTags(arguments.string), 400), phrases=arguments.highlightT)>
  <cfreturn newString />
</cffunction>

Next, some view files:

/views/search/index.cfm (where our search form and results output will live)

<cfparam name="params.s" default="">       
<cfoutput> 
<!---Form--->
<!--- NOTE THE remote=true attribute!--->
#startFormTag(controller="search", action="q", id="advancedSearchForm", remote="true")#
#textFieldTag(name="s", placeholder="Search..", label="Search For", value=params.s)#
#submitTag(value="Search")#
#endFormTag()#
<!---Output--->
<div id="results"></div> 
</cfoutput> 

/views/search/articles.js.cfm – This formats our returned dataset for return to the page

<!--- loop over and save the output in a variable--->
<cfsavecontent variable="resultSet">
<cfoutput>
<h2>Articles which match your search:  (#arguments.data.recordcount# results)</h2>
<cfloop query="arguments.data">
<cfoutput>
  <h3>#linkTo(text=title, controller="articles", action="view", key=id)#</h3>
  <h4>#Dateformat(createdAt, 'dd mmm yyyy')#</h4>
  <p>#content#</p>
</cfoutput>
</cfloop>
</cfsavecontent>

<!--- Use the plugin to shove back the results to the page --->
<cfoutput>
#pageReplaceHTML(selector="##results", content=resultSet)#
</cfoutput>

That’s it!
So the form at /views/search/index.cfm posts via ajax to the ‘q’ function in /controllers/Search.cfc.
That q() function returns the html via renderWith(), and the remote form helpers pageReplaceHTML() function posts the results to the results div on the calling page.

The nice part about this approach is that you can use that q() part with a switch/case statement to call any function to return data as you need. You could even go to a different search source, such as a Google Search Appliance, which I’ll cover soon.

Changing a few things!

Sorry to confuse everyone, but I’ve been changing some things… please bear with me whilst I move over posts/comments etc from the old blog. I’ll be adding redirects soon.

I decided to move over WordPress from Mango Blog – that may make the more staunch CF developers in the world shudder, but Mango Blog just wasn’t keeping up with the times for me 🙂

Besides, the right tool for the right job eh?

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.

 

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.