Adam Howitt's Blog

Nov 13
2008

TinyMCE ColdFusion Image Browser

I love tinyMCE but because I use ColdFusion I've not been able to use the built image browser.  The API provides the hooks required to roll your own so I figured someone else might have done the heavy lifting and sure enough, I found a CF based image browser plugin.

I installed Doug Jones' CF_iBrowser v1.0.4 to work with TinyMCE 3.2.1 in BlogCFC.  His instructions were excellent and you shouldn't have too many issues.  Things to look out for:
  1. At the time of writing the JavaScript function OpenFile in index.cfm doesn't trigger the onChange property for the imageURL field (at least in my browser) so I had to manually add the line
    srcWin.ImageDialog.showPreviewImage(fileUrl);
    before the
    window.close()
    line.  This manually calls the showPreviewImage function that sets the image dimensions, otherwise your image will be added with a height and width of 0px!
  2. If you are using BlogCFC v5.9.1.001, you'll need to manually comment out the image browser library since the setting for turning off the file browser didn't seem to work for me.
  3. Check to see that you don't already have a an option specified in the tinyMCE.init() code block - mine was set to the PHP based plugin that comes bundled for advimage.  Simply replace that value with the line in step 3 of Doug's instructions.
Overall, great experience with the plugin once I realized I was looking at the wrong tinyMCE.init block and kudos for Doug for a job well done!

Feb 10
2008

WalkJogRun Popfly mashup on Facebook

In just over an hour with no prior experience, I created a mashup using Microsoft's Popfly application to allow WalkJogRun visitors to enter their username to see an overview of their running routes plotted on Microsoft's Virtual Earth.  Next, through the magic of the Popfly Facebook plug-in, I can share my new application with all my Facebook friends.

I learned about Popfly just over an hour ago when my wife was reading the New York Times and asked me if I had heard of it.  From her description it sounded like a Yahoo Pipes wannabe so my initial reaction was lukewarm.  As I kept reading my Michael Crichton novel, my brain kept spinning so I finished the chapter and turned on my laptop to play around.  Within minutes I had mashed up my geo-tagged Flickr photos with Virtual Earth and so I looked at the Yahoo code block to see what kind of magic was occurring.

The blocks can all be inspected and there are two important pieces:

  1. Block Description: this XML document describes your application and defines the functions in terms of inputs, outputs and any custom object definitions.
  2. Block Code: this is JavaScript code and implements your methods


In my case I used the Yahoo Flickr code as a starter since it has a "getUserPhotos" method very similar to an existing function at WalkJogRun called getUserRoutes.  After three or four attempts I was able to remove any unnecessary code from the other functions used by the Flickr block, and had a working block that would retrieve the XML from my site and generate an RSS feed of routes by user.


Here is my block description

<?xml version="1.0" encoding="utf-8"?>
<block xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="http://www.popfly.com/schemas/blockschema.xsd"
  class="WalkJogRunClass">
  <providerName>walkjogrun</providerName>
  <providerUrl>http://www.walkjogrun.net/</providerUrl>
  <providerLogoUrl>/images/wjrlogo_tag.gif</providerLogoUrl>
  <blockIconUrl>/images/tshirt_pocket_6x6.png</blockIconUrl>
  <suggest output="geodisplay" input="userinput"/>
  <operations>
    <operation name="getUserRoutes" callMode="auto">
      <description>Get user routes.</description>
      <inputs>
        <input name="userName" required="true" type="string">
          <description>username</description>
          <defaultValue>Earnshavian</defaultValue>
          <constraints />
        </input>
      </inputs>
      <outputs>
        <output isArray="true" type="custom" object="Route" />
      </outputs>
    </operation>
  </operations>
  <objects>
    <object name="Route">
      <field name="Route Number" type="rn" isArray="false" />
      <field name="latitude" type="latitude" isArray="false" />
      <field name="longitude" type="longitude" isArray="false" />
      <field name="pintext" type="pintext" isArray="false" />
      <field name="Description" type="desc" isArray="false" />
      <field name="Long Description" type="longdesc" isArray="false" />
      <field name="Distance (m)" type="dist" isArray="false" />
      <field name="Distance (km)" type="distkm" isArray="false" />
      <field name="Owner" type="submit_by" isArray="false" />
      <field name="Route ID" type="rid" isArray="false" />
      <field name="Elevation string" type="elev8ngraph" isArray="false" />
      <field name="Severity" type="severity" isArray="false" />
      <field name="isPrivate" type="prv" isArray="false" />
      <field name="Create Date" type="c_dt" isArray="false" />
    </object>
  </objects>
