Search

Wednesday, October 24, 2007

I Knew I'm not a Nerd - My NerdTests Results

Whenever people would accuse me of being a nerd I always protested. I knew there was a line between nerds and geeks and here is my test scores to prove it: NerdTests.com says I'm a Cool Technology Geek. What are you? Click here!

Thursday, October 18, 2007

SQL Server Script to Restore a Database from File

I frequently have to backup a database from the production machine and restore it in my testing environment. You can do this with SQL Server Enterprise Manager (or other similar tools) but it can get to be a pain if you have to it over and over again. So here is a script to the second part - restore a database from a file to sql server. For the first part, please see my related post called SQL Server Script to Backup a Database to File. To use this script you need to:
  1. Set the database name in the @databaseName variable to your database
  2. Set the @restoreDirectory variable to the absolute directory path where the database backup is found
  3. Run the script (with something like Query Analyzer)
The script will restore a backup of the specified database from the restore directory specified in the @restoreDirectory variable. It will look for a backup filename that consists of the database name, today's date and the '.bak' extension (example: myDatabase-10.18.2007.bak).

Download the script from http://tech-cats.net/blog/downloads/sql/SQLServerRestoreDatabaseFromFile.txt
-----------------------------------------------------------------
-- Restore database from file
-----------------------------------------------------------------
use master
go

declare @backupFileName varchar(100), @restoreDirectory varchar(100),
 @databaseDataFilename varchar(100), @databaseLogFilename varchar(100),
 @databaseDataFile varchar(100), @databaseLogFile varchar(100),
 @databaseName varchar(100), @execSql nvarchar(1000)

-- Set the name of the database to restore
set @databaseName = 'myDatabase'
-- Set the path to the directory containing the database backup
set @restoreDirectory = 'aboslute_path_to_restore_directory' -- such as 'c:\temp\'

-- Create the backup file name based on the restore directory, the database name and today's date
set @backupFileName = @restoreDirectory + @databaseName + '-' + replace(convert(varchar, getdate(), 110), '-', '.') + '.bak'

-- Get the data file and its path
select @databaseDataFile = rtrim([Name]),
 @databaseDataFilename = rtrim([Filename])
from master.dbo.sysaltfiles as files
 inner join
 master.dbo.sysfilegroups as groups
 on
 files.groupID = groups.groupID
where DBID = (
  select dbid
  from master.dbo.sysdatabases
  where [Name] = @databaseName
 )

-- Get the log file and its path
select @databaseLogFile = rtrim([Name]),
 @databaseLogFilename = rtrim([Filename])
from master.dbo.sysaltfiles as files
where DBID = (
  select dbid
  from master.dbo.sysdatabases
  where [Name] = @databaseName
 )
 and
 groupID = 0

print 'Killing active connections to the "' + @databaseName + '" database'

-- Create the sql to kill the active database connections
set @execSql = ''
select @execSql = @execSql + 'kill ' + convert(char(10), spid) + ' '
from master.dbo.sysprocesses
where db_name(dbid) = @databaseName
 and
 DBID <> 0
 and
 spid <> @@spid
exec (@execSql)

print 'Restoring "' + @databaseName + '" database from "' + @backupFileName + '" with '
print '  data file "' + @databaseDataFile + '" located at "' + @databaseDataFilename + '"'
print '  log file "' + @databaseLogFile + '" located at "' + @databaseLogFilename + '"'

set @execSql = '
restore database [' + @databaseName + ']
from disk = ''' + @backupFileName + '''
with
  file = 1,
  move ''' + @databaseDataFile + ''' to ' + '''' + @databaseDataFilename + ''',
  move ''' + @databaseLogFile + ''' to ' + '''' + @databaseLogFilename + ''',
  norewind,
  nounload,
  replace'

exec sp_executesql @execSql

exec('use ' + @databaseName)
go

-- If needed, restore the database user associated with the database
/*
exec sp_revokedbaccess 'myDBUser'
go

exec sp_grantdbaccess 'myDBUser', 'myDBUser'
go

exec sp_addrolemember 'db_owner', 'myDBUser'
go

use master
go
*/

Wednesday, October 17, 2007

SQL Server Script to Backup a Database to File

I frequently have to backup a database from the production machine and restore it in my testing environment. You can do this with SQL Server Enterprise Manager (or other similar tools) but it can get to be a pain if you have to it over and over again. So here is a script to do the first part - backup a database from a sql server to a file. For the second part, please see my related post called SQL Server Script to Restore a Database from File. To use this script you need to:
  1. Set the database name in the @databaseName variable to your database
  2. Set the absolute path for the backup directory in the @backupDirectory variable to the directory where the backup will be created
  3. Run the script (with something like Query Analyzer)
The script will create a backup of the specified database and put in the directory specified in the @backupDirectory variable. The actual backup name will be based on the database name, today's date and the '.bak' extension (example: myDatabase-10.17.2007.bak).

Download the script from http://tech-cats.net/blog/downloads/sql/SQLServerBackupDatabaseToFile.txt
-----------------------------------------------------------------
-- Backup database to file
-----------------------------------------------------------------
declare @backupFileName varchar(100), @backupDirectory varchar(100),
 @databaseDataFilename varchar(100), @databaseLogFilename varchar(100),
 @databaseDataFile varchar(100), @databaseLogFile varchar(100),
 @databaseName varchar(100), @execSql varchar(1000)

-- Set the name of the database to backup
set @databaseName = 'myDatabase'
-- Set the path fo the backup directory on the sql server pc
set @backupDirectory = 'aboslute_path_to_backup_directory' -- such as 'c:\temp\'

-- Create the backup file name based on the backup directory, the database name and today's date
set @backupFileName = @backupDirectory + @databaseName + '-' + replace(convert(varchar, getdate(), 110), '-', '.') + '.bak'

-- Get the data file and its path
select @databaseDataFile = rtrim([Name]),
 @databaseDataFilename = rtrim([Filename])
from master.dbo.sysaltfiles as files
 inner join
 master.dbo.sysfilegroups as groups
 on
 files.groupID = groups.groupID
where DBID = (
  select dbid
  from master.dbo.sysdatabases
  where [Name] = @databaseName
 )

-- Get the log file and its path
select @databaseLogFile = rtrim([Name]),
 @databaseLogFilename = rtrim([Filename])
from master.dbo.sysaltfiles as files
where DBID = (
  select dbid
  from master.dbo.sysdatabases
  where [Name] = @databaseName
 )
 and
 groupID = 0

print 'Backing up "' + @databaseName + '" database to "' + @backupFileName + '" with '
print '  data file "' + @databaseDataFile + '" located at "' + @databaseDataFilename + '"'
print '  log file "' + @databaseLogFile + '" located at "' + @databaseLogFilename + '"'

