I'm Speechless!

Goog Feed, CSS, General, ColdFusion, CFUnited 2008    Comments (10)


The title says it all, so I'll write about it instead. I found out yesterday from Liz at Teratech that I was picked by the Teratech team to be one of the speakers at CFUnited 2008 in Washington D.C.

I am honored, nervous, and humbled to be chosen to speak at this awesome event. I feel that there are many people out there who are far better than me, but I look forward with a nervous excitement to this opportunity!

I will be speaking on The Power of CSS, including tabled layouts vs. css layouts and why CSS is a far better solution for web development. I have blogged in the past about the speed differences of the two which you can see here. If you are going to CFUnited, I look forward to seeing you there! If not, you can register here, don't forget today is the last day for the special early bird rate!

See you in D.C. :)

Web Development - How do you approach it?

Goog Feed, General, ColdFusion, SQL    Comments (3)


I work for a school district, and I am the first developer they have every had. So I've been developing web apps for them for about six months now using ColdFusion with Model-Glue and SQL, all authentication is done through Active Directory.

Lately I started thinking about the future and how to best do development. When I was hired they had a list of projects they needed done, and they needed to be done yesterday. So I began coding, coding, coding. No design docs, not project scope docs, no database diagrams, nothing of the sort.

My first thought was to slow down and start a process for each project thrown my way. It may be more tedious, time-consuming and boring, but I really think long-term, a well laid out play with an approval process up front lends itself to successful projects.

I am a big fan of the Interface-Driven approach to web development, but I wanted to get some feedback from the community...

What methods/steps do you employ when taking on a project?
Do you build out pieces and release them or wait till the project is complete?
What type of documentation is used and why?
What have you found most beneficial to you in your experience with this?

Thanks in advance for your input!

ColdFusion: Query to Excel Function

Goog Feed, General, ColdFusion    Comments (0)


I have a project at work where I needed to take a result set from a user's search and give them the option to download those results to excel, but the problem didn't stop there. On the same page they are allowed also to call a totally different function and get back a result set a bit different than the first. So I had two options. I could send them each to a CF page where they could take results and output them to Excel, or I could write a function that will allow any method to be called, any query result set to be put into Excel regardless of the result set. I chose the latter, because I have another project it could also be useful for.

Let me first give a quick overview of how the app is set up. I am using Model-Glue as my framework, with a SQL backend, CF as my development language. I admit that this is somewhat limited in the sense that you must be using an object oriented type methodology to make this work.

So, the first thing we do is add a bit of JavaScript to our page, which will allow for the submission of a hidden form which will call our function.

<form action="index.cfm?event=queryToExcel" method="post" name="myMethodCall">
   <input type="hidden" name="theCFC" value="myCFCNameHere" />
   <input type="hidden" name="theMethod" value="myCFCMethodHere" />
<input type="hidden" name="arg|anArgumentHere" value="#viewState.getValue("anArgumentHere")#" />
<input type="hidden" name="showFields" value="firstname|First Name,lastname|Last Name,dob|Date of Birth,statusName|Status" />
</form>

If you notice there are a few things that are very important about this form. We have four items that are vital to the entire process. These four items tell the Excel Function what data to go get for the Excel file.