</block>
and here is my block code
function WalkJogRunClass() {
}

WalkJogRunClass.prototype.getUserRoutes = function(userName, number) {
 // Retrieves a user's routes.
 //
 // userName (required): WalkJogRun user name

    // get userid
    var params = "&userid=" + escape(userName);
    var routes = this._getWalkJogRunRoutes(params);
   
    return routes;
};


WalkJogRunClass.prototype._getWalkJogRunRoutes = function(extraParams)
{
    var root = this._getWalkJogRunXml(extraParams);
    var routes = this._getRouteArrayFromXml(root);
   
    return routes;
};

WalkJogRunClass.prototype._getWalkJogRunXml = function(params)
{
    var root = environment.getXml("http://www.walkjogrun.net/functions/act_getRoutes.cfm?1=1"+ params, "walkjogrun");

    var rsp = root.getElementsByTagName("markers")[0];

    if(rsp == 'undefined')
    {
        var errMsg = "walkjogrun returned an unknown error";
        throw errMsg;
    }     
   
    return root;
};

WalkJogRunClass.prototype._getRouteArrayFromXml = function(root)
{
    var routeArray = new Array();

    var routes = root.getElementsByTagName("marker");
    var count = routes.length;
    for(var i = 0; i < count; i++)
    {
        var route = routes[i];
        var rn = route.getAttribute("rn");
        var lat = route.getAttribute("lat");
        var lng = route.getAttribute("lng");
        var desc = route.getAttribute("desc");
        var longdesc = route.getAttribute("longdesc");
        var dist = route.getAttribute("dist");
        var distkm = route.getAttribute("distkm");
        var submit_by = route.getAttribute("submit_by");
        var rid = route.getAttribute("rid");
        var elev8ngraph = route.getAttribute("elev8ngraph");
        var severity = route.getAttribute("severity");
        var prv = route.getAttribute("prv");
        var c_dt = route.getAttribute("c_dt");
        routeArray.push(new Route(rn,lat,lng,desc,longdesc,dist,distkm,submit_by,rid,elev8ngraph,severity,prv,c_dt));
    }
   
    return routeArray;
};

function Route(rn,lat,lng,desc,longdesc,dist,distkm,submit_by,rid,elev8ngraph,severity,prv,c_dt)
{
    this.rn = rn;
    this.latitude = lat;
    this.longitude = lng;
   
    this.desc = desc;
    this.longdesc = longdesc;
    this.dist = dist;
    this.distkm = distkm;
    this.submit_by = submit_by;
    this.rid = rid;
    this.elev8ngraph = elev8ngraph;
    this.severity = severity;
    this.prv = prv;
    this.c_dt = c_dt;
    this.toString = function()
    {
        return "<a href='http://www.walkjogrun.net/index.cfm?rid="+this.rid+"'>" + environment.escapeQuotes(this.desc) + "</a><br />"+
        "Description: " + environment.escapeQuotes(this.longdesc) + "<br />"+
        "Distance: "+ environment.escapeQuotes(this.dist) + " miles /" + environment.escapeQuotes(this.distkm)+" km<br />"+
        "Created By: " + environment.escapeQuotes(this.submit_by) + "<br />"+
        "Create Date: " + environment.escapeQuotes(this.c_dt) + "<br />";
    }
    this.pintext = this.toString();
}

 

Next I added the Virtual Earth application to a Popfly mashup page along with my WalkJogRun block and connected them so the geoRSS pumps into Virtual Earth's addPushpin method.  Some case issues were causing some trouble initially but after a couple of attempts, my block was publishing the format required for auto-discovery of inputs for Virtual Earth and I could run the application.  
WalkJogRun User Contributed Route Viewer mashup

To my surprise, it asked me where I would like to share my new mashup including the option to share on Facebook or add it to my profile.  By adding it to my profile it added the Popfly application to my profile so that my mashups are now shared.  Best of all, it gave me the ability to share my embedded Facebook app with my friends.

Process Overview 

