Friday, May 25, 2012

Converting Letters in Phone Numbers to Their Corresponding Digits

In a recent post, I mentioned that the feature that allows a user to tap on a phone number in their smartphone web browser and dial that number doesn't work if the phone number contains letters.  Well, some of the phone numbers in the database tables I'm working with contain letters (usually to spell out building abbreviations) so I needed to convert those letters into the corresponding digits on American phones (apparently it's not an international standard).

Before writing my own function to do this, I did some Googling and was a bit surprised to find that while folks have written routines in CFML to strip out dashes and such, no one had really written (and then published) a routine for this task.  So here's mine:

(Shrug) Nothing fancy, but it works. If you need to do additional adjustments to the phone number (like I did), you can just add them to this function or put them in separate functions (which is probably better).

Sunday, May 20, 2012

Post-cf.Objective() Thoughts

Just got back home from cf.Objective().  During the plane trip home, I wrote down a few paragraphs about the sessions I attended that I'll share now before kicking back for the rest of the day:

  • Marc Esher's session on threads and concurrency was excellent.  If you implement or plan to implement concurrent code execution in your CFML applications, you need to look at the CFConcurrent library he developed (available on RIAForge and GitHub).  The library utilizes the Java Concurrency Framework (JCF) and makes it possible to return results/data from with a thread (something you can't do with <cfthread>), schedule/defer threaded actions, and set up "threading within threading."

  • Nathan Strutz provided so much information and so many examples of using LESS to manage and generate CSS that I missed a few things trying to take notes.  No worries, though: he put up everything from his presentation on GitHub, including a number of examples and demos that show you some best practices on how to add LESS compilation into your development and production workflow.  And not long after the session, Matt Gifford made his own contribution to the LESS toolset:  a CF Builder extension that lets you do the compiling within the IDE.

  • Curt Gratz's session on mocking covered the reasons why you should mock data and objects when you unit testing, then showed attendees how MockBox makes it easy to mock function calls and their results on the fly.  As someone still finding his way regarding unit testing, I found his one walkthrough on how to make sure you're always testing a single function, and not a parent or dependent function, very helpful.  And even though MockBox is part of the ColdBox family of projects, it can be run as a standalone product, so you can make it part of your unit testing strategy regardless of what framework you use.

  • Billy Cravens also did an excellent job with his session on authenticating users via social networks.  Adding Google, Facebook, and Twitter-based authentication to your site is really now just a matter of getting an API and private key for your site from those services and making use of the available.  His demo code, available on GitHub, shows examples for all three service providers within a simple application built with the FW/1 framework.

  • I attended Denard Springle's multi-factor authentication workflow session mostly out of curiosity, as it's not something I can really implement with my particular user base.  But he showed us his technique of securing URL and form variables submissions, which involves hashing the variable names and encypting the variable values.  That's something I've never seen anyone else do.  Slides from his demo that illustrate that technique are available from his collection of presentation slide decks (look for the "Multi-Factor Authentication" one).

  • A room for conducting ad-hoc discussions or coding sessions was provided, and I ended up hosting a half-hour session on CFML applications in higher education.  The seven or so of us described some of the applications we've worked on, and as I suspected there was quite a diversity of applications, mostly built in support of the administrative processes at our schools rather than the educational process itself.  But I did learn that a few of us use (or are in the process of implementing) the Jasig CAS single sign-on tool, and that there would be interest in having some kind of robust form/survey tool (possibly with plugins like one to handle online payments) that we could set up such that non-programmers could create what they needed from it.  The question was raised as to whether it would make sense to augment Mura CMS to create such a thing.  We didn't discuss or make any further plans beyond that, so whether the discussion will continue in some form (move further forward) online remains to be seen.

As my conclusion, I will say that the conference was excellent as always (hardly a unique opinion, I know).  I've been to cf.Objective() before (in 2008), but this was my first time attending at the current venue, which is the Nicolett Mall area of Minneapolis.  It's a very pedestrian-friendly area of the city, and with the near-perfect weather we had it was quite lively.  It gave us a lot of options for dining and socializing after the sessions, and being able to hang out with other members of the community is one of the great benefits of the conference.

Thursday, May 3, 2012

Simple Technique For Creating Multiple Twitter Bootstrap Popovers

