« October 2010 | Main | December 2010 »

November 2010 Archives

November 23, 2010

A little fairy tale

I'm a Minnesota Vikings fan, and while I'm majorly disappointed in their season, I think it was a good move that they fired their head coach Brad Childress yesterday after Sunday's humiliating loss to the Green Bay. A friend of mine who's a Packers fan (and is quite proud that two head coaches have lost their jobs after losing to the Pack this season) sent me this hilarious little fairy tale today:

I met a fairy today that granted me one wish.
"I want to live forever, " I said.

"Sorry" said the fairy, "I'm not allowed to grant wishes like that!"

"Fine" I said, "I want to die after the Vikings win the Super Bowl!"

"You crafty bastard," said the fairy.

November 19, 2010

Technical Product Manager position in Denver, CO

There's another job opening here are Ping Identity, this one for a Technical Product Manager in our Denver office. This position "...will engage with customers to understand detailed use cases and drive requirements in to the Ping Identity product planning process for PingFederate, Ping Identity's flagship on-premise software. You will have the opportunity to interact with key stakeholders inside and outside of the company with a primary focus on the technical aspects of Ping Identity's Integration product offering. You will be one of the primary technical interfaces for third party technology partners, understanding key integration points and combined value propositions."

See http://www.pingidentity.com/about-us/career-detail.cfm?customel_datapageid_1441=12420 for more details, and feel free to send me your resume if you're interested-- sending your resume in by way of me, an employee, will get more attention than one coming in through the jobs@ email address.

November 18, 2010

Can you resolve this error?

Here at Ping Identity, we've been living with an error that sometimes every now and then when a user tries to download one of our software files. We haven't been able to resolve it ourselves despite months of debugging, searching the web for similar errors, and looking through stack traces via SeeFusion. We're stumped, and it's time to call in the community for reinforcements. Can any of you experts help us resolve this error?

Continue reading "Can you resolve this error?" »

November 17, 2010

Two developer positions open in Wellesley, MA

Sun Life has a couple of new CF developer positions open at Sun Life in Wellesley, MA. Here are the details:

Job Summary
This is an exciting opportunity to join a team currently working on enterprise level web based applications. The Application Developer Analyst will participate as an individual contributor to create new applications and support existing enterprise level applications developed in ColdFusion and Flex.

We are looking for a highly motivated individual whose specific tasks\responsibilities will include:

  • Creating enterprise level applications using the latest releases of Cold Fusion and Flex.
    • Developing using Adobe ColdFusion MX 7 or 8, specifically MVC framework using CFCs and Custom Tags
    • Developing using Adobe Flex 2, 3, or 4, with Action script 3.0
  • Coding, writing technical documentation, debugging code and unit testing their work.
  • Understanding and demonstrating how written requirements translate into a technical solution.
  • Responsible for major system installations.
  • Maintaining and supporting existing enterprise software applications developed in ColdFusion and Flex.

See https://sunlifefinancial.taleo.net/careersection/10360/jobdetail.ftl?lang=en&job=260089 for more information.

November 15, 2010

Spider.cfc for ColdFusion

After Ray Camden posted his first-draft spider code the other week, I remembered that I have spider code that I wrote last year but haven't published yet, so here goes. My component lets you set a maximum number of links to crawl, and lets you create filters for inclusion and exclusion of specific file paths or file names. It also:

  • checks the response code of any URL before calling the full content;
  • avoids calling the same URL more than once;
  • and, returns a query containing the url, title, and contents of each page called, perfect for creating a Verity (or Solr) search index.
If anyone has comments, let me know what you think.