So the overview of the whole process is as follows:

  1. Login to Popfly
  2. Create a block by editing the block description and block code or modifying an existing app with similar functions
  3. Save and publish the block
  4. Create a mashup by adding your block and any other components
  5. Save and publish the mashup
  6. Publish it to your Facebook profile
Summary

That took just over an hour for me since I already had a page generating XML based on URL parameters.  I'm planning to add more functions to my block soon too but for now, that's a great start.

How does Microsoft Popfly compare to Yahoo Pipes? 

It's a completely different beast.  Yahoo Pipes for me offered great promise but without adding too much value - just a way to repurpose and aggregate multiple RSS feeds.  Microsoft Popfly goes much further in adding end points to the feeds with visualization tools and the ability to share your applications more widely than I had imagined.  It's certainly worth an hour of ANY programmer's time.

Apr 30
2007

See Howitt Run

I have 2 weeks left to raise the remainder of my $3,900 goal for Leukemia and Lymphoma and I need your help. I'm in Week 15 of the Team in Training plan to run the San Diego marathon on June 4th.  Saturday was an 18 mile run out in Darien, IL and my legs are still tight from the challenge.  It's a distinct change from the flat course along Chicago's lakefront.  We've got a 10 mile run this weekend before the next jump to 20 miles the following week.

I was initially running in memory of my Aunty Moreen who passed away in 2004 after a 6 year struggle with bowel cancer.  The weekend after I started my training we discovered that my wife's 3 year old niece Haidyn had been diagnosed with ALL (Acute Lymphoblastic Leukemia).  Over the past 12 weeks she has been a tough little girl fighting infections caused by the drugs reducing her immune response and dealing with the loss of hair caused by the chemo.  At mile 16 on Saturday as I felt like my legs couldn't keep going I remembered the pain her and her family have experienced and it pushed me to the end.

If you have ever read one of my posts on SQL, AJAX, ColdFusion or even my most popular post on how to fix a bug on your Motorola DVR, please take time to donate.  I appreciate anything you can spare.

Feb 09
2007

ColdFusion Development

Despite the bottomless pit of information available in Google, I find myself falling back on a core set of sites for the answers to my questions.  Through the magic of Google Co-op I have created a ColdFusion Development Search Engine.  It is a hand made list of sites I trust for answers to my ColdFusion questions.  I've also included and tagged JavaScript, SQL, Fusebox and AJAX resources with the idea that anything related to CF that I use on a daily basis to do my job is fair game.

If you can't find what you are looking for, let me know which site provided your answer and I can add it to the list after reviewing it.   

UPDATE: The URL for the search is http://www.webdevref.com 

Jan 05
2007

WalkJogRun on Fox News

I'm delighted to report that WalkJogRun was featured on Fox News two days ago for the Cool Websites section after I tipped off Nancy Loo about the site.  I was surprised in her coverage when she said she had already featured the site at one point but I missed it I guess.  This time I was prepared and have my first YouTube video up now.  I know, I'm probably the last person in the tech world to do this but this is the first thing I felt was worth it.

For anyone who hasn't read anything about WalkJogRun, it's over 3 years old and is a community for runners, cyclists, walkers and anyone else to create and share routes.  It is based on Google Maps and uses custom AJAX, ColdFusion and is built on BlueDragon 6 with MySQL on an Ubuntu server. 

 

Dec 14
2006

Smart Marketing with Google Analytics order tracking

Edward had asked about the code I used in Google Analytics to track each transaction. This allows you to see a wealth of information, not just product revenue through the website.  When you add the transaction logging to your order complete or "thank you" page, google updates the basic reports it offers with an extra column so each minute piece of data can be cross referenced against the revenue it generated.  For example:

  1. New vs returning customers - how much revenue (total and average) did each segment generate.  This report can help you focus your marketing efforts to either increase first time spend or provide discounts for returning users to encourage them to buy more and reward their loyalty.
  2. Which referring sources were responsible for the most revenue on the site?  The report columns are all sortable so rather than see which affiliate drove the most traffic to your site, which affiliate drives the biggest spenders?  It can help you review your marketing spend with affiliates so you can focus your budget on the people whose visitors are more likely to buy from you.
  3. What is the ROI on your Google adwords campaign?  One client spent nearly 3000 dollars per month on adwords campaigns that yielded just 21 dollars in revenue and one sale.  He had already asked why Google drove 50% of his traffic and MSN and Yahoo were so small in comparison.  I suggested that moving his adwords budget to the search engines without organic presence would make sure he had the exposure he needed at MSN and Yahoo and help adjust the balance.