One of the JavaScript/jQuery plugins that comes with Twitter Bootstrap is the Popover plugin, which lets you create windows of content that appear when you hover over or focus on a DOM element.  They behave like tool tips but are larger and better suited to support styled content.

As noted in the documentation, the title and content of an individual popover can be coded in one of three ways:  via HTML attributes within the DOM element that triggers the popover, by assiging text values to the title and content properties when applying popover functionality to the DOM element, or by assigning functions that return markup to those title and content properties.  Here's an example of the last of those options:

$("#pop1".popover(
    {
        title: getPopTitle(),
        content: getPopContent()
    }
);

function getPopTitle() {
    return "Title 1";
};
		
function getPopContent() {
    return "Content 1";
}

The page I was working on needed to have several popovers, and the popover content included styled text: paragraphs, bolded text, etc. I didn't want to have to put that content into HTML attributes or long string values, nor did I want to code several different invocations of the popover function.

So what I decided to do was to create hidden blocks of HTML to hold the popover content and associate each set of "content" (title and content) with a unique id:

<style>
    .popSourceBlock {
        display:none;
    }
</style>

<div id="pop1_content" class="popSourceBlock">
    <div class="popTitle">
        Title 1
    </div>
    <div class="popContent">
        <p>This is the content for the <strong>first</strong> popover.</p>
    </div>
</div>

<div id="pop2_content" class="popSourceBlock">
    <div class="popTitle">
        Title 2
    </div>
    <div class="popContent">
        <p>This is the content for the <strong>second</strong> popover.</p>
    </div>
</div>


Then I assigned ids to the DOM objects that would trigger the popups when hovered over and gave them a CSS class of "pop". I opted to use one of the icons provide by Bootstrap to trigger the various popovers:

<i id="pop1" class="icon-question-sign pop"></i>
...
<i id="pop2" class="icon-question-sign pop"></i>


Finally, I used jQuery's each() function to loop through all the DOM elements with a class of "pop", grab the id value and use that to locate the matching content to associate with each popover:

$(".pop").each(function() {
    var $pElem= $(this);
    $pElem.popover(
        {
          title: getPopTitle($pElem.attr("id")),
          content: getPopContent($pElem.attr("id"))
        }
    );
});
				
function getPopTitle(target) {
    return $("#" + target + "_content > div.popTitle").html();
};
		
function getPopContent(target) {
    return $("#" + target + "_content > div.popContent").html();
};

With this code in place, I can add new popovers by creating additional hidden HTML blocks and DOM elements with the "pop" CSS class and an id that follows the pattern I've established.

Another nice aspect to this is that, down the road, I could put the page with the HTML blocks in a directory where a web designer or even a client with some basic HTML skills could edit the content, and I could call that file into my page programmatically. So long as they didn't add new content blocks, they could tweak the wording of the content without having to call me to do it (assuming I can trust them not to mess up the HTML formatting).

Wednesday, April 4, 2012

Letters In Place of Numbers in Phone Number Links on Mobile Phones

Part of an upcoming project involves displaying office phone numbers on a web page such that, if the page is being viewed on a smartphone, tapping on the number pulls up the phone's dialing program with the number filled in and the user just needs to start the call.

Some of the phone numbers in question use letters in place of numbers (like "1-800-555-CFML" as an example) and I wondered if such numbers would work.

The answer is "probably not," as it's not something supported in the spec (the spec being RFC3966).  Tried it on my Galaxy Nexus anyway: no dice.  So I'll have to convert any such letters to their numerical equivalent

Some other things I learned today:

  • Per the W3C recommendation regarding "click-to-call" links, you should always include the "+" sign in front of the number to support international callers ("+1-800-555-CFML").
  • You can also use "." as a separator instead of a "-", and you can get away with using both types of separators in the same number.

Sunday, December 4, 2011

Announcing tableSnapshots: A ColdFusion Tool For Preserving and Reverting Database Table Data

Several months ago, I was doing some functional testing on a web application that was taking forever. Each run of the test resulted in changes to the data in several tables, and in order to reset the test I had to go into the tables and undo the changes. It occurred to me that it would be nice to have a tool or script that could preserve the current table data and then later write that version of the data back to the tables.

That marked the beginning of the tableSnapshots tool.

Creating the initial version of the tool just to suit my own needs was quick and easy compared to the work I've put into the tool since then to make it worth sharing, but I'm pretty happy with the result.  It's essentially a small web application that you can drop into any folder on your ColdFusion-enabled (ColdFusion 8 or 9) web server. Once you update the configuration settings in its Application.cfc file with the name of your datasource, the tool enables you to:

  • Take a "snapshot" of one or more of your database tables, storing all of the current table data as JSON data in a text file (NOTE: this tool will not work on tables with binary data fields).

  • Replace the current data in a table with the data stored in a snapshot, creating a backup snapshot of the data being replaced in the process.

  • View the data in a snapshot or backup snapshot in a jQuery-enhanced table that allows you to sort and filter the data.

  • Delete older snapshot and backup snapshot files.

All of this functionality is available via a web interface.  Creating a snapshot is as easy as clicking on the "Create" menu option, selecting the tables from your datasource that you want to generate snapshots from an HTML table (enhanced with the jQuery Datatables plugin), and clicking a button.  You reload the data from one or more snapshot files in the same fashion (though of course reloading the data takes longer), and you can view the data stored in any snapshot or backup file in tabular format.  The tool even makes it easy to delete snapshot and backup files you no longer need so you don't have to manually delete them from your file system.

The tool serializes the table data using the functions provided in the JSONUtil project because the "strictMapping" option provided by JSONUtil avoids the data conversion issues that can occur with the native ColdFusion JSON functions (such as strings being converted to numbers and "true"/"false" string values being converted to "yes/no").  However, it uses the native JSON functions for deserializing the data because they seem to perform better when working with large data sets.

This tool was built and tested on ColdFusion 9.0.1. It should work on Adobe ColdFusion 8, and perhaps other CFML engines as well, but it has not been tested in those environments. It relies on the <cfdbinfo> tag to acquire the data types of the table fields it processes, so access to that tag is a requirement.

The current version supports Apache Derby, MySQL, and Oracle:  there is one .cfc file in the tool for each of these databases that tells tableSnapshots how to map the data stored in JSON format to the relevant table fields during the snapshot reload process. So adding support for additional databases simply means writing similar .cfc files and placing them in the main tool directory. Further details about how tableSnapshots works and what configuration options are available are described in the "Technical Details" page within the application itself.

As I said at the beginning, the original purpose of tableSnapshots was to make it easy to rollback data in multiples tables during testing.  But there are a couple of other potential uses as well:

  • You could use it to simply track changes to data over time (since each snapshot file is timestamped).

  • If for some reason you don't have an IDE or database client program that lets you browse table data, you can create snapshots and view the data with the snapshot view option (which gives you the ability to sort and filter the data).

  • You can use it to copy table data from one type of database server to another.  I used it for this purpose myself:  I made a snapshot of the data, created a similar table with a different name on the other datasource, renamed the snapshot file to match that different name, reconfigured and reset tableSnapshots to interact with the new datasource, and "reloaded" the snapshot data into the new table.

  • If your development database isn't being backed up as well or as often as you'd like, you could use the snapshots as your own backup.

  • You can create snapshots as data for use while offline or in mock objects. tableSnapshots comes with a mini-tool in the "tools/jsonasquery" subfolder that can be used to pull snapshot data into a query object.

I do want to be clear that tableSnapshots is meant for use as a development tool.  While there's no risk of data loss when taking a snapshot, reloading snapshots involves deleting data and no software is perfect, so make sure you take additional steps to safeguard any data that you're afraid to lose.

tableSnapshots is available for download from both RIAForge and GitHub, so have at it!

Friday, November 25, 2011

dirtyFields jQuery Plugin Updated To Fix jQuery 1.6 Compatibility Issue

A few days ago, I received an e-mail informing me that there was a bug in my dirtyFields jQuery plugin having to do with the plugin's evaluation of checkbox and radio button state (the plugin lets you track the state of form elements and highlight changes).

At first, I didn't see the problem and didn't understand the need for the code suggestion he provided because the demo file included in the plugin download worked just fine.  But then I realized that the demo was coded to use an older version of jQuery (the plugin is over 2 years old now), and once I updated the demo to use jQuery 1.7 I could see the problem.

I had coded the plugin to use the jQuery attr() function to check the selection state of checkboxes, radio buttons, and the option elements in <select> elements.  Prior to jQuery 1.6, code like myCheckbox.attr("checked") would return a Boolean value based on whether the checkbox was currently checked or not.  But that changed in jQuery 1.6: now (in 1.6 and higher), myCheckbox.attr("checked") only returns the initial value of the "checked" attribute of the element ("checked" rather than "true"), and returns undefined if the checkbox/radio button element does not possess the checked attribute at all.  The same hold true for the "selected" attribute of option elements.  In jQuery 1.6 and higher, the new prop() function is designed to return the current state of the selected attribute (this change to jQuery is in fact described on the API page for prop()).

So I updated the plugin to use the is() function in conjunction with the ":checked" and ":selected" selectors to evaluate checkbox, radio button, and option state, since that method works with both older and newer versions of jQuery.  The new version of the plugin is available via the GitHub link from its dedicated GitHub Pages site.

Monday, October 3, 2011

Two Thoughts On Today's Adobe MAX (2011) Day One Keynote

I'm not going to try and summarize the keynote:  you can either watch the replay at http://www.max.adobe.com/online/ or read a brief synopsis at http://max.adobe.com/news/2011/keynote_day1.html.

My first thought is actually about some news that wasn't part of the keynote, but was announced on the Internet during the keynote:  Adobe's acquisition of PhoneGapPhoneGap is an application framework that allows developers to build mobile apps using web technologies (HTML, CSS, and Javascript) and the PhoneGap API (to access particular device functions like geolocation, the camera, internal storage, etc) and then package and deploy those apps such that they work on most mobile operating systems (Android, IOS, Blackberry, and a few others).

What struck me about that news was that it means Adobe now provides two ways for app developers to develop cross-platform apps.  If a developer is already familiar with Flash and Flex development, they can build mobile apps using Flash Builder and Adobe AIR.  If the developer is more skilled with web design and programming, they can build apps using PhoneGap.  In both cases, the end result is an app that runs natively on your device of choice (IOS, Android, whatever).

First of all, it's yet another example of Adobe's willingness to support both Flash and HTML5 as development platforms going forward.  A number of tech analysts contend that Adobe's development of new tools that play nice with HTML5 is a capitulation of some kind to the way the industry is moving.  I don't see it that way: Adobe may be synonymous with Flash, but it's never been all about Flash with them.  They have always supplied the tools that the digital and web creation markets have demanded, just as any sane company would, regardless of what their internal preferences might be.

The other thing about the PhoneGap acquisition is that it will expand the number of developers using Adobe tools and technologies to create mobile apps for both Android and IOS.  And now suddenly Adobe potentially a big player in the smartphone and tablet space, not by release their own mobile OS or mobile hardware, but by providing the tools to any developer who wants to write on any and all of these platforms.  To be clear, at the moment there are certain kinds of apps or app functions that a developer would want or need to develop in native OS code, but as the Adobe tools provide ways to overcome those limitations (such as the ability to include native code extensions in Flash/AIR apps), the number of reasons for writing an app in native OS code (and hence writing a second version for deployment on another platform) are going to become fewer and fewer.

My second observation regarding the keynote concerns the suite of touch-optimized mobile apps - the Adobe Touch Apps - coming in November to Android devices and later to IOS devices.  While some of the apps are geared more towards visual designers, the Proto wireframing app is something I could use in the design phase of my web and mobile application work, and, well, who wouldn't want a touch-optimized version of Photoshop to use to mess around with their photos?

And that's when the thought occurred to me:  here were some apps that I could use to be productive with a tablet.  I'm an application developer who writes code.  I have access to two tablets and I don't use either of them to write code or generate digital content of any kind.  For me, tablets have always been digital consumption devices (something Amazon seems to recognize with their new Kindle Fire tablet) for viewing video, looking at pictures, or reading web pages or books, and kinda superfluous given I can do all that and much more with a laptop (to be clear, I do love my Nook Color as an e-reader, but I rarely use it to surf the web or anything else).

But the apps Adobe showed off today made me feel like I would in fact want to use them on a tablet, because they were designed specifically with tablets and touch-interaction in mind, not as desktop apps shrunk into a mobile form factor.  That to me is a step in the right direction, and it's apps like those that are going to grow the tablet market by making the tablet experience more compelling.