<cfcomponent output="false">
	<!--- spider.cfc written by Tom Mollerus (tmollerus@pingidentity.com) --->
	
	<!--- Initialize parameters --->
	<cfset VARIABLES.maxLinks = "0" />
	<cfset VARIABLES.extensions = "" />
	<cfset VARIABLES.excludeFilters = "" />
	<cflock name="qDataLock" timeout="2">
		<cfset VARIABLES.qData = QueryNew('url,title,body,itemDate', 'varchar,varchar,varchar,date') />
	</cflock>
	<cflock name="qLinkLock" timeout="2">
		<cfset VARIABLES.qLinks = QueryNew('url', 'varchar') />
	</cflock>
	
	<cffunction name="crawl">
		<cfargument name="site" default="" />
		<cfargument name="extensions" default="" />
		<cfargument name="excludeFilters" default="" />
		<cfargument name="maxLinks" default="0" />
		<cfif IsValid('URL', ARGUMENTS.site) and GetStatus(ARGUMENTS.site)>
			<!--- Check the links --->
			<cfset VARIABLES.maxLinks = Val(ARGUMENTS.maxLinks) />
			<cfset VARIABLES.excludeFilters = ARGUMENTS.excludeFilters />
			<cfset VARIABLES.extensions = ARGUMENTS.extensions />
			<cfset checkLinks(ARGUMENTS.site, ARGUMENTS.site, ARGUMENTS.extensions) />
		</cfif>
		
		<cfreturn VARIABLES.qData />
	</cffunction>
	
	<cffunction name="getStatus">
		<cfargument name="link" required="true" />
		<cfset var result = 0 />
		
		<cftry>
			<cfhttp method="head" url="#ARGUMENTS.link#" redirect="true" timeout="5"></cfhttp>
			<cfset result = Val(cfhttp.statusCode) />
			<cfcatch></cfcatch>
		</cftry>
		
		<cfreturn result />
	</cffunction>
	
	<cffunction name="shouldFollow">
		<cfargument name="link" required="true" />
		<cfargument name="domain" required="true" />
		<cfset var result = true />
		
		<cflock name="qLinkLock" timeout="2">
			<cfquery name="qHasBeenChecked" dbtype="query">
				SELECT url
				FROM VARIABLES.qLinks
				WHERE url = '#ARGUMENTS.link#'
			</cfquery>
		</cflock>
		<cfif qHasBeenChecked.recordCount>
			<cfset result = false />
		<cfelseif ARGUMENTS.link contains 'javascript:'>
			<cfset result = false />
		<cfelseif Val(VARIABLES.maxLinks) and VARIABLES.qLinks.recordCount gte Val(VARIABLES.maxLinks)>
			<cfset result = false />
		<cfelseif Left(link, Len(ARGUMENTS.domain)) neq ARGUMENTS.domain>
			<cfset result = false />
		</cfif>
		
		<cfreturn result />
	</cffunction>
	
	<cffunction name="shouldIndex">
		<cfargument name="link" required="true" />
		<cfset var result = true />
		
		<cfif ListLen(VARIABLES.extensions) and not ListFindNoCase(VARIABLES.extensions, ListLast(ListFirst(ARGUMENTS.link, '?'), '.'))>
			<cfset result = false />
		<cfelseif ListLen(VARIABLES.excludeFilters)>
			<cfloop index="filter" list="#VARIABLES.excludeFilters#" delimiters="|">
				<cfset literalFilter = Replace(filter, '*', '', 'ALL')>
				<cfif Left(filter, 1) eq '*' and Right(filter, 1) eq '*'>
					<cfif link contains literalFilter>
						<cfset result = false />
					</cfif>
				<cfelseif Right(filter, 1) eq '*'>
					<cfif Left(link, Len(literalFilter)) eq literalFilter>
						<cfset result = false />
					</cfif>
				<cfelseif Left(filter, 1) eq '*'>
					<cfif Right(link, Len(literalFilter)) eq literalFilter>
						<cfset result = false />
					</cfif>
				<cfelse>
					<cfif link eq filter>
						<cfset result = false />
					</cfif>
				</cfif>
			</cfloop>
		</cfif>
		
		<cfreturn result />
	</cffunction>
	
	<cffunction name="checkLinks">
		<cfargument name="page" required="true" />
		<cfargument name="domain" required="true" />
		<cfset var link = '' />
		
		<!--- Get the page --->
		<cfhttp method="get" url="#ARGUMENTS.page#" redirect="true" resolveurl="true" timeout="10"></cfhttp>
		<cflock name="qLinkLock" timeout="2">
			<cfset QueryAddRow(VARIABLES.qLinks) />
			<cfset QuerySetCell(VARIABLES.qLinks, 'url', ARGUMENTS.page) /><!--- Enter the link in the result query --->
		</cflock>
		<cfif Val(CFHTTP.statusCode) eq 200>
			<cfif shouldIndex(ARGUMENTS.page)>
				<cflock name="qDataLock" timeout="2">
					<cfset QueryAddRow(VARIABLES.qData) />
					<cfset QuerySetCell(VARIABLES.qData, 'url', getRelativePath(ARGUMENTS.page)) /><!--- Enter the link in the result query --->
					<cfset QuerySetCell(VARIABLES.qData, 'title', getPageTitle(CFHTTP.fileContent)) /><!--- Enter the status in the result query --->
					<cfset QuerySetCell(VARIABLES.qData, 'body', getBrowsableContent(CFHTTP.fileContent)) /><!--- Enter the status in the result query --->
					<cfset QuerySetCell(VARIABLES.qData, 'itemDate', '') /><!--- Enter the status in the result query --->
				</cflock>
			</cfif>
			
			<!--- Parse out the links in the page --->
			<cfset aLinks = ReMatchNoCase('((((https?:|ftp:)\/\/)|(www\.|ftp\.))[-[:alnum:]\?$%,\.\/\|&##!@:=\+~_]+[A-Za-z0-9\/])', StripComments(cfhttp.fileContent)) />
			
			<!--- For each of the links --->
			<cfloop index="link" array="#aLinks#">
				<!--- Strip the link of any bookmark ('#') --->
				<cfset link = Replace(ListFirst(link, '##'), ':80', '', 'ONE') />
				<!--- If the link hasn't been checked already --->
				<cfif shouldFollow(link, ARGUMENTS.domain)>
					<cfset linkStatus = GetStatus(link) />
					<!--- If the link is up and matches the domain --->
					<cfif linkStatus eq 200>
						<!--- Link check its contents as well --->
						<cfset checkLinks(link, ARGUMENTS.domain)>
					</cfif>
				</cfif>
			</cfloop>
		</cfif>
		
		<cfreturn />
	</cffunction>
	
	<cffunction name="getBrowsableContent">
		<cfargument name="string" required="true" />
		
		<cfset ARGUMENTS.string = StripComments(ARGUMENTS.string) />
		<cfset ARGUMENTS.string = ReReplaceNoCase(ARGUMENTS.string, '<script.*?>.*?</script>', '', 'ALL') />
		<cfset ARGUMENTS.string = ReReplaceNoCase(ARGUMENTS.string, '<style.*?>.*?</style>', '', 'ALL') />
		<cfset ARGUMENTS.string = ReReplace(ARGUMENTS.string, '<[^>]*>', '', 'ALL') />
		
		<cfreturn ARGUMENTS.string />
	</cffunction>
	
	<cffunction name="stripComments">
		<cfargument name="string" required="true" />
		
		<cfset ARGUMENTS.string = ReReplace(ARGUMENTS.string, '<--[^(-->)]*-->', '', 'ALL') />
		
		<cfreturn ARGUMENTS.string />
	</cffunction>
	
	<cffunction name="getPageTitle">
		<cfargument name="string" required="true" />
		
		<cfreturn ReReplace(ARGUMENTS.string, ".*<title>([^<>]*)</title>.*", "\1") />
	</cffunction>
	
	<cffunction name="getRelativePath">
		<cfargument name="path" required="true" />
		
		<cfset ARGUMENTS.path = ReplaceNoCase(ARGUMENTS.path, 'http://', '', 'ONE') />
		<cfset ARGUMENTS.path = ReplaceNoCase(ARGUMENTS.path, ListFirst(ARGUMENTS.path, '/'), '', 'ONE') />
		
		<cfreturn ARGUMENTS.path />
	</cffunction>