set @execSql = '
backup database [' + @databaseName + ']
to disk = ''' + @backupFileName + '''
with
  noformat,
  noinit,
  name = ''' + @databaseName + ' backup'',
  norewind,
  nounload,
  skip'

exec(@execSql)

Tuesday, October 16, 2007

Unzip with UTF Support in ColdFusion - Function Updates for v0.2

Due to some shortcommings, here is an update of the "unzipWithUTFSupport" function. The change log is below. You can get the update version at http://tech-cats.net/blog/downloads/coldFusion/unzipWithUTFSupportUDF.txt
  • Support for absolute paths relative to the root of the web site
    Example: destination = "/"
  • Back slashes get converted to forward slashes
    Example: destination = "/test\blah" = "/test/blah"
  • Better support for relative paths:
    - relative to the "destination" if one is specified
    - relative to the name of the zip archive otherwise (which is used as the extract directory name)
  • Support for parent paths (relative to the executing script)
    Example: destination = "../test"
  • Support for absolute paths
    Example: destination = "d:\webroot\test"
  • All possible ways to define a path also apply to the "destination" key in the locationsByFileType structure
    Example: locationsByFileType.jpg.destination = "images\jpg"
  • No need for file type sub structure if you only care for the path where the files will be extracted
    Example: locationsByFileType.xml = "images\jpg"
    Instead of: locationsByFileType.jpg.destination = "images\jpg"
  • Corrected an omission in the build script that prevented all files from being extracted because of lower case or upper case file extensions
  • Renamed "overwriteDestination" to "deleteDestination" and set it to "false" by default

DriverMax - Reinstall your Windows Drivers within Minutes

Why didn't anybody think of this before?! Reinstalling your hardware drivers can always be a pain when reinstalling Windows. This program aims to backup your installed drivers so when you have to reinstall Windows, you can easily restore your drivers from the backup. DriverMax for Windows Vista and Windows XP - easily reinstall all your Windows drivers within a few minutes

Monday, October 15, 2007

Unzip with UTF Support in ColdFusion Curtosy of Ant via cfant

Update: Function was updated to v0.2 (see Unzip with UTF Support in ColdFusion - Function Updates for v0.2)

A fellow by the name of Ricardo Parente contacted me today about unzipping a zip file that contains files with special characters (to be more technical, the file names were using UTF8 encoding). Some filenames contained accents and/or other characters that would cause issues. He was trying to extract the zip file programmatically using the zip component from Alagad but that was throwing an exception. After looking around for a bit, turns out there is allready an existing user defined function on cflib called unzipFile. However, it seems that suffered from the same problem as the Alagad component when trying to extract files with UTF8 encoding. So after some searching, I found that the UTF encoding issue is an issue with Java and has not be fixed for 6 years!! Bah! So anyway, it turns out Ant, does support that with no problem and forutnatelly ColdFusion MX 7 comes with the undocumented function "cfant". So here is the custom function to unzip those pesky zip files contained UTF encoded file names. Createvely, I've named it "unzipWithUTFSupport". You can call it as simple as:
<cfset unzipResults = unzipWithUTFSupport("myUTFEncodedZipFile.zip") />
or like so to specify the destination directory:
<cfset unzipResults = unzipWithUTFSupport(
	zipFile = "myUTFEncodedZipFile.zip",
	destination = "yourRelativeDestination"
) />
This will return a structure with the following keys:
  • antMessage = string with the message Ant returned
  • argumentErrors = array of argument validation errors
  • fileList = query of files unzipped
  • success = boolean indicating the success or the failure of the unzip process
Now, for the more advanced functionality. You can have the function unzip the different file types to different sub directores. For example ".jpg" files can go in a "images" sub directory, while ".xml" files can go in a "configuration" sub directory. To take advantage of this, you need to pass the function a structure with the argument name "extractLocationsByFileType". That structure will define how what file extentions wil be extracted where. Here is a sample configuration that will extract only ".jpg" files to a "images" sub diretory:
<cfset locationsByFileType = structnew() />
<cfset locationsByFileType.jpg = structnew() />
<cfset locationsByFileType.jpg.destination = "images" />
Once you configure the "locationsByFileType" structure, you just pass it to the function like so:
<cfset results = unzipWithUTFSupport(
	zipFile = "myUTFEncodedZipFile.zip",
	extractLocationsByFileType = locationsByFileType
) />
The important think to keep in mind is that, if you use this functionality, you will have to define the configurtion for each file type. This means that if you use the configuration above, only ".jpg" files will be extracted from the zip archive. This will return a structure with the following keys:
  • antMessage = string with the message Ant returned
  • argumentErrors = array of argument validation errors
  • fileList = a structure of queries with the unzipped files under each file extension
  • success = boolean indicating the success or the failure of the unzip process
There are a couple of optional parameters you can pass to the function. If you are not using the advanced feature defined above, you can also pass two more values to the function:
  • specialCharsMatchRegEx = string with the regular expression that will match the special characters in the file names (by default "[^A-Za-z0-9\._\-]")
  • replaceSpecialChars = boolean indicating if special characters in the file names should be replaced (by default true)
If you are using the structure that defines specific file types to be extracted thenn you can specify specific setting for the "replaceSpecialChars" and "specialCharsMatchRegEx" for each file type as so:
<cfset locationsByFileType = structnew() />
<cfset locationsByFileType.jpg = structnew() />
<cfset locationsByFileType.jpg.destination = "images" />
<cfset locationsByFileType.jpg.replaceSpecialChars = false />
<cfset locationsByFileType.jpg.specialCharsMatchRegEx = "[^A-Za-z0-9\._\-]" />
Finally, here is the function code
<!---
Function:		unzipWithUTFSupport
Created on:		10.15.2007
Author:			Boyan Kostadinov
Version:		0.1
Arguments:		zipFile (string) required
				The name of the zip file to extract

				destination (string)
				The relative destination directory to extract the zip to (by default ".")

				overwriteDestination (boolean)
				Should the destination directory to be overwritte (by default true)

				extractLocationsByFileType (struct)
				A structure containing the type of files to be extracted
				and their destinations (by default empty structure)

				Example:
				<cfset locationsByFileType.jpg = structnew() />

				Specify the relative destination where to extract this type of files
				<cfset locationsByFileType.jpg.destination = "jpgFilesDir" />

				Should special characters be replaced for this type of files
				<cfset locationsByFileType.jpg.replaceSpecialChars = true />

				The regular expression to match special characters against
				<cfset locationsByFileType.jpg.specialCharsMatchRegEx = "[^A-Za-z0-9\._\-]" />

				If "replaceSpecialChars" and "specialCharsMatchRegEx" do not exist, the default
				values in the functions are used (true and "[^A-Za-z0-9\._\-]")

				replaceSpecialChars (boolean)
				Should special characters in the file names be replaced (by default true)

				specialCharsMatchRegEx (string)
				The regular expression that will match the special characters in the file names
				that need to replaced (by default "[^A-Za-z0-9\._\-]")

Return Value:	unzipResults (struct)
				A structure containing the results of the unzip process with the keys:
				success (boolean)
					the sucess of the unzip
				antMessage (string)
					the message the unzip task returns
				fileList (struct)
					the list of files/directories unzipped
Description:	I extract a zip file no matter of encoding with the help of Ant
--->
<cffunction name="unzipWithUTFSupport" hint="I extract a zip file no matter of encoding with the help of Ant" returntype="struct">
	<cfargument name="zipFile" type="string" required="yes" />
	<cfargument name="destination" type="string" required="no" default="." />
	<cfargument name="overwriteDestination" type="boolean" required="no" default="true" />
	<cfargument name="extractLocationsByFileType" type="struct" required="no" default="#structnew()#" />
	<cfargument name="replaceSpecialChars" type="boolean" required="no" default="true" />
	<cfargument name="specialCharsMatchRegEx" type="string" required="no" default="[^A-Za-z0-9\._\-]" />

	<!--- Create local variables for the zip file name and the destination directory --->
	<cfset var zipFileName = "" />
	<cfset var unzipDestination = "" />
	<cfset var uniqueUnzipDestinationDirectory = "" />
	<cfset var unzipResults = structnew() />
	<cfset var buildMessage = "" />
	<cfset var currentDir = "" />
	<cfset var fileRenamed = false />

	<!--- Set the name of the temporary ant build file --->
	<cfset var buildFile = expandpath("unzip.xml") />

	<cfset unzipResults.success = false />
	<cfset unzipResults.antMessage = "" />
	<cfset unzipResults.fileList = 0 />
	<cfset unzipResults.argumentErrors = arraynew(1) />

	<!--- Set the name of the zip file to extract --->
	<cfif arguments.zipFile neq "">
		<cfset zipFileName = expandPath(arguments.zipFile) />

		<cfif not fileExists(zipFileName)>
			<cfset arrayappend(unzipResults.argumentErrors, "The zip file #arguments.zipFile# was not found") />
		</cfif>
	<cfelse>
		<cfset arrayappend(unzipResults.argumentErrors, "The zip file was not specified") />
	</cfif>

	<!--- Set the extract destination --->
	<cfif arguments.destination neq "">
		<cfset unzipDestination = expandPath(arguments.destination) />
	<cfelse>
		<cfset arrayappend(unzipResults.argumentErrors, "Destination was empty") />
	</cfif>

	<!--- If there were not argument validation errors --->
	<cfif arrayisempty(unzipResults.argumentErrors)>
		<!--- Create a directory for the zip file based on the name of the zip archive --->
		<cfset uniqueUnzipDestinationDirectory =
			unzipDestination & "\" &
			rereplacenocase(arguments.zipFile, "\.zip$", "") />

		<cfdump var="#uniqueUnzipDestinationDirectory#" />
		<!--- Create the xml string for the ant build file --->
		<cfoutput>
		<!--- If the "extractLocationsByFileType" structure is not empty, loop over it
		to extract the different files types to the specified sub directory --->
		<cfsavecontent variable="unzipXml">
		<project>
			<target name="unzip">
				<cfif not structisempty(extractLocationsByFileType)>
					<cfloop list="#structKeyList(extractLocationsByFileType)#" index="key">
						<cfif isstruct(extractLocationsByFileType[key])>
				<unzip src="#zipFileName#" dest="#uniqueUnzipDestinationDirectory#\#lcase(extractLocationsByFileType[key].destination)#">
					<patternset>
						<include name="**/*.#lcase(key)#"/>
					</patternset>
				</unzip>
						<cfelse>
				<unzip src="#zipFileName#" dest="#uniqueUnzipDestinationDirectory#\#lcase(extractLocationsByFileType[key])#">
					<patternset>
						<include name="**/*.#lcase(key)#"/>
					</patternset>
				</unzip>
						</cfif>
					</cfloop>
				<cfelse>
				<unzip src="#zipFileName#" dest="#uniqueUnzipDestinationDirectory#" />
				</cfif>
			</target>
		</project>
		</cfsavecontent>
		</cfoutput>

		<cftry>
			<!--- If the destination directory already exists, delete it --->
			<cfif directoryExists(uniqueUnzipDestinationDirectory) and arguments.overwriteDestination>
				<cfdirectory action="delete" directory="#uniqueUnzipDestinationDirectory#" recurse="yes" />
			</cfif>

			<!--- Write the temporary ant build file on the file system --->
			<cffile action="write" nameconflict="overwrite" file="#buildFile#" output="#unzipXml#" />
		
			<!--- Execute the ant task with the created ant build file --->
			<!--- "messages" holds the coldfusion variable to write the ant output to --->
			<!--- "target" is the name of the "defaultTarget" (ant speak) to execute when runing the ant task --->
			<cfant buildFile="#buildFile#"
				defaultDirectory=""
				anthome=""
				messages="buildMessage"
				target="unzip"
			/>

			<cfif refindnocase(".*unable to expand to file.*", buildMessage)>
				<cfset unzipResults.success = false />
			<cfelseif refindnocase(".*build successful.*", buildMessage)>
				<cfset unzipResults.success = true />
			</cfif>

			<cfset unzipResults.antMessage = buildMessage />

			<!--- Delete the temporary ant build file --->
			<cffile action="delete" file="#buildFile#" />

			<cfif unzipResults.success>
				<cfif not structisempty(extractLocationsByFileType)>
					<cfset unzipResults.fileList = structnew() />
	
					<cfloop list="#structKeyList(extractLocationsByFileType)#" index="key">
						<cfif isstruct(extractLocationsByFileType[key])>
							<cfset currentDir =
							uniqueUnzipDestinationDirectory & "\" &
							extractLocationsByFileType[key].destination />
	
							<cfif structkeyexists(extractLocationsByFileType[key], "replaceSpecialChars")>
								<cfif isboolean(extractLocationsByFileType[key].replaceSpecialChars)>
									<cfset replaceSpecialChars = extractLocationsByFileType[key].replaceSpecialChars />
								</cfif>
							</cfif>
	
							<cfif replaceSpecialChars>
								<cfif structkeyexists(extractLocationsByFileType[key], "specialCharsMatchRegEx")>
									<cfset specialCharsMatchRegEx = extractLocationsByFileType[key].specialCharsMatchRegEx />
								</cfif>
							</cfif>
						<cfelse>
							<cfset currentDir = uniqueUnzipDestinationDirectory & "\" &
							extractLocationsByFileType[key] />
						</cfif>
	
						<!--- Get a list of the files in the directory --->
						<cfdirectory
							action="list"
							directory="#currentDir#" name="currentFileList" />
	
						<cfset fileRenamed = false />
	
						<cfif replaceSpecialChars>
							<!--- Loop over all the files --->
							<cfloop query="currentFileList">
								<!--- If the filename has special characters --->
								<cfif refind(specialCharsMatchRegEx, name)>
									<!--- Create a new name for the file by replacing all special characters with "_" --->
									<cfset newName = rereplace(name, specialCharsMatchRegEx, "_", "all") />
				
									<!--- Rename the file to the new name --->
									<cffile
										action="rename"
										source="#directory#\#name#"
										destination="#directory#\#newName#" />
	
									<cfset fileRenamed = true />
								</cfif>
							</cfloop>
						</cfif>
	
						<cfif fileRenamed>
							<!--- Get a list of the files in the directory
						(again since some files might have been renamed) --->
							<cfdirectory
								action="list"
								directory="#currentDir#" name="currentFileList" />
						</cfif>
	
						<cfset unzipResults.fileList[key] = currentFileList />
					</cfloop>
				<cfelse>
					<!--- Get a list of the files in the directory --->
					<cfdirectory
						action="list"
						directory="#uniqueUnzipDestinationDirectory#" name="currentFileList" />
	
					<cfset fileRenamed = false />
					<cfif arguments.replaceSpecialChars and specialCharsMatchRegEx neq "">
						<!--- Loop over all the files --->
						<cfloop query="currentFileList">
							<!--- If the filename has special characters --->
							<cfif refind(specialCharsMatchRegEx, name)>
								<!--- Create a new name for the file by replacing all special characters with "_" --->
								<cfset newName = rereplace(name, specialCharsMatchRegEx, "_", "all") />
			
								<!--- Rename the file to the new name --->
								<cffile
									action="rename"
									source="#directory#\#name#"
									destination="#directory#\#newName#" />
	
								<cfset fileRenamed = true />
							</cfif>
						</cfloop>
					</cfif>
	
					<cfif fileRenamed>
						<!--- Get a list of the files in the directory
						(again since some files might have been renamed) --->
						<cfdirectory
							action="list"
							directory="#uniqueUnzipDestinationDirectory#" name="currentFileList" />
					</cfif>
	
					<cfset unzipResults.fileList = currentFileList />
				</cfif>
			</cfif>
		<cfcatch type="any">
			<cfdump var="#cfcatch#" />
		</cfcatch>
		</cftry>
	</cfif>

	<cfreturn unzipResults />
</cffunction>

Friday, October 12, 2007

Get a User's Primary Group Membership from Active Directory with ColdFusion

Recently, I've had to do much work with Active Directory/LDAP. I needed a way to grab the user's primary group so I can set some permissions in my application based on the primary group the user is a member of. This turned out to be a bit tricky since the primary group for each user is not part of the list of groups the user is a member of. Instead, the primary group token (just an ID) is stored in each user's record. Seems pretty simple now that I got it working but there was a big lack of documentation on how to do this in ColdFusion. So here is the psudo code:
  1. We need to query Active Directory and get the value of the "primaryGroupID" for the selected user
  2. We also need a full list of groups in Active Directory (this is acheived with using the filter "(&(objectcategory=group))")
  3. Next, we need to get the group name from the full list of groups based on the primary group token we got from the user's record
  4. Bingo!
Here is the code:
<!--- Get the primary group id for an example user --->
<cfldap
	action="query"
	name="userLdapQuery"
	start="dc=ica,dc=com"
	server="ldapServer"
	username="ldapUser"
	password="secretPassword"
	scope="subtree"
	filter="sAMAccountName=bkostadinov"
	attributes="primaryGroupID"
	timeout="0"
	maxrows="1" />

<!--- Query the ldap server for the full list of groups --->
<cfldap
	action="query"
	name="groupsQuery"
	start="dc=ica,dc=com"
	server="ldapServer"
	username="ldapUser"
	password="secretPassword"
	scope="subtree"
	filter="(&(objectcategory=group))"
	attributes="primaryGroupToken,name"
	timeout="0" />

<!--- Beacause the list of groups the user belongs to,
does not contain the user's primary group, query the
ldap groups to get only the group name for witch
the value of the primaryGroupTokenAttribute
matches the value of the primaryGroupIDAttribute --->
<cfquery name="primaryGroupQuery" dbtype="query">
select	lower(name) as name
from	groupsQuery
where	primaryGroupToken = '#userLdapQuery.primaryGroupID#'
</cfquery>

<!--- Get the primary group name from the query resutls --->
<cfset primaryGroup = primaryGroupQuery["name"][1] />

Thursday, October 11, 2007

Must Have Tools for Windows Power Users

This is a list of must have tools with inspiration from blog post at http://www.nodans.com/index.cfm/2007/10/10/Dana-Kowalski-sez-Crack-Open-your-Toolbox-and-Share

Launchy: http://www.launchy.net/ - An open source keystroke launcher for Windows
XYPlorer: http://www.xyplorer.com - A windows file manager and explorer replacement
Hamachi: http://hamachi.cc/download/list.php - Software based VPN
FolderShare: http://www.foldershare.com - Sync files/directories between multiple computers
RoboForm: http://www.roboform.com - An automated form filer and password manager
Altiris Software Virtualization: http://www.svsdownloads.com - A virtual engine for applications
Paint.NET: http://www.svsdownloads.com - Free image and photo editing software
Quest Comparison Suite for SQL Server: http://www.quest.com/Comparison-Suite-for-SQL-Server - Free Microsoft SQL server comparison suite

Tuesday, October 09, 2007

Neat Prototype Trick - Activate First Form Element Without a Certain Class Name

Let's say you have form with a bunch of fields that you have disabled with the "read only" attribute so the user can't change the data. Also, you have applied a css class called "readOnlyInput" so you can style the input field as read only field (different color or whatever else you like). I had such form this evening but I wanted the focus to automatically go to the first non read only element when the page is loaded. Usually, you can pass focus on a form element by doing this:
// 'myFormField' is the id of form field you want to focus on
$('myFormField').focus();
However, if the input field already contains data, you might want to focus the field and highlight it in the same time. In that case you will do:
// 'myFormField' is the id of form field you want to focus on
$('myFormField').activate();
That is all fine and great but what when I have a bunch of input elements and some of them might be marked "read only". To make things more complicated (as I love to do), I would hate to hard code the id of the first element I want to focus/activate. So here is the solution:
// For each input element on the form
$A($('myForm').getInputs()).each(function(formElement) {
 // If the form element does not have the class "readOnlyInput"
 if (!$(formElement).hasClassName('readOnlyInput')) {
  // Activate that element
  $(formElement).activate();
  // Break out of the loop (prototype specific syntax here)
  throw $break;
 }
});
Just so Doug's kids would understand this, here it is in psudo code:
  1. Get a list of form input elements by using Prototype's built in "getInputs" function $('myForm').getInputs()
  2. Convert the list of form inputs to a Prototype array $A($('myForm').getInputs())
  3. Now for each of the elements in the array, run a function pass in the current element $A($('myForm').getInputs()).each(function(formElement)
  4. If the current form element has a css class not matching "readOnlyInput" if (!$(formElement).hasClassName('readOnlyInput'))
  5. Activate that form element $(formElement).activate();
  6. We found the first element so break out of the loop throw $break;

Monday, October 08, 2007

ColdFusion CFC Scope Exploration

Doug Boude's post at http://www.dougboude.com/blog/1/2007/10/Appropriate-Usage-of-the-THIS-Scope.cfm, prompted me to create this simple test that should show the difference between using the "this" scope, the "variables" scope and the "var" scope. Here is what you need to get things setup. Declare a test component as follows:
<cfcomponent displayname="thisScopeToUseOrNotToUse">
 <cffunction name="testMethod">
  <!--- Create a variable in the local scope of the function --->
  <cfset var iAmAccessableOnlyWithinThisMethod = true />

  <!--- Create a variable in the "this" scope --->
  <cfset this.iAmAccessableFromOutside = true />

  <!--- Create variable in the private component scope --->
  <cfset variables.iAmAccessableOnlyWithinThisCFC = true />
 </cffunction>

 <cffunction name="getIAmAccessableOnlyWithinThisCFCButCanBeViewedFromTheOutsideWithAPublicMethod">
  <cfreturn variables.iAmAccessableOnlyWithinThisCFC />
 </cffunction>
</cfcomponent>
Then declare a test file as follows:
<cfset testCFC = createObject("component", "thisScopeToUseOrNotToUse") />
<cfset testCFC.testMethod() />
=======================================
<p />
Try to read a variable declare in the "this" scope of the component
<p />
<strong>iAmAccessableFromOutside:</strong>
<cfdump var="#testCFC.iAmAccessableFromOutside#" />
<p />
=======================================
<p />
Try to read a variable declared in the private component scope (the "variables" scope)
<p />
<strong>iAmAccessableOnlyWithinThisCFC:</strong>
<cftry>
 <cfdump var="#testCFC.iAmAccessableOnlyWithinThisCFC#" />
<cfcatch type="any">
 <cfdump var="#cfcatch.message#" />
</cfcatch>
</cftry>
<p />
=======================================
<p />
Using a public method to read a variable in the private component scope (the "variables" scope):
<p />
<strong>getIAmAccessableOnlyWithinThisCFCButCanBeViewedFromTheOutsideWithAPublicMethod:</strong>
<cfoutput>#testCFC.getIAmAccessableOnlyWithinThisCFCButCanBeViewedFromTheOutsideWithAPublicMethod()#</cfoutput>
<p />
=======================================
<p />
<strong>Trying to read a variable declared only in the method scope:
<p />
iAmAccessableOnlyWithinThisMethod:</strong>
<cftry>
 <cfdump var="#testCFC.iAmAccessableOnlyWithinThisMethod#" />
<cfcatch type="any">
 <cfdump var="#cfcatch.message#" />
</cfcatch>
</cftry>
Here are the results: ColdFusion "this" Scope Test Now, for the explanation:
  1. First we create a instance of the compontent with "createObject"
  2. Call the "testMethod" of the component so we can set the variables
  3. Then we try to access the "iAmAccessableFromOutside" variable and get "true" since that was the value set to the variable in the "testMethod" function
  4. Next, we try to read a variable declared in the private component scope "iAmAccessableOnlyWithinThisCFC". The code block is wrapped with "cfcatch" so we get a nice error message when ColdFusion cannot access the privately declare variable
  5. However, we can use the method "getIAmAccessableOnlyWithinThisCFCButCanBeViewedFromTheOutsideWithAPublicMethod" which just returns the private scope variable "iAmAccessableOnlyWithinThisCFC"
  6. Lastly, we try to access the variable "iAmAccessableOnlyWithinThisMethod" which was declared in the private "var" scope of the "testMethod" function. The code block is wrapped with "cfcatch" so we get a nice error message when ColdFusion cannot access the privately declare variable

Friday, October 05, 2007

Web Page Syntax Highlighting for ColdFusion - Brush for SyntaxHighlighter

I have been working on a implementing a solution to highlight ColdFusion code snippets when they are being posted on your web site/blog. Highlighting the code according to the appropriate language can make it much easier to read and comprehend (and make it pretty of course). To do this, I have created a ColdFusion syntax "brush" for the JavaScript highlighter SyntaxHighlighter (formally dp.SyntaxHighlighter). In this context, a "brush" is a set of language specific settings for highlighting/prettifying code. SyntaxHighlighter is a pretty cool piece of software and I had some fun and faced some challenges while trying to create ColdFusion specific highlighting brush. Here is a demo of the highlighter at work when given ColdFusion code snippet:
<cfcomponent displayname="applicationController" extends="ModelGlue.unity.controller.Controller" output="false">

<cfscript>
// Set component variables
variables.appConfig = 0;
variables.modelGlueConfig = 0;
variables.rolePermissions = structnew();
variables.userIDWithAdminRights = "";
</cfscript>

<!---
Function: name="changeUserSiteRole"
Created on: 09.25.2007
Updated on: 09.25.2007
Author: Boyan Kostadinov
Arguments: event(ModelGlue.Core.Event)
Return Value: none
Description:
--->
<cffunction name="changeUserSiteRole" access="public" returntype="void" output="false">
 <cfargument name="event" type="ModelGlue.Core.Event">
 <cfset var userID = arguments.event.getValue("userID", 0) />
 <cfset var siteID = arguments.event.getValue("siteID", 0) />
 <cfset var roleID = arguments.event.getValue("roleID", 0) />
 <cfset var showAllUsers = arguments.event.getValue("showAllUsers", false) />

<cfif userID neq 0 and siteID neq 0 and roleID neq 0> <!--- Call the changeUserSiteRole method in the UsersGateway to change the user's role in the specified siteID ---> <cfset variables.usersGateway.changeUserSiteRole(userID, siteID, roleID) /> </cfif>
<cfif showAllUsers> <cfset arguments.event.addResult("allUsers") /> <cfelse> <cfset arguments.event.addResult("singleUser") /> </cfif> </cffunction>
<cffunction name="onQueueComplete" access="public" returntype="void" output="false"> <cfargument name="event" type="any">
<cfset arguments.event.setValue("defaultEvent", getModelGlue().getBean("modelGlueConfiguration").getdefaultEvent()) /> <cfset arguments.event.setValue("appConfig", variables.appConfig) /> <cfset arguments.event.setValue("ApplicationVersion", variables.appConfig.GetConfigSetting("ApplicationVersion")) /> <cfset arguments.event.setValue("ApplicationLastRevisionDate", variables.appConfig.GetConfigSetting("ApplicationLastRevisionDate")) /> <cfset arguments.event.setValue("urlPrefix", variables.defaultTemplate & "?" & arguments.event.getValue("eventValue") & "=") /> <cfset arguments.event.setValue("CurrentPageUrl", getfilefrompath(cgi.script_name) & "?" & cgi.query_string) /> </cffunction>
<cffunction name="onRequestEnd" access="public" returntype="void" output="false"> <cfargument name="event" type="any"> </cffunction>
</cfcomponent>
To install the SyntaxHighlighter on your page:
  1. Get it http://code.google.com/p/syntaxhighlighter/
  2. Extract it somewhere accessable from the web
  3. Add the following code
    <!-- Include the SyntaxHighlighter stylesheet -->
    <style type="text/css" media="screen">@import url("pathToSyntaxHighlighterStyle/syntaxHighlighter.css");</style>
    <!-- Include the core SyntaxHighlighter library -->
    <script language="javascript" src="pathToSyntaxHighlighter/scripts/shCore.js"></script>
    <!-- Include the ColdFusion brush -->
    <script language="javascript" src="pathToSyntaxHighlighter/scripts/shBrushColdFusion.js"></script>
    <script language="javascript">
    window.onload = function () {
     // Set the path to the flash component to enable 'copy to clipboard' in firefox
     dp.SyntaxHighlighter.ClipboardSwf = 'pathToSyntaxHighlighter/scripts/clipboard.swf';
     // Enable blogger mode (if using Blogger)
     dp.SyntaxHighlighter.BloggerMode();
     // Highlight page elements with the name "code"
     // For configuration options see http://code.google.com/p/syntaxhighlighter/wiki/HighlightAll
     dp.SyntaxHighlighter.HighlightAll('code', false, true, false, 1, false);
    }
    </script>
    
  4. Modify the css for your preferences (Firebug is really handy here to figure out what classes you want to modify). Here are my changes:
    <style type="text/css">
    .dp-highlighter ol li, .dp-highlighter .columns div { padding: 0px 3px 0px 0px  !important; font-size: 12px !important; font-family: 'Lucida Console', 'Bitstream Vera Sans Mono', 'Courier New', Monaco, Courier, monospace; }
    .dp-highlighter .tools { padding-left: 0px; }
    .dp-highlighter .tools a { color: #000; text-decoration: underline; }
    .dp-highlighter .tools a:hover { margin-right: 10px; }
    </style>
    
  5. Add some more brushes. For supported languages are C++, C#, CSS, Delphi, Java, JavaScript, PHP, Pythod, Ruby, SQL, VB, XML, HTML, XLST and now ColdFusion :-). More information on the languages at http://code.google.com/p/syntaxhighlighter/wiki/Languages.Here are the brushes I am using:
    <script language="javascript" src="scripts/shBrushCSharp.js"></script>
    <script language="javascript" src="scripts/shBrushCss.js"></script>
    <script language="javascript" src="scripts/shBrushJScript.js"></script>
    <script language="javascript" src="scripts/shBrushSql.js"></script>
    <script language="javascript" src="scripts/shBrushVb.js"></script>
    <script language="javascript" src="scripts/shBrushXml.js"></script>
    
  6. Post your code enclosed in "<pre>" tags as follows:
    <!-- For xml/html/xslt code -->
    <pre class="xml" name="code">
    <!-- For css code -->
    <pre class="css" name="code">
    <!-- For coldfusion code -->
    <pre class="cf" name="code">
    <!-- For sql code -->
    <pre class="sql" name="code">
What the ColdFusion brush does:
  1. Highlights ColdFusion functions, tags, attributes, strings, numbers, comments, cfscript comments
  2. Highlights a very limited set of Model-Glue specific keywords (I got adventures)
  3. Uses the Dreamweaver color shema to apply the syntax highlighting
How to use it and/or modify it:
  1. To just use it (without modifications), just include the compressed version from the svn location at http://opensourceprojects.googlecode.com/svn/dpSyntaxHighlighterColdFusionBrush/trunk/compressed/shBrushColdFusion.js
  2. To use it but modify the css styles, get the uncompressed version from http://opensourceprojects.googlecode.com/svn/dpSyntaxHighlighterColdFusionBrush/trunk/uncompressed/shBrushColdFusion.js and modify the css definitions in "this.Style"
  3. Include it as a brush in your page:
    <script language="javascript" src="pathTo_shBrushColdFusion.js"></script>
Here is actual brush definiton file (highlighted with the JavaScript SyntaxHighlighter brush):
dp.sh.Brushes.ColdFusion = function()
{
 this.CssClass = 'dp-coldfusion';
 this.Style = '.dp-coldfusion { font: 13px "Courier New", Courier, monospace; }' +
 '.dp-coldfusion .tag, .dp-coldfusion .tag-name { color: #990033; }' +
 '.dp-coldfusion .attribute { color: #990033; }' +
 '.dp-coldfusion .attribute-value { color: #0000FF; }' +
 '.dp-coldfusion .cfcomments { background-color: #FFFF99; color: #000000; }' +
 '.dp-coldfusion .cfscriptcomments { color: #999999; }' +
 '.dp-coldfusion .keywords { color: #0000FF; }' +
 '.dp-coldfusion .mgkeywords { color: #CC9900; }' +
 '.dp-coldfusion .numbers { color: #ff0000; }' +
 '.dp-coldfusion .strings { color: green; }';

 this.mgKeywords = 'setvalue getvalue addresult viewcollection viewstate';

 this.keywords = 'var eq neq gt gte lt lte not and or true false ' +
 'abs acos addsoaprequestheader addsoapresponseheader ' +
 'arrayappend arrayavg arrayclear arraydeleteat arrayinsertat ' +
 'arrayisempty arraylen arraymax arraymin arraynew ' +
 'arrayprepend arrayresize arrayset arraysort arraysum ' +
 'arrayswap arraytolist asc asin atn binarydecode binaryencode ' +
 'bitand bitmaskclear bitmaskread bitmaskset bitnot bitor bitshln ' +
 'bitshrn bitxor ceiling charsetdecode charsetencode chr cjustify ' +
 'compare comparenocase cos createdate createdatetime createobject ' +
 'createobject createobject createobject createobject createodbcdate ' +
 'createodbcdatetime createodbctime createtime createtimespan ' +
 'createuuid dateadd datecompare dateconvert datediff dateformat ' +
 'datepart day dayofweek dayofweekasstring dayofyear daysinmonth ' +
 'daysinyear de decimalformat decrementvalue decrypt decryptbinary ' +
 'deleteclientvariable directoryexists dollarformat duplicate encrypt ' +
 'encryptbinary evaluate exp expandpath fileexists find findnocase ' +
 'findoneof firstdayofmonth fix formatbasen generatesecretkey ' +
 'getauthuser getbasetagdata getbasetaglist getbasetemplatepath ' +
 'getclientvariableslist getcontextroot getcurrenttemplatepath ' +
 'getdirectoryfrompath getencoding getexception getfilefrompath ' +
 'getfunctionlist getgatewayhelper gethttprequestdata gethttptimestring ' +
 'getk2serverdoccount getk2serverdoccountlimit getlocale ' +
 'getlocaledisplayname getlocalhostip getmetadata getmetricdata ' +
 'getpagecontext getprofilesections getprofilestring getsoaprequest ' +
 'getsoaprequestheader getsoapresponse getsoapresponseheader ' +
 'gettempdirectory gettempfile gettemplatepath gettickcount ' +
 'gettimezoneinfo gettoken hash hour htmlcodeformat htmleditformat ' +
 'iif incrementvalue inputbasen insert int isarray isbinary isboolean ' +
 'iscustomfunction isdate isdebugmode isdefined isk2serverabroker ' +
 'isk2serverdoccountexceeded isk2serveronline isleapyear islocalhost ' +
 'isnumeric isnumericdate isobject isquery issimplevalue issoaprequest ' +
 'isstruct isuserinrole isvalid isvalid isvalid iswddx isxml ' +
 'isxmlattribute isxmldoc isxmlelem isxmlnode isxmlroot javacast ' +
 'jsstringformat lcase left len listappend listchangedelims listcontains ' +
 'listcontainsnocase listdeleteat listfind listfindnocase listfirst ' +
 'listgetat listinsertat listlast listlen listprepend listqualify ' +
 'listrest listsetat listsort listtoarray listvaluecount ' +
 'listvaluecountnocase ljustify log log10 lscurrencyformat lsdateformat ' +
 'lseurocurrencyformat lsiscurrency lsisdate lsisnumeric lsnumberformat ' +
 'lsparsecurrency lsparsedatetime lsparseeurocurrency lsparsenumber ' +
 'lstimeformat ltrim max mid min minute month monthasstring now ' +
 'numberformat paragraphformat parameterexists parsedatetime pi ' +
 'preservesinglequotes quarter queryaddcolumn queryaddrow querynew ' +
 'querysetcell quotedvaluelist rand randomize randrange refind ' +
 'refindnocase releasecomobject removechars repeatstring replace ' +
 'replacelist replacenocase rereplace rereplacenocase reverse right ' +
 'rjustify round rtrim second sendgatewaymessage setencoding ' +
 'setlocale setprofilestring setvariable sgn sin spanexcluding ' +
 'spanincluding sqr stripcr structappend structclear structcopy ' +
 'structcount structdelete structfind structfindkey structfindvalue ' +
 'structget structinsert structisempty structkeyarray structkeyexists ' +
 'structkeylist structnew structsort structupdate tan timeformat ' +
 'tobase64 tobinary toscript tostring trim ucase urldecode urlencodedformat ' +
 'urlsessionformat val valuelist week wrap writeoutput xmlchildpos ' +
 'xmlelemnew xmlformat xmlgetnodetype xmlnew xmlparse xmlsearch xmltransform ' +
 'xmlvalidate year yesnoformat';

 // Array to hold the possible string matches
 this.stringMatches = new Array();
 this.attributeMatches = new Array();
}

dp.sh.Brushes.ColdFusion.prototype = new dp.sh.Highlighter();
dp.sh.Brushes.ColdFusion.Aliases = ['coldfusion', 'cf'];

dp.sh.Brushes.ColdFusion.prototype.ProcessRegexList = function()
{
 function push(array, value)
 {
  array[array.length] = value;
 }

 function find(array, element)
 {
  for(var i = 0; i < array.length; i++){
   if(array[i] == element){
    return i;
   }
  }

  return -1;
 }

 var match = null;
 var regex = null;

 // Match numbers
 // (\\d+)
 this.GetMatches(new RegExp('\\b(\\d+)', 'gm'), 'numbers');

 // Match mg keywords
 this.GetMatches(new RegExp(this.GetKeywords(this.mgKeywords), 'igm'), 'mgkeywords');

 // Match single line comments via the built in single line regex (for cfscript)
 this.GetMatches(dp.sh.RegexLib.SingleLineCComments, 'cfscriptcomments');

 // Match multi line comments via the built in multi line regex (for cfscript)
 this.GetMatches(dp.sh.RegexLib.MultiLineCComments, 'cfscriptcomments');

 // Match tag based comments (including multiline comments)
 // (\<|<)!---[\\s\\S]*?---(\>|>)
 this.GetMatches(new RegExp('(\<|<)!---[\\s\\S]*?---(\>|>)', 'gm'), 'cfcomments');

 // Match attributes and their values excluding cfset tags
 // (cfset\\s*)?([:\\w-\.]+)\\s*=\\s*(".*?"|\'.*?\')*
 regex = new RegExp('(cfset\\s*)?([:\\w-\.]+)\\s*=\\s*(".*?"|\'.*?\')*', 'gm');
 while((match = regex.exec(this.code)) != null)
 {
  // If there is match in element 1 (the tag is cfset), continute to the next match
  if (match[1] != undefined && match[1] != '')
  {
   continue;
  }

  // Add the atribute to the matches only if it has a matching value (dbtype="query")
  // and the match is not an empty string
  if (match[3] != undefined && match[3] != '' && match[3] != '""' && match[3] != "''")
  {
   push(this.matches, new dp.sh.Match(match[2], match.index, 'attribute'));
   push(this.matches, new dp.sh.Match(match[3], match.index + match[0].indexOf(match[3]), 'attribute-value'));
   // Add the attribute value to the array of string matches
   push(this.stringMatches, match[3]);

   // Add the attribute to the array of attribute matches
   push(this.attributeMatches, match[2]);
  }
 }

 // Match opening and closing tag brackets
 // (\<|<)/*\?*(?!\!)|/*\?*(\>|>)
 this.GetMatches(new RegExp('(\<|<)/*\\?*(?!\\!)|/*\\?*(\>|>)', 'gm'), 'tag');

 // Match tag names
 // (\<|<)/*\?*\s*(\w+)
 regex = new RegExp('(?:\<|<)/*\\?*\\s*([:\\w-\.]+)', 'gm');
 while((match = regex.exec(this.code)) != null)
 {
  push(this.matches, new dp.sh.Match(match[1], match.index + match[0].indexOf(match[1]), 'tag-name'));
 }

 // Match keywords
 regex = new RegExp(this.GetKeywords(this.keywords), 'igm');
 while((match = regex.exec(this.code)) != null)
 {
  // if a match exists (there is a value for the attribute)
  if (find(this.attributeMatches, match[0]) == -1)
  {
   push(this.matches, new dp.sh.Match(match[0], match.index, 'keywords'));
  }
 }

 // Match cfset tags and quoated attributes
 regex = new RegExp('cfset\\s*.*(".*?"|\'.*?\')', 'gm');
 while((match = regex.exec(this.code)) != null)
 {
  // if a match exists (there is a value for the attribute)
  if(match[1] != undefined && match[1] != '')
  {
   push(this.matches, new dp.sh.Match(match[1], match.index + match[0].indexOf(match[1]), 'strings'));

   // Add the attribute to the array of string matches
   push(this.stringMatches, match[1]);
  }
 }

 // Match string enclosed in double quoats
 while((match = dp.sh.RegexLib.DoubleQuotedString.exec(this.code)) != null)
 {
  //if (this.stringMatches.indexOf(match[0]) == -1)
  if (find(this.stringMatches, match[0]) == -1)
   push(this.matches, new dp.sh.Match(match[0], match.index, 'strings'));
 }

 // Match string enclosed in single quoats
 while((match = dp.sh.RegexLib.SingleQuotedString.exec(this.code)) != null)
 {
  //if (this.stringMatches.indexOf(match[0]) == -1)
  if (find(this.stringMatches, match[0]) == -1)
   push(this.matches, new dp.sh.Match(match[0], match.index, 'strings'));
 }
}
Questions/comments? You can leave a comment here or use my contact form to send me your thoughts.

Full List of ColdFusion Functions (according to DW)

Yesterday I needed a full list of ColdFusion functions for my syntax highlighting. What easier way to get that list than using your IDE?! I found the functions under the dreamweaver directory, in the file "configuration\CodeHints\CodeHints.hml". After some regex matching with the following pattern:
<function name="code" html? pattern="(\w+).*>
and replacing with the following pattern:
$1
Here is the list of ColdFusion functions that I got:
Abs ACos AddSOAPRequestHeader AddSOAPResponseHeader ArrayAppend ArrayAvg ArrayClear
ArrayDeleteAt ArrayInsertAt ArrayIsEmpty ArrayLen ArrayMax ArrayMin ArrayNew
ArrayPrepend ArrayResize ArraySet ArraySort ArraySum ArraySwap ArrayToList
Asc ASin Atn BinaryDecode BinaryEncode BitAnd BitMaskClear BitMaskRead BitMaskSet
BitNot BitOr BitSHLN BitSHRN BitXor Ceiling CharsetDecode CharsetEncode Chr
Cjustify Compare CompareNoCase Cos CreateDate CreateDateTime CreateObject
CreateObject CreateObject CreateObject CreateObject CreateODBCDate
CreateODBCDateTime CreateODBCTime CreateTime CreateTimeSpan CreateUUID
DateAdd DateCompare DateConvert DateDiff DateFormat DatePart Day DayOfWeek
DayOfWeekAsString DayOfYear DaysInMonth DaysInYear DE DecimalFormat DecrementValue
Decrypt DecryptBinary DeleteClientVariable DirectoryExists DollarFormat Duplicate
Encrypt EncryptBinary Evaluate Exp ExpandPath FileExists Find FindNoCase FindOneOf
FirstDayOfMonth Fix FormatBaseN GenerateSecretKey GetAuthUser GetBaseTagData
GetBaseTagList GetBaseTemplatePath GetClientVariablesList GetContextRoot
GetCurrentTemplatePath GetDirectoryFromPath GetEncoding GetException GetFileFromPath
GetFunctionList GetGatewayHelper GetHTTPRequestData GetHTTPTimeString
GetK2ServerDocCount GetK2ServerDocCountLimit GetLocale GetLocaleDisplayName
GetLocalHostIP GetMetaData GetMetricData GetPageContext GetProfileSections
GetProfileString GetSOAPRequest GetSOAPRequestHeader GetSOAPResponse
GetSOAPResponseHeader GetTempDirectory GetTempFile GetTemplatePath GetTickCount
GetTimeZoneInfo GetToken Hash Hour HTMLCodeFormat HTMLEditFormat IIf IncrementValue
InputBaseN Insert Int IsArray IsBinary IsBoolean IsCustomFunction IsDate IsDebugMode
IsDefined IsK2ServerABroker IsK2ServerDocCountExceeded IsK2ServerOnline IsLeapYear
IsLocalHost IsNumeric IsNumericDate IsObject IsQuery IsSimpleValue IsSOAPRequest
IsStruct IsUserInRole IsValid IsValid IsValid IsWDDX IsXML IsXMLAttribute IsXMLDoc
IsXMLElem IsXMLNode IsXMLRoot JavaCast JSStringFormat LCase Left Len ListAppend
ListChangeDelims ListContains ListContainsNoCase ListDeleteAt ListFind ListFindNoCase
ListFirst ListGetAt ListInsertAt ListLast ListLen ListPrepend ListQualify ListRest
ListSetAt ListSort ListToArray ListValueCount ListValueCountNoCase LJustify Log
Log10 LSCurrencyFormat LSDateFormat LSEuroCurrencyFormat LSIsCurrency LSIsDate
LSIsNumeric LSNumberFormat LSParseCurrency LSParseDateTime LSParseEuroCurrency
LSParseNumber LSTimeFormat LTrim Max Mid Min Minute Month MonthAsString Now
NumberFormat ParagraphFormat ParameterExists ParseDateTime Pi PreserveSingleQuotes
Quarter QueryAddColumn QueryAddRow QueryNew QuerySetCell QuotedValueList Rand
Randomize RandRange REFind REFindNoCase ReleaseCOMObject RemoveChars RepeatString
Replace ReplaceList ReplaceNoCase REReplace REReplaceNoCase Reverse Right RJustify
Round RTrim Second SendGatewayMessage SetEncoding SetLocale SetProfileString
SetVariable Sgn Sin SpanExcluding SpanIncluding Sqr StripCR StructAppend StructClear
StructCopy StructCount StructDelete StructFind StructFindKey StructFindValue StructGet
StructInsert StructIsEmpty StructKeyArray StructKeyExists StructKeyList StructNew
StructSort StructUpdate Tan TimeFormat ToBase64 ToBinary ToScript ToString Trim UCase
URLDecode URLEncodedFormat URLSessionFormat Val ValueList Week Wrap WriteOutput
XMLChildPos XMLElemNew XMLFormat XMLGetNodeType XMLNew XMLParse XMLSearch XMLTransform
XMLValidate Year YesNoFormat

Wednesday, October 03, 2007

Readable ModelGlue Debugging Output while using CSS Layout

If you have your application layout done with CSS, most likely ModelGlue debugging output looks something like the picture below. ModelGlue Debugging with CSS Layouts Overlaying the debug output with your application styles can make it a bit unreadable. So here is a tip. Get and install the Web Developer extension if you don't have it already. Once installed (and you restarted Firefox), go to the "CSS" menu and under "Disable Styles", click on "All Styles" (or use the shortcut key "Ctrl+Shift+S"). Here is a screen shot of doing so: disable_css

If you use Internet Explorer instead of Firefox, you can use the same thing with the Internet Explored Developer Toolbar by going to the "Disable" menu and click on "All CSS". Here is a screen shot of doing so: ie

Now your ModelGlue debugging output should look much cleaner and readable: Model-Glue Debbuging without CSS

Free Ruby on Rails E-Book


Sitepoint.com is offering a free Ruby on Rails book called "Build Your Own Ruby on Rails Web Applications" by Patrick Lenz.

You can download the book in PDF format from the address
http://media.sitepoint.com/books/ror.pdf

Tuesday, October 02, 2007

Prototype.js Object Initialization and Bind While Enumerating Arrays

If the title sounds confusing, that is because it is. I couldn't figure out a better way to describe the issue I was running into. Let see if I can better demonstrate. I have a "initialize" function defined in my JavaScript object as such:

initialize: function() {
// siteID contains a list of ids such as "1,2,3,4"
// Convert the id list in siteIDs to a prototype array $A(siteIDs) and
// for each element in the array, run a function passing it the single siteID
$A(siteIDs).each(function(siteID) {
// If an element exists on the page with the id of setCurrentSiteElementPrefix + current siteID
if ($(this.setCurrentSiteElementPrefix + siteID)) {
 // Add an observer to the 'click' event of that element and bind it
 // to the 'getContent' function
 $(this.setCurrentSiteElementPrefix + siteID).observe('click', this.getContent.bind(this));
}
});
}
where "siteIDs" is a comma delimited list of ids ("1,2,3,4") and "setCurrentSiteElementPrefix" is a part of the name of link element with a dynamically generated id (such as setCurrentSiteElementPrefix_1, setCurrentSiteElementPrefix_2). Here is the above loop in pseudo code (just for your kids Doug): Convert the list of siteIDs to a prototype array and for each found array element, run a function passing the individual site id. $A(siteIDs) converts the list of site ids to an array, while .each() executes the inline function for each array element. // $A(siteIDs).each(function(siteID) If an element exists on the page with the id starting with setCurrentSiteElementPrefix (definition not shown here) + current siteID (passed by the inline function above) // if ($(this.setCurrentSiteElementPrefix + siteID)) Add an observer to the "click" event of that element and bind it to the "getContent" function. The "getContent" function has to bound to the "this" scope since the "getContent" function will not otherwise know what "this" means when trying to refer to elements defined in the object. // $(this.setCurrentSiteElementPrefix + siteID).observe('click', this.getContent.bind(this)); Never mind why I am doing this, I will follow up on that with another post. So I was trying to tie an event observer to the 'click' event of each link element by looping through the list of dynamic ids ($A(siteIDs).each(function(siteID)). However, that was giving me some issues with the message "this.getContent is not defined". Why the heck not, the code is inside the "initialize" method of my object?! It turns out while looping over an array as above the "this" scope is not preserved. So the function inside the loop has no idea what "this" means. The simple fix is to surround the function with parenthesis and append ".bind(this)":
initialize: function() {
 // Note the extra parenthesis before 'function(siteID)'
 $A(siteIDs).each((function(siteID) {
  if ($(this.setCurrentSiteElementPrefix + siteID)) {
   $(this.setCurrentSiteElementPrefix + siteID).observe('click', this.getContent.bind(this));
  }
 }).bind(this));
 // Note the ').bind(this)' before the semicolon
}
If you like to learn more on bind, check out Understanding bind and bindAsEventListener in Javascript and Understanding bind and bindAsEventListener in Javascript - Part II

Modifying Canvas ColdFusion Wiki

Canvas is an excellent wiki written by Ray Camden. It has a vast number of features for creating/maintaining documentation of any sort. I have been playing with it for a bit but I still haven't taken advantage of all its features. However, it lacks some basic features I thought it needed. I have been working on a side project and as a side effect I have added a few essentials (in my opinion). In subsequent posts, I will attempt to explain how to modify Canvas and add the following features:

  1. Database user authentication
  2. Persist user sessions between visits - "Remember Me" functionality
  3. LDAP user authentication
  4. Integration with Google Analytics
  5. Contact form
  6. Better exception handling / notifications

Loop Over ColdFusion Structure

Just a quick reference on how to loop over a structure without knowing its keys.
<cfset testStruct = structnew() />
<cfset testStruct.key1 = "test1" />
<cfset testStruct.key2 = "test2" />
<cfset testStruct.key3 = "test3" />

<cfdump var="#testStruct#" />

<cfloop list="#structKeyList(testStruct)#" index="key">
<cfoutput>
Key: #key#, Value: #testStruct[key]#
</cfoutput>
</cfloop>
// //]]>