[Code updated on March 27, 2014, for ACF compatibility]
For a client which is migrating from a Windows environment to Linux, I needed to check if the source code contained broken links, due to case sensitivity on Linux. So I wrote this script, which goes through all your source files, and checks all links inside href="..." and src="..." to see if they exist.
Hope it helps you out as well :)
Tested on Railo 4.2.0.006. Drop me a comment if there's anything not working in ACF / Railo 4.0.
<cfset maxErrors = 500 />| Viewed 8073 times
<cfset root = expandPath('/') />
<cfset files = directoryList(root, true, "path", "*.cfm|*.cfc|*.html|*.htm") />
<cfset currErrors = 0 />
<cfoutput>
<p>#arraylen(files)# cfm/cfc/html/htm files found</p>
</cfoutput>
<table border="1">
<thead>
<tr>
<th>Found in file</th>
<th>Non-existent link</th>
</tr>
</thead>
<tbody>
<cfsetting enablecfoutputonly="true" />
<cfset reg = '(src|href) ?= ?[''"](.*?)[''"]' />
<cfloop array="#files#" index="filePath">
<!--- skip railo directories --->
<cfif find('/WEB-INF/', filePath)>
<cfcontinue />
</cfif>
<cfset contents = fileRead(filePath) />
<!--- remove breaks and cfml comments from page where possible (nested cfml comments won't be completely removed) --->
<cfif refindNoCase(reg, contents)>
<cfset contents = replace(replace(contents, chr(10), chr(9), 'all'), chr(13), chr(9), 'all') />
<cfset contents = rereplace(contents, '<\!\-\-\-.*?\-\-\->', '', 'all') />
</cfif>
<cfset curr = 1 />
<cfloop condition="refindNoCase(reg, contents, curr)">
<cfset found = refindNoCase(reg, contents, curr, true) />
<cfset curr = found.pos[3] />
<cfset link = mid(contents, found.pos[3], found.len[3]) />
<!--- remove query string --->
<cfif find('?', link)>
<cfset link = listToArray(link, '?')[1] />
</cfif>
<!--- js links / dynamic links / abs links / cf code inside: skip --->
<cfif findNoCase('javascript:', link) or find('##', link) or find('http://', link) eq 1
or findNoCase('<cf', link) or link eq 'about:blank'
or find('https://', link) eq 1 or find('ftp://', link) eq 1 or find('mailto:', link) eq 1>
<cfcontinue />
</cfif>
<cfif left(link, 1) neq "/">
<cfset abslink = expandPath(replace(getDirectoryFromPath(filePath), root, "/") & link) />
<cfelse>
<cfset abslink = expandPath(link) />
</cfif>
<cfif not fileExists(abslink)>
<!--- link can be a directory! --->
<cfif directoryExists(abslink)>
<cfcontinue />
</cfif>
<cfoutput>
<tr>
<td>#htmleditformat(filePath)#</td>
<td>#mid(contents, found.pos[1], found.len[1])#<!---<br/>#htmleditformat(abslink)#---></td>
</tr>
</cfoutput>
<cfflush />
<cfif ++currErrors eq maxErrors>
<cfbreak />
</cfif>
<!--- file exists, okay. But did expandPath change case of the file? --->
<cfelseif compare(listLast(link, '/\'), listLast(absLink, '/\')) neq 0>
<cfoutput>
<tr>
<td>#htmleditformat(filePath)#</td>
<td>#mid(contents, found.pos[1], found.len[1])#
<b>Case sensitive mismatch</b>
</td>
</tr>
</cfoutput>
<cfflush />
<cfif ++currErrors eq maxErrors>
<cfbreak />
</cfif>
</cfif>
</cfloop>
<cfif currErrors eq maxErrors>
<cfoutput>
<tr>
<td colspan="2">
<h3 style="color:red">The max number of reported errors, #maxErrors#, has been reached</h3>
</td>
</tr>
</cfoutput>
<cfbreak />
</cfif>
</cfloop>
<cfsetting enablecfoutputonly="false" />
</tbody>
</table>
<h1>Done</h1>
#1 by Frank Semrau - March 26, 2014 at 12:51 PM
found your script today via coldfusionbloggers.org, very nice. I work on a similar problem for a costumer who has an old and large codebase which we first try to clean up before we go one with any change requests. For that we use CF10 and I had to made some slightly changes to your script to got it working on our system:
- the listInfo attribute for the directoryList function I changed to 'path'
- I prefer for arrays <cfloop index="i" from="1" to="#arrayLen(files)#"> and then instead using 'file' change that to 'files[i]'
Know I'm fine tuning the regEx and play with the mime types to minimize the fault results.
Thanks for that starting point.
Frank
#2 by Paul Klinkenberg - March 26, 2014 at 5:37 PM
Did you change the listInfo attribute because it did not work in CF10, or because of personal preference?
Cheers, Paul
#3 by Frank Semrau - March 27, 2014 at 1:07 PM
I changed the listInfo attribute because it did not work on CF10. There is no 'array' value for this attribute in ColdFusion, you can choose between 'query', 'name' and 'path' (https://wikidocs.adobe.com/wiki/display/coldfusionen/DirectoryList).
Regards,
Frank
#4 by Scott Busche - March 27, 2014 at 1:46 PM
#5 by Paul Klinkenberg - March 27, 2014 at 2:15 PM
I updated the code with your proposed changes. Thanks for taking the time to let me know.
Cheers, Paul