The CF code I use comes in two parts.  The first creates a structure of the items in his order in the act file after his order is processed:

<cfscript>
    utm_trans = structNew();
    utm_trans.order_id = client.order_header_id;
    utm_trans.affiliation = 'Name of the Affiliate program';
    utm_trans.total = variables.grandtotal;
    utm_trans.tax = variables.grandtaxes;
    utm_trans.shipping = variables.grandshipcost;
    utm_trans.city = get_shipaddress.city;
    utm_trans.state = get_shipaddress.region;
    utm_trans.country = 'USA';
    utm_trans.items = queryNew('order_id,sku,productname,category,price,quantity');
    //add code to populate utm_trans.items query here
</cfscript>
The second chunk of code is responsible for putting the hidden form on the page in the correct format for Google.  You may note that I use cftry/cfcatch around it all with no action in the cfcatch.  I did this in case anything odd occurred to prevent the google tracking code from creating a customer service call for the client.  You could easily add a simple cfmail to say there was a problem
<cfoutput>
<cftry>
<form style="display:none;" name="utmform">
<textarea id="utmtrans">UTM:T|#utm_trans.order_id#|#utm_trans.affiliation#|#utm_trans.total#|#utm_trans.tax#|#utm_trans.shipping#|#utm_trans.city#|#utm_trans.state#|#utm_trans.country#
<cfloop query="utm_trans.items">UTM:I|#utm_trans.items.order_id#|#utm_trans.items.sku#|#utm_trans.items.productname#|#utm_trans.items.category#|#utm_trans.items.price#|#utm_trans.items.quantity#
</cfloop></textarea>
</form>
<script language="text/javascript">
__utmSetTrans();
</script>
<cfcatch type="any"></cfcatch>
</cftry></cfoutput>

The first chunk creates the transaction wrapper (starts with UTM:T) then I loop over my collection of items to build the item rows (start with UTM:I).  The last part is the __utmSetTrans() which sends the transaction to Google.

If you have found this useful or have any questions about Google Analytics, feel free to let me know in the comments section.  I have worked very closely with both the code implementation and reviewing the results to make recommendations. 

Oct 23
2006

Google Analytics trouble

I've been helping a client setup Google Analytics, specifically adding the e-commerce tracking.  As with most analytics packages, stats are updated as part of a batch every few hours so it's tricky to troubleshoot the code when things aren't going well.

I was getting the tracking code to work, even on the SSL encrypted pages but the e-commerce tracking piece wasn't reporting any revenue.  The tracking code is offered in two flavors, either an inline chunk of script to execute or called from the page onload function.  To minimize disruption to the dsp_header.cfm in use throughout the site I thought I would add the inline code on the receipt page instead.  No dice.  Here is the problem: it fails silently on an AJAX call so I was stuck. LiveHTTPHeaders to the rescue. This neat little Firefox plugin works like Fiddler but is built into your browser.  Turn it on and each HTTP request is logged either in the sidebar or a new tab. 

With LiveHTTPHeaders turned on it became clear the inline JavaScript fragment didn't work so I moved my call to the dsp_header.cfm file with a condition based on whether you were viewing a receipt page or not.  LiveHTTPHeaders cheerfully reported that I was now sending data to Google.  All I need to do now is wait 4 hours to see if the data shows up.

Really loving Google Analytics - has anyone else used it?  How does it stack up to other analytics packages you have seen?  I love the custom funnel pages to be able to track e-commerce activity and the cross-tabbing of most data points to be able to dig in by city, country etc.

Jul 07
2006

How to Design a large AJAX Application

In the guide which you can download from the link below I'll cover the process I have developed in the course of implementing two AJAX applications as a developer for Duo Consulting  in Chicago.  This approach has made it easier for me to work with the design team, produce estimates for this type of project and communicate what is involved each step of the way to the project managers for scheduling purposes.

Download the PDF guide (105kb)

If this sounds like the type of work you enjoy and you live in Chicago or are willing to move here check out our careers page as we are looking for talented ColdFusion developers.

Jun 29
2006

CFUnited mid-conference roundup

The sun came out in DC yesterday as the conference began and the city is breathing a sigh of relief. ColdFusion can do that :-) I'll try and keep it short but you all know how I can ramble on...