1. The name/path to the CFC you need to get your data from.
2. The method of the CFC to call which returns a query result set of the data.
3. Any arguments that you know the method takes as a hidden field with an "arg|" before it so our function knows that it is an argument. (If you have more than one argument, then you'll have more of these fields.)
4. The fields that you want to show from your query result set, often you may bring back more fields then you want in your result set, so this allows you to give the fields you want, the order in which you want them, and the alias of the fieldname for your spreadsheet with the original db fieldname pipe delimited with the alias fieldname. (ie: fieldname|My Field Name)

Next we'll insert our JavaScript link which submits this form to our Excel method.

<a href="javascript:document.myMethodCall.submit();">Download Current Results to Excel</a>

Our form when submitted goes to an event called "queryToExcel", this event calls our method "getExcelData" (below) in our controller passing it the form values.

<cffunction name="getExcelData" access="public" returnType="void" output="false">
   <cfargument name="event" type="ModelGlue.Core.Event" required="true">
      
<cfset var excelQuery = "" />
<cfset var pathString = "myApplication.model." & arguments.event.getValue('theCFC') />
<cfinvoke component="#pathString#" method="#arguments.event.getValue("theMethod")#" returnvariable="excelQuery">
   <cfloop list="#arguments.event.getValue("fieldNames")#" index="i">
   <cfif listFirst(i,"|") IS "arg">
   <cfinvokeargument name="#listLast(i,"|")#" value="#arguments.event.getValue(i)#">
</cfif>
</cfloop>
</cfinvoke>
   <cfset arguments.event.setValue("excelQuery", excelQuery) />
</cffunction>

The first thing our controller does is set a couple of variables, one being the path to our CFC. We then use cfInvoke to make our "dynamic" method call, passing in any arguments which we loop through the form variables to get anything with our "arg|" prefix, then we get the data that we want back in a query result set, that result is then set in the viewState and sent on to our "rendering page" which you can see below.

<cfset excelQuery = viewState.getValue("excelQuery") />
<cfset showFields = viewState.getValue("showFields") />

<cfset variables.columnList = "" />

<cfif len(showFields) AND listLen(showFields) GT 0>
   <cfloop list="#showFields#" index="i">
   <cfset variables.columnList = listAppend(variables.columnList,listLast(i,"|")) />
</cfloop>
<cfelse>
   <cfset variables.columnList = excelQuery.columnList />   
</cfif>

<!---If the page was submitted there should be a query, make sure there is one, and make sure there are records in it. Then spit out the file.--->
<cfif isQuery(excelQuery) AND excelQuery.recordCount GT 0>

<cfsetting enableCFoutputOnly="Yes">

<cfheader name="Content-Disposition" value="attachment; filename=myFileName-#dateFormat(now(),'m/d/yyyy')#.xls">
<cfcontent type="application/vnd.ms-excel" reset="yes">

<!--- Output data, each row on it's own line.--->
<cfoutput>
<table border="1" cellpadding="1" cellspacing="2">
<tr>
   <cfloop list="#variables.columnList#" index="i">
    <th>#i#</th>
      </cfloop>
</tr>
</cfoutput>
   <cfoutput>
   <cfloop from="1" to="#excelQuery.recordCount#" index="num">
<tr>
<cfloop list="#variables.columnList#" index="i">
<td><cfif isDate(trim(excelQuery[listFirst(i,"|")][num]))>#dateFormat(trim(excelQuery[listFirst(i,"|")][num]),"m/d/yyyy")#<cfelse>#trim(excelQuery[listFirst(i,"|")][num])#</cfif></td>
</cfloop>
</tr>
</cfloop>
</cfoutput>
<cfoutput>
</table>
</cfoutput>

<cfelse>
   <p>Sorry, try again.</p>
</cfif>

In our processing file which I call dspExportData.cfm, we are setting our query to a variable as well as the fields in the previous form submission that we want to show. If the "showFields" form value was blank we set our columnList to output to the query.columnList, otherwise we grab the passed in column names.

Next, we check to make sure we're dealing with a query, we then set our content output to Excel. To output the data, we first output our column names then move on to our query. We do a bit of double looping to make this work, first looping from 1 to our recordCount, inside of that loop we loop over our column list and refer to our query result set like an array outputting our results and coming out with what we wanted all along.

I hope this was useful for you, this is just the way I thought to do it, there are probably better ways to do this but never-the-less this is what worked for me! If you think of better ways to do this, let me know I would love the feedback!

Recursion and Active Directory

Goog Feed, General, ColdFusion    Comments (5)


So, I have this project on my plate of building a "portal" type application where you can access all internal applications from one spot, with one login. We are using Active Directory to authenticate the user, but the permissions on each application is set on the application side of the house.

I quickly ran into a problem.

When I would use cfAuthenticate to login it would give me back all of the groups that I was a member of, I would then use cfLDAP to grab other important data needed for the user. The only problem was that when I would get my list of groups back from cfAuthenticate it wouldn't give me nested groups.

For instance if I was a member of "Group One" in AD as an individual it would see me as having "Group One" as a group I could access, however if I was a member of "Technology" and we put the entire technology group into "Group One" it wouldn't see me as having access to "Group One".

This was my first time messing with Active Directory so I didn't know anything about this, but here is what I came up with.

I could take that group that I originally got back from my cfAuthenticate call and pass it in to a function that would loop through them and see what groups they were a member of. This would allow me to loop through the "technology" group and see that it was a member of "Group One" therefore I should be able to access anything "Group One" had access to.

So here it is...

First, I authenticate the user and then go grab important info about them for later use.

<!---Authenticating to the domain.--->
<cfntauthenticate username="#trim(arguments.username)#" password="#trim(arguments.password)#" domain="#theDomain#" result="loginResult" listgroups="yes">

<!---Querying Active Directory for employee specific data for later use. (correct name, email, employeeID, locationID)--->
<cfldap server="my.ldap.server" port="123" action="query" name="UserInfo" start="ou=Employees,ou=Users,dc=my,dc=ldap,dc=server" attributes = "cn,mail,employeeID,houseIdentifier" filter="(sAMAccountName=#trim(arguments.username)#)" username="AD_UserName" password="AD_Password" maxrows="1">

If they are authenticated, I take the groups passed back and pass them into a function which I will later call recursively.

<cfset recursiveGroupCall = getAllGroups(groupList = loginResult["groups"]) />

The function then does its thing which is detailed below and builds a string with all of my groups.

<!---Initializing public access variables.--->
<cfset variables.groupString = "" />

<cffunction name="getAllGroups" access="public" returntype="string">
<cfargument name="groupList" type="string" required="yes" default="">

<cfset var newGroups = "" />
<cfset var removeDups = "" />

<!---Loop over the passed in list and get the groups that each item is a member of.--->
<cfloop list="#arguments.groupList#" index="i">
<cfldap server="myLDAPServer" port="123" action="query" name="user" start="DC=my,DC=ldap,DC=server" attributes = "memberof,cn" scope="subtree" separator="|" filter="(cn=#i#)" username="ADUsername" password="ADPassword">
<!---Loop over the query returned for the item.--->
<cfloop query="user">
<cfoutput>
<!---Loop over the members of the query item and grab the groups out.--->
<cfloop list="#user.memberof#" index="i" delimiters="|">
<cfif NOT len(newGroups)>
<cfset newGroups = listLast(listFirst(i),"=") />
<cfelse>
<cfset newGroups = newGroups & ',' & listLast(listFirst(i),"=") />
</cfif>
</cfloop>
</cfoutput>
</cfloop>
</cfloop>

<!---Set our overall group string to whatever is present.--->
<cfif NOT len(variables.groupString)>
<cfset variables.groupString = arguments.groupList & ',' & newGroups>
<cfelse>
<cfset variables.groupString = variables.groupString & ',' & newGroups>
</cfif>

<cfif len(newGroups)>
<!---If there are new groups then call this function again, until there is no more new groups.--->
<cfset recursivenav = getAllGroups(groupList=newGroups)>
<cfelse>
<!---If there is nothing in the new groups variable, return the entire group string.--->

<!---Removing dups from the list, and cleaning it up.--->
<cfset removeDups = StructNew()>
<cfloop index="i" list="#variables.groupString#">
<cfset removeDups[i] = "">
</cfloop>
<!--- Convert the set back to a list --->
<cfset variables.groupString = StructKeyList(removeDups)>

<!---Now we return it.--->
<cfreturn variables.groupString>
</cfif>
</cffunction>

Feedback is always appreciated, if I could have done this differently, or more efficiently let me know.

ColdFusion: Stored Procedures For Beginners

General, ColdFusion, SQL    Comments (2)


So, you are a beginner in ColdFusion and you want to know the quick and dirty way to build a stored procedure and call it in ColdFusion.

Step One: Open SQL Server Enterprise Manager and browse to your database, you will find a section called "Stored Procedures", right click on it and select "New Stored Procedure".

Step Two: A window "Stored Procedure Properties" window will open where you will drop in your t-SQL code for running a query. Save it and you are good to go. Below you'll find the window and example code.

CREATE PROCEDURE dbo.getStuff
@firstname varchar(50)
AS

SELECT dbo.[User].firstName AS Firstname, dbo.[User].lastName AS Lastname, dbo.[User].email AS EmailAddress, dbo.Role.name AS Role,dbo.Organization.Name AS OrgName, dbo.Permission.name AS PermissionName
FROM dbo.RolePermission INNER LOOP JOIN
   dbo.UserOrganization INNER LOOP JOIN
dbo.[User] ON dbo.UserOrganization.UserID = dbo.[User].userID INNER LOOP JOIN
dbo.UserOrganizationRole ON dbo.UserOrganization.UserOrganizationID = dbo.UserOrganizationRole.userorganizationid INNER LOOP JOIN
dbo.Role ON dbo.UserOrganizationRole.roleid = dbo.Role.roleId ON dbo.RolePermission.roleid = dbo.Role.roleId INNER LOOP JOIN
dbo.Organization ON dbo.UserOrganization.OrganizationID = dbo.Organization.OrganizationID INNER JOIN
dbo.Permission ON dbo.RolePermission.permissionid = dbo.Permission.permissionID
WHERE dbo.[User].firstName = @firstname
GO

Step Three: Open up a ColdFusion document and drop the following code in to call the stored procedure. You'll need the datasource to the database where the strored procedure lives, the stored procedure name, and a name you will call your result, also pass in any parameters that the stored procedure needs.

<cfset myFirstname = "Joe" />

<cfstoredproc datasource="aDatasource" procedure="getStuff">
   <cfprocresult name="myQueryResultSet" />
   <cfprocparam type = "IN" CFSQLType="cf_sql_varchar" value="#myFirstname#" dbvarname="@firstname">
</cfstoredproc>

Dump your stored procedure result.

<cfdump var="#myQueryResultSet#">

And just like that, you've got results. A stored procedure is really quite simple and very useful.

Why use stored procedures?

Many times, depending on what you are doing a stored procedure is much quicker. Everytime you write a query in ColdFusion, the SQL server takes it and says... "What would be the most efficient way to run this query?" It then runs it. Whereas with a stored procedure, the "Execution Plan" of how to run the query most efficiently is stored in the system so the next time you call the stored procedure, it doesn't have to evaluate and come up with an "Execution Plan."

The main reason for using them is speed. You'll have to evaluate for yourself based on your situation at hand, what is quicker for the task at hand. And no, I don't recommend using stored procedures for every little query, but for some of the bigger more complex ones... WHY NOT???

NOTE:

Microsoft SQL and Sybase among others use Transact SQL as the language to write a stored procedure. It is extremely robust and evolved and it may behoove you to learn some of the functionality available to you to perform more advanced operations (branching logic, looping over results, etc...).

An excellent reference book on this subject can be found here.

More Entries