</cfcomponent>

Not just images: using generated content in CSS

I often use graphics in my CSS stylesheets: for background images, for link descriptors, and every once in a while, for list icons. And it was while reading A List Apart's venerable 2002 article "Taming Lists" that I remembered that graphics aren't the only assets you can specify in stylesheets-- you can also use plain text, in a format called generated content.

The term generated content refers to content that your users see in their browser but isn't present in the underlying HTML code. Instead, it's specified in the stylesheet and generated by the browser. This is especially useful with the :before and :after psuedoclasses. For instance, if you wanted users to see whether any link on your site led to an external site , you could specify the following:

<style>
a[target=_new]:after, a[href^=http]:after { content: " (External link)"; }
</style>

You'll end up with Ping Identity, Inc.. Look at the source of this post, and you'll see that the link is just "<a href="http://www.pingidentity.com" target="_new">Ping Identity, Inc.</a>

November 12, 2010

RIA Unleashed: Boston is a great time

If you're a CF/Flash/Flex developer in New England and didn't make it to RIA Unleashed's party last night at the MIT Museum, you missed a good time! Loads of people came in from New Hampshire, New York, or even further. I grabbed dinner and beers afterward with Josh Cyr, Todd Sharp, and Ray Camden.

I couldn't make the conference's workshops yesterday, but today I am definitely looking forward to making the web developer track.

November 9, 2010

New HTML5 pseudoclasses for form inputs

I just learned about some great new pseudoclasses in HTML5 for use on input tags: :valid and :invalid (These are among the many other new features in CSS3). Used in conjunction with the new validation capabilities for input fields, you can write styles which will change the appearance of your inputs in real time depending on where the cursor is in the form and what data the user has entered.

The new validation capabilities of HTML5 have to do with the new input types: "email", "url", "number", or "date", and the new "required" attribute. These new types are supported by most browsers except for Firefox, and just appear as regular text inputs in non-compliant browsers (and the "required" attribute will just be ignored). For example, in HTML5 you can now create a field like this:

Email address: <input type="email" name="your_email" required="true" />

Using these new features allows the use of the new pseudoclasses:

  1. When your input field has the "required" attribute set, it will match the pseudoclass :valid if it has a value entered (unless the value doesn't match an additional validation rule)
  2. If your input field has the "required" attribute set and it does not have a value entered, it will match the :invalid pseudoclass.
  3. When your field is of type "email", "url", "number", or "date", and the value entered is not of the corresponding type, the will input will match the pseudoclass :invalid.

So our CSS can look like this (only works in Chrome, Safari, and Opera):

<style>
input:valid { border: 1px solid green; }
input:invalid { border: 1px solid red; }
</style>

<form>
Your name: <input type="text" name="your_name" required="true" /><br />
Email address: <input type="email" name="your_email" required="true" />
</form>

Give it a try:

Your name: Required
Email address: Valid email required

Better yet, you can use the "adjacent" selector to style messages next to your inputs (only works in Chrome, Safari, and Opera):

<style>
input:valid + span { color: green; }
input:invalid + span { color: red; }
</style>

<form>
Your name: <input type="text" name="your_name" required="true" /><br />
Email address: <input type="email" name="your_email" required="true" />
</form>

Give it a try:

Your name: Required
Email address: Valid email required

November 2, 2010

Best. Cardboard. Star Wars. Fan movie. Ever.