CF Roadmap keynote: I agree with my coworker Kelly that the Adobe influence on CF looks ominous. It's clear that PDF integration for CF jumped the queue at the expense of other key features that were left out. "Scorpio", the next release of ColdFusion, will include some new PDF form based tags to allow users to fill forms in online. That's about it from that angle. Flex 2 was launched officially yesterday and Ben Forta gave a demo of a wizard to build a master-detail web application without writing any code. I could see us thinking about this for our CMS duoCMS for the content administration since it offers some saucy looking UI out of the box.

Sean Corfield talked for an hour on factories in the context of ColdSpring, a framework Duo Consulting (my employer) can and should leverage for all of our CMS cfcs. As it stands, the CMS is built in a way that forces us to keep only one version on each server. ColdSpring would allow us to maintain multiple builds of the code so that a client who was built on duoCMS 1.1.345 would have identical CMS to another client operating on the same build number. This is HUGE for Duo since we can easily identify a fix for one customer and know that it can be fixed for all clients on that build version. It also allows us to plot upgrade paths for clients based on their build number. I know this isn't as exciting as my design buddy Jeff's new addition to the family but it's a close second :-) The other advantage of the framework is that it allows us to hotswap different databases for an individual client so they are still duoCMS but the connectors for getting and setting information in the database are broken out. Fusebox 5 release candidate 1 was also released on Tuesday but I haven't had a chance to look at the details yet.

Note: if you are wondering where I have the time for this in the middle of the day, Kelly is attending the Microsoft Atlas presentation on their attempt at an AJAX library. The part I saw was too intense about Visual Studio so I skipped out.

Next up was Joey Coleman, a thoroughly entertaining speaker describing the concept of having turning yourself into an IPO. Not about starting a business but just managing your career as a brand to maximise your earning potential. Things like getting testimonials from your manager about the work you do and taking stock once a year with an annual report. This can make you more productive and help you realize whether you are stagnating and need more training or need to dig in a little more.

Douglas Ward gave an average presentation on Fundamentals of Usability. The issue was that it was another Jakob Nielsen style "Don't do this, don't do that" and not enough "You should do this". Darn negativity drives me crazy. If anyone has any good recommendations on authors or books describing what should be done please let me know. The only good thing I took away was his analogy of your website as a billboard and the notion that an effective billboard is short, sweet and to the point "Good chicken next exit at Bob's" vs a long winded flowery pile of text which doesn't help the user get to their end point.

An Adobe presenter, Sarge Sargent gave a disappointing talk on gateways with little substance so it looks like I will need to dig in more myself.

John Ashenfelter talked about Agile Programming as a methodology. There was a lot of content and theory but less practical. It would have been better if he had revised his material to focus on the audience: every coder here either builds websites or reports so with that assumption I wanted him to say "a client wants a new website, here is how the process works". I still have pages scribbled on the topic if anyone wants detail. The nugget here was the use of ANT as a tool for web development. Built into the tools most of our developers use it allows you to automate the deployment of applications and the configuration to setup a project. In our environment this would reduce some of the lead time necessary to set our machines up for maintenance of client websites and grab local copies. He also recommended some great development books he believes every developer you hire should read as part of their job requirements.

This morning, slightly less awake I watched Joe Rinehart introduce Model-glue unity as a framework for rapidly developing ColdFusion applications with a ruby on rails for ColdFusion feel. In five minutes he built a rudimentary blog from scratch typing every line of code while we watched in awe. The key component is something called reactor which works on most database platforms through JDBC. It understands the database design and then creates the pages with Model-glue to list, edit, add and delete items.

Lastly, I watched a friend from Atlanta present the benefits of running BlueDragon on .NET - our company sysadmin and I will need to try a demo of this because the benefits are huge including the ability to leverage application pools allowing you to start and stop one part of a website without bringing down the whole site or any other clients on the server. It also means better monitoring abilities and helps us see which client site is bringing the others down.

Food is being served now so I'll wrap up. Great conference, great content and a really good crowd.

May 06
2006

Spanish Caravan

New countries, new languages, new problems. Fixed bug in Internet Explorer for WalkJogRun.co.uk where Spanish Characters broke the tool!  Tip: encoding="ISO-8859-1" in the xml declaration allows the latin characters.  Firefox was good enough to work out what was going on, IE on the other hand thought it was Chico time and turned off the radio.