- qpscanner/fusebox5/fuseboxApplication.cfc
- v0.7.4
- 61 KB
- 1454
1<!---
2Copyright 2006-2007 TeraTech, Inc. http://teratech.com/
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15--->
16<cfcomponent output="false" hint="I am the Fusebox application object, formerly the application.fusebox data structure.">
17
18 <cfscript>
19 // initialize the fusebox (available to be read by developers but not to be written to)
20 this.isFullyLoaded = false;
21 this.circuits = structNew();
22 this.classes = structNew();
23 this.lexicons = structNew();
24 this.plugins = structNew();
25 this.pluginphases = structNew();
26 this.nonFatalExceptionPrefix = "INFORMATION (can be ignored): ";
27
28 // this always has to be overridden:
29 this.defaultFuseaction = "";
30
31 this.precedenceFormOrURL = "form";
32 this.fuseactionVariable = "fuseaction";
33
34 // these are all ignored:
35 this.parseWithComments = false;
36 this.ignoreBadGrammar = true;
37 this.allowLexicon = true;
38 this.useAssertions = true;
39
40 // FB55: this is implemented now:
41 this.conditionalParse = false;
42
43 this.password = "";
44 this.mode = "production";
45 this.scriptLanguage = "cfmx";
46 this.scriptFileDelimiter = "cfm";
47 this.maskedFileDelimiters = "htm,cfm,cfml,php,php4,asp,aspx";
48 this.characterEncoding = "utf-8";
49 // this is ignored:
50 this.parseWithIndentation = this.parseWithComments;
51 this.strictMode = false;
52 this.allowImplicitFusebox = false;
53 this.allowImplicitCircuits = false;
54 this.debug = false;
55 </cfscript>
56
57 <cffunction name="init" returntype="fuseboxApplication" access="public" output="false"
58 hint="I am the constructor.">
59 <cfargument name="appKey" type="string" required="true"
60 hint="I am FUSEBOX_APPLICATION_KEY." />
61 <cfargument name="appPath" type="string" required="true"
62 hint="I am FUSEBOX_APPLICATION_PATH." />
63 <cfargument name="myFusebox" type="myFusebox" required="true"
64 hint="I am the myFusebox data structure." />
65 <cfargument name="callerPath" type="string" required="true"
66 hint="I am FUSEBOX_CALLER_PATH." />
67 <cfargument name="appParameters" type="struct" required="true"
68 hint="I am FUSEBOX_PARAMETERS." />
69
70 <cfset var myVersion = "5.5.1" />
71 <!--- <cfset var myVersion = "5.5.0.#REReplace('$LastChangedRevision:683 $','[^0-9]','','all')#" /> --->
72
73 <cfset variables.factory = createObject("component","fuseboxFactory").init() />
74 <cfset variables.fuseboxLexicon = variables.factory.getBuiltinLexicon() />
75 <cfset variables.customAttributes = structNew() />
76
77 <cfset variables.fuseboxFileExtension = "" />
78
79 <cfset variables.fuseboxVersion = myVersion />
80
81 <cfset variables.appKey = arguments.appKey />
82 <cfset this.webrootdirectory = replace(getDirectoryFromPath(getBaseTemplatePath()),"\","/","all") />
83 <cfset variables.coreRoot = replace(getDirectoryFromPath(getCurrentTemplatePath()),"\","/","all") />
84
85 <cfset this.approotdirectory = getCanonicalPath(normalizePartialPath(arguments.callerPath) & normalizePartialPath(arguments.appPath)) />
86
87 <!--- this works on all platforms: --->
88 <cfset this.osdelimiter = "/" />
89
90 <cfset this.coreToAppRootPath = relativePath(variables.coreRoot,this.approotdirectory) />
91 <cfset this.appRootPathToCore = relativePath(this.approotdirectory,variables.coreRoot) />
92 <cfset this.coreToWebRootPath = relativePath(variables.coreRoot,this.webrootdirectory) />
93 <cfset this.WebRootPathToCore = relativePath(this.webrootdirectory,variables.coreRoot) />
94
95 <cfset this.parsePath = "parsed/" />
96 <cfset this.parseRootPath = "../" />
97 <cfset this.pluginsPath = "plugins/" />
98 <cfset this.lexiconPath = "lexicon/" />
99 <cfset this.errortemplatesPath = "errortemplates/" />
100
101 <!--- new in Fusebox 5.1: --->
102 <cfset this.self = CGI.SCRIPT_NAME />
103 <cfset this.queryStringStart = "?" />
104 <cfset this.queryStringSeparator = "&" />
105 <cfset this.queryStringEqual = "=" />
106
107 <cfset this.circuits = structNew() />
108 <cfset reload(arguments.appKey,arguments.appPath,arguments.myFusebox,arguments.appParameters) />
109
110 <cfif this.strictMode>
111 <!--- rootdirectory was deprecated in Fusebox 5 so we no longer set it it strict mode: --->
112 <cfset structDelete(this,"rootdirectory") />
113 <cfelse>
114 <!--- for FB4.0 compatibility: --->
115 <cfset this.rootdirectory = this.approotdirectory />
116 </cfif>
117
118 <cfreturn this />
119
120 </cffunction>
121
122 <cffunction name="reload" returntype="void" access="public" output="false"
123 hint="I (re)load the fusebox.xml file into memory and (re)load all of the application components referenced by that.">
124 <cfargument name="appKey" type="string" required="true"
125 hint="I am FUSEBOX_APPLICATION_KEY." />
126 <cfargument name="appPath" type="string" required="true"
127 hint="I am FUSEBOX_APPLICATION_PATH." />
128 <cfargument name="myFusebox" type="myFusebox" required="true"
129 hint="I am the myFusebox data structure." />
130 <cfargument name="appParameters" type="struct" required="true"
131 hint="I am FUSEBOX_PARAMETERS." />
132
133 <cfset var fbFile = "fusebox.xml.cfm" />
134 <cfset var fbFileAlt = "fusebox.xml" />
135 <cfset var fbFileExists = false />
136 <cfset var fbXML = "" />
137 <cfset var fbCode = "" />
138 <cfset var encodings = 0 />
139 <cfset var needToLoad = true />
140 <cfset var fuseboxFiles = 0 />
141 <cfset var p = "" />
142
143 <!--- FB55: cache of parsed file signatures --->
144 <cfset variables.parsedFileCache = structNew() />
145
146 <!--- FB55: can override fusebox.xml parameters programmatically --->
147 <!--- this sets up the defaults for the reload - prior to actually reading parameters --->
148 <cfloop collection="#arguments.appParameters#" item="p">
149 <cfset this[p] = arguments.appParameters[p] />
150 </cfloop>
151 <cfif structKeyExists(this,"allowImplicitFusebox") and this.allowImplicitFusebox>
152 <cfset this.allowImplicitCircuits = true />
153 </cfif>
154
155 <!---
156 since we need to check the file, regardless of whether we load it,
157 we might as well do the test up front and perform the strict check
158 that just one version exists (ticket 135)
159 --->
160 <cfset fbFileExists = fileExists(this.approotdirectory & fbFile) />
161 <cfif fbFileExists>
162 <cfif this.strictMode and fileExists(this.approotdirectory & fbFileAlt)>
163 <cfthrow type="fusebox.multipleFuseboxXML"
164 message="Both 'fusebox.xml' and 'fusebox.xml.cfm' exist"
165 detail="'fusebox.xml.cfm' will be used but 'fusebox.xml' also exists in '#this.approotdirectory#." />
166 </cfif>
167 <cfelse>
168 <cfset fbFile = fbFileAlt />
169 <cfset fbFileExists = fileExists(this.approotdirectory & fbFile) />
170 </cfif>
171
172 <cfif structKeyExists(this,"timestamp")>
173 <cfif fbFileExists>
174 <!--- we only check the time stamp if the file exists --->
175 <cfset needToLoad = fileModificationDate(this.approotdirectory & fbFile) gt parseDateTime(this.timestamp) />
176 <cfelse>
177 <!---
178 if we loaded the application and there is no fusebox.xml file,
179 then we must have loaded an implicit fusebox.xml file so there
180 is no need to reload it again because an implicit file can't change!
181 --->
182 <cfset needToLoad = false />
183 </cfif>
184 </cfif>
185
186 <cfif needToLoad>
187
188 <cfif this.debug>
189 <cfset arguments.myFusebox.trace("Compiler","Loading fusebox.xml file") />
190 </cfif>
191
192 <!--- attempt to load fusebox.xml(.cfm): --->
193 <cfif fbFileExists>
194 <cftry>
195
196 <cffile action="read" file="#this.approotdirectory##fbFile#"
197 variable="fbXML"
198 charset="#this.characterEncoding#" />
199
200 <cfset variables.fuseboxFileExtension = listLast(fbFile,".") />
201
202 <cfcatch type="security">
203 <!--- cffile denied by sandbox security --->
204 <cfthrow type="fusebox.security"
205 message="security error reading fusebox.xml"
206 detail="The file '#fbFile#' in the directory #this.approotdirectory# could not be read because sandbox security has disabled the cffile tag."
207 extendedinfo="#cfcatch.detail#" />
208 </cfcatch>
209
210 <cfcatch type="any">
211 <!--- the file exists but we cannot read it --->
212 <cfthrow type="fusebox.missingFuseboxXML"
213 message="missing fusebox.xml"
214 detail="The file '#fbFile#' in the directory #this.approotdirectory# could not be read."
215 extendedinfo="#cfcatch.detail#" />
216 </cfcatch>
217
218 </cftry>
219
220 <cfelse>
221
222 <cfif this.allowImplicitFusebox>
223
224 <cfif this.debug>
225 <cfset arguments.myFusebox.trace("Compiler","Implicit fusebox.xml(.cfm) identified") />
226 </cfif>
227 <cfset fbXML = "<fusebox/>" />
228 <!--- if the fusebox.xml file is missing there are no circuit declarations: --->
229 <cfset this.allowImplicitCircuits = true />
230
231 <cfelse>
232
233 <cfthrow type="fusebox.missingFuseboxXML"
234 message="missing fusebox.xml"
235 detail="The file '#fbFile#' could not be found in the directory #this.approotdirectory#." />
236
237 </cfif>
238
239 </cfif>
240
241 <cftry>
242
243 <cfset fbCode = xmlParse(fbXML) />
244
245 <!--- see if we need to re-read based on the encoding being different to our default --->
246 <cfset encodings = xmlSearch(fbCode,"/fusebox/parameters/parameter[@name='characterEncoding']") />
247 <cfif arrayLen(encodings) eq 1 and structKeyExists(encodings[1].xmlAttributes,"value")>
248 <cfif encodings[1].xmlAttributes.value is not this.characterEncoding>
249 <cfset this.characterEncoding = encodings[1].xmlAttributes.value />
250 <!--- now re-read the file in case anything is changed in that new encoding --->
251 <cffile action="read" file="#this.approotdirectory##fbFile#"
252 variable="fbXML"
253 charset="#this.characterEncoding#" />
254 <cfset fbCode = xmlParse(fbXML) />
255 </cfif>
256 </cfif>
257
258 <cfcatch type="any">
259 <cfthrow type="fusebox.fuseboxXMLError"
260 message="Error reading fusebox.xml"
261 detail="A problem was encountered while reading the #fbFile# file. This is usually caused by unmatched XML tags (a <tag> without a </tag> or without use of the <tag/> short-cut.)"
262 extendedinfo="#cfcatch.detail#" />
263 </cfcatch>
264
265 </cftry>
266
267 <cfif fbCode.xmlRoot.xmlName is not "fusebox">
268 <cfthrow type="fusebox.badGrammar.badFuseboxFile"
269 detail="Fusebox file does contain 'fusebox' XML"
270 message="Fusebox file #fbFile# does not contain 'fusebox' as the root XML node." />
271 </cfif>
272
273 <cfset loadParameters(fbCode) />
274
275 <!--- FB55: can override fusebox.xml parameters programmatically --->
276 <cfloop collection="#arguments.appParameters#" item="p">
277 <cfset this[p] = arguments.appParameters[p] />
278 </cfloop>
279 <cfif structKeyExists(this,"allowImplicitFusebox") and this.allowImplicitFusebox>
280 <cfset this.allowImplicitCircuits = true />
281 </cfif>
282
283 <!---
284 if the user overrides certain path variables, we need to normalize them:
285 - this.parsePath (reset parseRootPath)
286 - this.pluginsPath
287 - this.lexiconPath
288 - this.errortemplatesPath
289 normalizing means changing all \ to / and appending / if not present
290 --->
291 <cfset this.parsePath = normalizePartialPath(this.parsePath) />
292 <cfset this.parseRootPath = relativePath(expandFuseboxPath(this.parsePath),getApplicationRoot()) />
293 <cfset this.pluginsPath = normalizePartialPath(this.pluginsPath) />
294 <cfset this.lexiconPath = normalizePartialPath(this.lexiconPath) />
295 <cfset this.errortemplatesPath = normalizePartialPath(this.errortemplatesPath) />
296
297 <!--- Fusebox 5.1: default this.myself: --->
298 <cfif not structKeyExists(this,"myself")>
299 <cfset this.myself = getDefaultMyself(this.self) />
300 </cfif>
301
302 <cfset loadLexicons(fbCode) />
303 <cfset loadClasses(fbCode) />
304 <cfset loadPlugins(fbCode) />
305 <cfset loadGlobalPreAndPostProcess(fbCode) />
306 <!--- save fusebox.xml DOM internally for (re-)loading circuits --->
307 <cfset variables.fbCode = fbCode />
308 <cfset variables.fbFile = fbFile />
309 <cfset this.timestamp = now() />
310 </cfif>
311
312 <!--- to track circuit loads on this request --->
313 <cfparam name="request.__fusebox.CircuitsLoaded" default="#structNew()#" />
314 <cfset loadCircuits(variables.fbCode,arguments.myFusebox) />
315
316 <!--- FB5: fusebox.loadclean will delete all the parsed files --->
317 <cfif arguments.myFusebox.parameters.clean>
318 <cfset deleteParsedFiles() />
319 </cfif>
320
321 <!--- application data available to developers via getApplicationData() method: --->
322 <cfset variables.data = structNew() />
323
324 <cfset this.isFullyLoaded = true />
325 <cfset this.applicationStarted = false />
326 <cfset this.dateLastLoaded = now() />
327
328 <!---
329 The following documented parts of application.fusebox are not supported in Fusebox 5:
330 - application.fusebox.xml
331 - application.fusebox.globalfuseactions.*
332 - application.fusebox.circuits.*.xml
333 - application.fusebox.circuits.preFuseaction.*
334 - application.fusebox.circuits.postFuseaction.*
335 - application.fusebox.circuits.*.fuseactions.*.xml
336 --->
337
338 </cffunction>
339
340 <cffunction name="getPluginsPath" returntype="string" access="public" output="false"
341 hint="I am a convenience method to return the location of the plugins.">
342
343 <cfreturn this.pluginsPath />
344
345 </cffunction>
346
347 <!--- FB55: exposes this directly in myFusebox for convenience --->
348 <cffunction name="getApplicationData" returntype="struct" access="public" output="false"
349 hint="I return a reference to the application data cache. This is a new concept in Fusebox 5.">
350
351 <cfreturn variables.data />
352
353 </cffunction>
354
355 <cffunction name="getApplicationRoot" returntype="any" access="public" output="false"
356 hint="I am a convenience method to return the full application root directory path.">
357
358 <cfreturn this.approotdirectory />
359
360 </cffunction>
361
362 <cffunction name="expandFuseboxPath" returntype="any" access="public" output="false"
363 hint="I expand a path in the context of a Fusebox application.">
364 <cfargument name="partialPath" type="any" required="true"
365 hint="I am the partial path to expand. If I do not begin with a /, prepend the Fusebox application root." />
366
367 <cfif left(arguments.partialPath,1) is "/">
368 <!--- absolute path, i.e., root-relative or mapped --->
369 <cfreturn replace(expandPath(arguments.partialPath),"\","/","all") />
370 <cfelse>
371 <!--- relative, i.e., relative to the Fusebox application root --->
372 <cfreturn getApplicationRoot() & arguments.partialPath />
373 </cfif>
374
375 </cffunction>
376
377 <cffunction name="getFuseboxXMLFilename" returntype="string" access="public" output="false"
378 hint="I return the actual name of the fusebox.xml(.cfm) file.">
379
380 <cfreturn variables.fbFile />
381
382 </cffunction>
383
384 <cffunction name="getCoreToAppRootPath" returntype="any" access="public" output="false"
385 hint="I am a convenience method to return the relative path from the core files to the application root.">
386
387 <cfreturn this.coreToAppRootPath />
388
389 </cffunction>
390
391 <cffunction name="compileAll" returntype="void" access="public" output="false"
392 hint="I compile all the public fuseactions in the application.">
393 <cfargument name="myFusebox" type="myFusebox" required="true"
394 hint="I am the myFusebox data structure." />
395
396 <cfset var c = 0 />
397 <cfset var a = 0 />
398 <cfset var f = 0 />
399
400 <cfloop collection="#this.circuits#" item="c">
401 <cfset a = this.circuits[c].getFuseactions() />
402 <cfloop collection="#a#" item="f">
403 <cfif a[f].access is "public">
404 <cfset compileRequest(c & "." & f,arguments.myFusebox) />
405 </cfif>
406 </cfloop>
407 </cfloop>
408
409 </cffunction>
410
411 <cffunction name="compileRequest" returntype="struct" access="public" output="false"
412 hint="I compile a specific (public) fuseaction as an external request.">
413 <cfargument name="circuitFuseaction" type="string" required="true"
414 hint="I am the full name of the requested fuseaction (circuit.fuseaction)." />
415 <cfargument name="myFusebox" type="myFusebox" required="true"
416 hint="I am the myFusebox data structure." />
417
418 <cfset var myVersion = getVersion() />
419 <cfset var circuit = "" />
420 <cfset var fuseaction = listLast(arguments.circuitFuseaction,".") />
421 <cfset var i = 0 />
422 <cfset var n = 0 />
423 <cfset var needRethrow = true />
424 <cfset var needTryOnFuseaction = false />
425 <cfset var parsedName = "#lCase(arguments.circuitFuseaction)#.cfm" />
426 <cfset var parsedFile = "#this.parsePath##parsedName#" />
427 <cfset var fullParsedFile = "#this.expandFuseboxPath(this.parsePath)##parsedName#" />
428 <cfset var result = structNew() />
429 <cfset var writer = 0 />
430
431 <!--- to track reloads on this request --->
432 <cfparam name="request.__fusebox.CircuitsLoaded" default="#structNew()#" />
433 <cfparam name="request.__fusebox.fuseactionsDone" default="#structNew()#" />
434
435 <!--- note that in Fusebox 5, these are really all the same set of files --->
436 <cfset arguments.myFusebox.version.loader = myVersion />
437 <cfset arguments.myFusebox.version.parser = myVersion />
438 <cfset arguments.myFusebox.version.transformer = myVersion />
439 <!--- legacy test from FB41 although it's a bit pointless --->
440 <cfif myFusebox.version.runtime neq myFusebox.version.loader>
441 <cfthrow type="fusebox.versionMismatchException"
442 message="The loader is not the same version as the runtime" />
443 </cfif>
444
445 <!--- validate format of the fuseaction: --->
446 <cfif listLen(arguments.circuitFuseaction,".") lt 2>
447 <cfthrow type="fusebox.malformedFuseaction"
448 message="malformed Fuseaction"
449 detail="You specified a malformed Fuseaction of #arguments.circuitFuseaction#. A fully qualified Fuseaction must be in the form [Circuit].[Fuseaction]." />
450 </cfif>
451
452 <cfset circuit = left(arguments.circuitFuseaction,len(arguments.circuitFuseaction)-len(fuseaction)-1) />
453
454 <!--- set up myFusebox values for this request: --->
455 <cfset arguments.myFusebox.originalCircuit = circuit />
456 <cfset arguments.myFusebox.originalFuseaction = fuseaction />
457 <cfloop collection="#this.plugins#" item="i">
458 <cfset arguments.myFusebox.plugins[i] = structNew() />
459 </cfloop>
460
461 <!--- ticket 293 - prevent dynamic do() from executing until prerequisites complete --->
462 <cfset request.__fusebox.dynamicDoOK = true />
463
464 <!--- check access on request - if the circuit/fuseaction doesn't exist we trap it later --->
465 <cfif structKeyExists(this.circuits,circuit) and
466 structKeyExists(this.circuits[circuit].fuseactions,fuseaction) and
467 this.circuits[circuit].fuseactions[fuseaction].getAccess() is not "public">
468 <cfthrow type="fusebox.invalidAccessModifier"
469 message="Invalid Access Modifier"
470 detail="You tried to access #circuit#.#fuseaction# which does not have access modifier of public. A Fuseaction which is to be accessed from anywhere outside the application (such as called via an URL, or a FORM, or as a web service) must have an access modifier of public or if unspecified at least inherit such a modifier from its circuit.">
471 </cfif>
472
473 <cfif not fileExists(fullParsedFile) or arguments.myFusebox.parameters.parse>
474 <cflock name="#fullParsedFile#" type="exclusive" timeout="300">
475 <cfif not fileExists(fullParsedFile) or arguments.myFusebox.parameters.parse>
476 <cfset request.__fusebox.SuppressPlugins = false />
477 <cfset writer = createObject("component","fuseboxWriter").init(this,arguments.myFusebox) />
478 <cfset writer.open(parsedName) />
479 <cfset writer.rawPrintln("<!--- circuit: #circuit# --->") />
480 <cfset writer.rawPrintln("<!--- fuseaction: #fuseaction# --->") />
481 <cfset writer.rawPrintln("<cftry>") />
482 <cfset writer.setCircuit(circuit) />
483 <cfset writer.setFuseaction(fuseaction) />
484 <cfif variables.hasProcess["appinit"]>
485 <cfset writer.setPhase("appinit") />
486 <cfset writer.println("<cfif myFusebox.applicationStart or") />
487 <cfset writer.println(' not myFusebox.getApplication().applicationStarted>') />
488 <cfset writer.println(' <cflock name="##application.ApplicationName##_fusebox_##FUSEBOX_APPLICATION_KEY##_appinit" type="exclusive" timeout="30">') />
489 <cfset writer.println(' <cfif not myFusebox.getApplication().applicationStarted>') />
490 <cfset request.__fusebox.SuppressPlugins = true />
491 <cfset variables.process["appinit"].compile(writer) />
492 <cfset writer.println(' <cfset myFusebox.getApplication().applicationStarted = true />') />
493 <cfset writer.println(' </cfif>') />
494 <cfset writer.println(' </cflock>') />
495 <cfset writer.println('</cfif>') />
496 </cfif>
497 <cfset request.__fusebox.SuppressPlugins = false />
498 <cfif structKeyExists(this.pluginPhases,"preProcess")>
499 <cfset n = arrayLen(this.pluginPhases["preProcess"]) />
500 <cfloop from="1" to="#n#" index="i">
501 <cfset this.pluginPhases["preProcess"][i].compile(writer) />
502 </cfloop>
503 </cfif>
504 <cfset writer.setPhase("preprocessFuseactions") />
505 <cfif variables.hasProcess["preprocess"]>
506 <cfset variables.process["preprocess"].compile(writer) />
507 </cfif>
508 <cfif structKeyExists(this.pluginPhases,"fuseactionException") and
509 arrayLen(this.pluginPhases["fuseactionException"]) gt 0 and
510 not request.__fusebox.SuppressPlugins>
511 <cfset needTryOnFuseaction = true />
512 <cfset writer.rawPrintln("<cftry>") />
513 </cfif>
514 <cfif structKeyExists(this.pluginPhases,"preFuseaction")>
515 <cfset n = arrayLen(this.pluginPhases["preFuseaction"]) />
516 <cfloop from="1" to="#n#" index="i">
517 <cfset this.pluginPhases["preFuseaction"][i].compile(writer) />
518 </cfloop>
519 </cfif>
520 <cfset writer.setPhase("requestedFuseaction") />
521 <cfset compile(writer,circuit,fuseaction,true) />
522 <cfif structKeyExists(this.pluginPhases,"postFuseaction")>
523 <cfset n = arrayLen(this.pluginPhases["postFuseaction"]) />
524 <cfloop from="1" to="#n#" index="i">
525 <cfset this.pluginPhases["postFuseaction"][i].compile(writer) />
526 </cfloop>
527 </cfif>
528 <cfif needTryOnFuseaction>
529 <cfset n = arrayLen(this.pluginPhases["fuseactionException"]) />
530 <cfloop from="1" to="#n#" index="i">
531 <cfset this.pluginPhases["fuseactionException"][i].compile(writer) />
532 </cfloop>
533 <cfset writer.rawPrintln("</cftry>") />
534 </cfif>
535 <cfset writer.setPhase("postprocessFuseactions") />
536 <cfif variables.hasProcess["postprocess"]>
537 <cfset variables.process["postprocess"].compile(writer) />
538 </cfif>
539 <cfif structKeyExists(this.pluginPhases,"postProcess")>
540 <cfset n = arrayLen(this.pluginPhases["postProcess"]) />
541 <cfloop from="1" to="#n#" index="i">
542 <cfset this.pluginPhases["postProcess"][i].compile(writer) />
543 </cfloop>
544 </cfif>
545 <cfif structKeyExists(this.pluginPhases,"processError") and
546 not request.__fusebox.SuppressPlugins>
547 <cfset n = arrayLen(this.pluginPhases["processError"]) />
548 <cfloop from="1" to="#n#" index="i">
549 <cfset needRethrow = false />
550 <cfset this.pluginPhases["processError"][i].compile(writer) />
551 </cfloop>
552 </cfif>
553 <cfif needRethrow>
554 <cfset writer.rawPrintln('<' & 'cfcatch><' & 'cfrethrow><' & '/cfcatch>') />
555 </cfif>
556 <cfset writer.rawPrintln("</cftry>") />
557 <cfset writer.close(variables.parsedFileCache) />
558 </cfif>
559 </cflock>
560 </cfif>
561
562 <cfset result.parsedName = parsedName />
563 <cfif left(parsedFile,1) is "/">
564 <cfset result.parsedFile = parsedFile />
565 <cfelse>
566 <cfset result.parsedFile = this.getCoreToAppRootPath() & parsedFile />
567 </cfif>
568 <cfset result.lockName = fullParsedFile />
569
570 <cfreturn result />
571
572 </cffunction>
573
574 <cffunction name="do" returntype="string" access="public" output="true"
575 hint="I compile and execute a specific fuseaction.">
576 <cfargument name="circuitFuseaction" type="string" required="true"
577 hint="I am the full name of the requested fuseaction (circuit.fuseaction)." />
578 <cfargument name="myFusebox" type="myFusebox" required="true"
579 hint="I am the myFusebox data structure." />
580 <cfargument name="returnOutput" type="boolean" required="true"
581 hint="I indicate whether to display output (false) or return the output (true)." />
582
583 <cfset var parsedFileInfo = 0 />
584 <cfset var output = "" />
585 <cfset var circuit = "" />
586 <cfset var fuseaction = "" />
587
588 <!--- ticket 293 - prevent dynamic do() from executing until prerequisites complete --->
589 <cfif not structKeyExists(request,"__fusebox") or
590 not structKeyExists(request.__fusebox,"dynamicDoOK") or
591 not request.__fusebox.dynamicDoOK>
592 <cfif structKeyExists(arguments.myFusebox,"originalCircuit") and
593 structKeyExists(arguments.myFusebox,"originalFuseaction")>
594 <cfset output = output & " while executing " & arguments.myFusebox.originalCircuit &
595 "." & arguments.myFusebox.originalFuseaction />
596 </cfif>
597 <cfthrow type="fusebox.dynamicDoNotAllowed" message="dynamic 'do' not allowed"
598 detail="This request did not reach the state where dynamic 'do' is possible#output#." />
599 </cfif>
600
601 <!--- allow for abbreviated circuitFuseaction form: --->
602 <cfif listLen(arguments.circuitFuseaction,".") lt 2>
603 <cfset arguments.circuitFuseaction = arguments.myFusebox.thisCircuit & "." & arguments.circuitFuseaction />
604 <cfelse>
605 <cfset circuit = listFirst(arguments.circuitFuseaction,".") />
606 <cfif circuit is not arguments.myFusebox.thisCircuit>
607 <!--- different circuit, check access is not private (implicit fuseactions cannot be private) --->
608 <cfset fuseaction = listRest(arguments.circuitFuseaction,".") />
609 <cfif structKeyExists(this.circuits,circuit) and
610 structKeyExists(this.circuits[circuit].fuseactions,fuseaction) and
611 this.circuits[circuit].fuseactions[fuseaction].getAccess() is "private">
612 <cfthrow type="fusebox.invalidAccessModifier"
613 message="invalid access modifier"
614 detail="The fuseaction '#circuit#.#fuseaction#' has an access modifier of private and can only be called from within its own circuit. Use an access modifier of internal or public to make it available outside its immediate circuit." />
615 </cfif>
616 </cfif>
617 </cfif>
618
619 <cfset parsedFileInfo = compileDynamicDo(arguments.circuitFuseaction,arguments.myFusebox) />
620
621 <cfif this.debug>
622 <cfset arguments.myFusebox.trace("Runtime","<do action=""#arguments.circuitFuseaction#""/> -- dynamic") />
623 </cfif>
624 <cfinvoke component="fuseboxExecutionContext" method="__executeDynamicDo" returnvariable="output"
625 parsedFileInfo="#parsedFileInfo#" myFusebox="#arguments.myFusebox#" returnOutput="#arguments.returnOutput#" />
626
627 <cfreturn output />
628
629 </cffunction>
630
631 <cffunction name="compileDynamicDo" returntype="struct" access="private" output="false"
632 hint="I compile a specific fuseaction as a dynamic request.">
633 <cfargument name="circuitFuseaction" type="string" required="true"
634 hint="I am the full name of the requested fuseaction (circuit.fuseaction)." />
635 <cfargument name="myFusebox" type="myFusebox" required="true"
636 hint="I am the myFusebox data structure." />
637
638 <cfset var circuit = "" />
639 <cfset var fuseaction = listLast(arguments.circuitFuseaction,".") />
640 <cfset var i = 0 />
641 <cfset var n = 0 />
642 <cfset var needTryOnFuseaction = false />
643 <cfset var parsedName = "do.#lCase(arguments.circuitFuseaction)#.cfm" />
644 <cfset var parsedFile = "#this.parsePath##parsedName#" />
645 <cfset var fullParsedFile = "#this.expandFuseboxPath(this.parsePath)##parsedName#" />
646 <cfset var result = structNew() />
647 <cfset var writer = 0 />
648
649 <!--- validate format of the fuseaction: --->
650 <cfif listLen(arguments.circuitFuseaction,".") lt 2>
651 <cfthrow type="fusebox.malformedFuseaction"
652 message="malformed Fuseaction"
653 detail="You specified a malformed Fuseaction of #arguments.circuitFuseaction#. A fully qualified Fuseaction must be in the form [Circuit].[Fuseaction]." />
654 </cfif>
655
656 <cfset circuit = left(arguments.circuitFuseaction,len(arguments.circuitFuseaction)-len(fuseaction)-1) />
657
658 <cfif not fileExists(fullParsedFile) or arguments.myFusebox.parameters.parse>
659 <cflock name="#fullParsedFile#" type="exclusive" timeout="300">
660 <cfif not fileExists(fullParsedFile) or arguments.myFusebox.parameters.parse>
661 <cfset writer = createObject("component","fuseboxWriter").init(this,arguments.myFusebox) />
662 <cfset writer.open(parsedName) />
663 <cfset writer.rawPrintln("<!--- circuit: #circuit# --->") />
664 <cfset writer.rawPrintln("<!--- fuseaction: #fuseaction# --->") />
665 <cfset writer.setCircuit(circuit) />
666 <cfset writer.setFuseaction(fuseaction) />
667 <cfif structKeyExists(this.pluginPhases,"fuseactionException") and
668 arrayLen(this.pluginPhases["fuseactionException"]) gt 0 and
669 not request.__fusebox.SuppressPlugins>
670 <cfset needTryOnFuseaction = true />
671 <cfset writer.rawPrintln("<cftry>") />
672 </cfif>
673 <cfif structKeyExists(this.pluginPhases,"preFuseaction")>
674 <cfset n = arrayLen(this.pluginPhases["preFuseaction"]) />
675 <cfloop from="1" to="#n#" index="i">
676 <cfset this.pluginPhases["preFuseaction"][i].compile(writer) />
677 </cfloop>
678 </cfif>
679 <cfset writer.setPhase("requestedFuseaction") />
680 <cfset compile(writer,circuit,fuseaction) />
681 <cfif structKeyExists(this.pluginPhases,"postFuseaction")>
682 <cfset n = arrayLen(this.pluginPhases["postFuseaction"]) />
683 <cfloop from="1" to="#n#" index="i">
684 <cfset this.pluginPhases["postFuseaction"][i].compile(writer) />
685 </cfloop>
686 </cfif>
687 <cfif needTryOnFuseaction>
688 <cfset n = arrayLen(this.pluginPhases["fuseactionException"]) />
689 <cfloop from="1" to="#n#" index="i">
690 <cfset this.pluginPhases["fuseactionException"][i].compile(writer) />
691 </cfloop>
692 <cfset writer.rawPrintln("</cftry>") />
693 </cfif>
694 <cfset writer.close(variables.parsedFileCache) />
695 </cfif>
696 </cflock>
697 </cfif>
698
699 <cfset result.parsedName = parsedName />
700 <cfif left(parsedFile,1) is "/">
701 <cfset result.parsedFile = parsedFile />
702 <cfelse>
703 <cfset result.parsedFile = this.getCoreToAppRootPath() & parsedFile />
704 </cfif>
705 <cfset result.lockName = fullParsedFile />
706
707 <cfreturn result />
708
709 </cffunction>
710
711 <cffunction name="compile" returntype="void" access="public" output="false"
712 hint="I compile a specific fuseaction during a request (such as for a 'do' verb).">
713 <cfargument name="writer" type="any" required="false"
714 hint="I am the parsed file writer object. I am required but it's faster to specify that I am not required." />
715 <cfargument name="circuit" type="any" required="false"
716 hint="I am the circuit name. I am required but it's faster to specify that I am not required." />
717 <cfargument name="fuseaction" type="any" required="false"
718 hint="I am the fuseaction name, within the specified circuit." />
719 <cfargument name="topLevel" type="boolean" default="false"
720 hint="I specify whether or not this is a top-level (public) request." />
721
722 <cfset var c = "" />
723
724 <cfif not structKeyExists(this.circuits,arguments.circuit)>
725 <cfif this.allowImplicitCircuits>
726 <!--- FB55: attempt to create an implicit circuit --->
727 <cfset this.circuits[arguments.circuit] =
728 createObject("component","fuseboxImplicitCircuit")
729 .init(this,arguments.circuit,arguments.writer.getMyFusebox()) />
730 <!--- still need to check access here! --->
731 <cfif arguments.writer.getMyFusebox().thisCircuit is not arguments.circuit and
732 structKeyExists(this.circuits[circuit].fuseactions,arguments.fuseaction) and
733 this.circuits[circuit].fuseactions[fuseaction].getAccess() is "private">
734 <cfthrow type="fusebox.invalidAccessModifier"
735 message="invalid access modifier"
736 detail="The fuseaction '#arguments.circuit#.#arguments.fuseaction#' has an access modifier of private and can only be called from within its own circuit. Use an access modifier of internal or public to make it available outside its immediate circuit." />
737 </cfif>
738 <cfelse>
739 <cfthrow type="fusebox.undefinedCircuit"
740 message="undefined Circuit"
741 detail="You specified a Circuit of #arguments.circuit# which is not defined." />
742 </cfif>
743 </cfif>
744 <!--- FB5: development-circuit-load only reloads the requested circuit --->
745 <cfif this.mode is "development-circuit-load">
746 <!--- FB5: ensure we only reload each circuit once per request --->
747 <cfif not structKeyExists(request.__fusebox.CircuitsLoaded,arguments.circuit)>
748 <cfset request.__fusebox.CircuitsLoaded[arguments.circuit] = true />
749 <cfset this.circuits[arguments.circuit].reload(arguments.writer.getMyFusebox()) />
750 </cfif>
751 </cfif>
752
753 <cfset c = arguments.writer.setCircuit(arguments.circuit) />
754 <cfset this.circuits[arguments.circuit]
755 .compile(arguments.writer,arguments.fuseaction,arguments.topLevel) />
756 <cfset arguments.writer.setCircuit(c) />
757
758 </cffunction>
759
760 <cffunction name="handleFuseboxException" returntype="boolean" access="public" output="true"
761 hint="I attempt to handle a Fusebox exception by looking for a handler file in the errortemplates/ directory. I return true if I handle the exception, else I return false.">
762 <cfargument name="cfcatch" type="any" required="true"
763 hint="I am the original cfcatch structure from the exception that fusebox5.cfm caught." />
764 <cfargument name="attributes" type="struct" required="true"
765 hint="I am the attributes 'scope'." />
766 <cfargument name="myFusebox" type="any" required="true"
767 hint="I am the myFusebox object." />
768 <cfargument name="appKey" type="string" required="true"
769 hint="I am the application key object." />
770
771 <cfset var __handled = false />
772 <cfset var __type = arguments.cfcatch.type />
773 <cfset var __ext = "." & this.scriptFileDelimiter />
774 <cfset var __errorFile = this.errortemplatesPath & __type & __ext />
775 <cfset var __handlerExists = fileExists(expandFuseboxPath(__errorFile)) />
776 <cfset var FUSEBOX_APPLICATION_KEY = arguments.appKey />
777
778 <cfloop condition="not __handlerExists and len(__type) gt 0">
779 <cfset __type = listDeleteAt(__type,listLen(__type,"."),".") />
780 <cfset __errorFile = this.errortemplatesPath & __type & __ext />
781 <cfset __handlerExists = fileExists(expandFuseboxPath(__errorFile)) />
782 </cfloop>
783 <cfif __handlerExists>
784 <cfif left(__errorFile,1) is "/">
785 <cfinclude template="#__errorFile#" />
786 <cfelse>
787 <cfinclude template="#getCoreToAppRootPath()##__errorFile#" />
788 </cfif>
789 <cfset __handled = true />
790 </cfif>
791
792 <cfreturn __handled />
793
794 </cffunction>
795
796 <cffunction name="getFuseactionFactory" returntype="any" access="public" output="false"
797 hint="I return the factory object that makes fuseaction objects for the framework.">
798
799 <cfreturn variables.factory />
800
801 </cffunction>
802
803 <cffunction name="getClassDefinition" returntype="struct" access="public" output="false"
804 hint="I return the class declaration for a given class. I throw an exception if the class has no declaration.">
805 <cfargument name="className" type="string" required="true"
806 hint="I am the name of the class whose declaration should be returned." />
807
808 <cfreturn this.classes[arguments.className] />
809
810 </cffunction>
811
812 <cffunction name="getLexiconDefinition" returntype="any" access="public" output="false"
813 hint="I return the lexicon definition for a given namespace. I return either the internal Fusebox lexicon or a declared (Fusebox 4.1 style) lexicon.">
814 <cfargument name="namespace" type="any" required="false"
815 hint="I am the namespace of the lexicon whose definition should be returned. I am required but it's faster to specify that I am not required." />
816
817 <cfif arguments.namespace is variables.fuseboxLexicon.namespace>
818 <cfreturn variables.fuseboxLexicon />
819 <cfelse>
820 <cfreturn variables.fb41Lexicons[arguments.namespace] />
821 </cfif>
822
823 </cffunction>
824
825 <cffunction name="getVersion" returntype="string" access="public" output="false"
826 hint="I return the version of this Fusebox 5 object. This is the preferred way to obtain the version in Fusebox 5.">
827
828 <cfreturn variables.fuseboxVersion />
829
830 </cffunction>
831
832 <cffunction name="getAlias" returntype="any" access="public" output="false"
833 hint="I return the fake circuit alias for the application.">
834
835 <cfreturn "$fusebox" />
836
837 </cffunction>
838
839 <cffunction name="getApplication" returntype="any" access="public" output="false"
840 hint="I return the fusebox application object.">
841
842 <cfreturn this />
843
844 </cffunction>
845
846 <cffunction name="getCustomAttributes" returntype="struct" access="public" output="false"
847 hint="I return any custom attributes for the specified namespace prefix.">
848 <cfargument name="ns" type="string" required="true"
849 hint="I am the namespace for which to return custom attributes." />
850
851 <cfif structKeyExists(variables.customAttributes,arguments.ns)>
852 <!--- we structCopy() this so folks can't poke values back into the metadata! --->
853 <cfreturn structCopy(variables.customAttributes[arguments.ns]) />
854 <cfelse>
855 <cfreturn structNew() />
856 </cfif>
857
858 </cffunction>
859
860 <cffunction name="getFuseboxFileExtension" returntype="string" access="public" output="false"
861 hint="I return the fusebox.xml file extension: either xml or cfm.">
862
863 <cfreturn variables.fuseboxFileExtension />
864
865 </cffunction>
866
867 <cffunction name="deleteParsedFiles" returntype="void" access="private" output="false"
868 hint="I delete all the script files in the parsed/ directory.">
869
870 <cfset var fileQuery = 0 />
871 <cfset var parseDir = expandFuseboxPath(this.parsePath) />
872
873 <cftry>
874 <cfdirectory action="list" directory="#parseDir#"
875 filter="*.#this.scriptFileDelimiter#" name="fileQuery" />
876 <cfloop query="fileQuery">
877 <cffile action="delete" file="#parseDir##fileQuery.name#" />
878 </cfloop>
879 <cfcatch />
880 </cftry>
881
882 </cffunction>
883
884 <cffunction name="loadCircuits" returntype="void" access="private" output="false"
885 hint="I (re)load all the circuits in an application.">
886 <cfargument name="fbCode" type="any" required="true"
887 hint="I am the parsed XML representation of the fusebox.xml file." />
888 <cfargument name="myFusebox" type="myFusebox" required="true"
889 hint="I am the myFusebox data structure." />
890
891 <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/circuits/circuit") />
892 <cfset var i = 0 />
893 <cfset var n = arrayLen(children) />
894 <cfset var previousCircuits = this.circuits />
895 <cfset var alias = "" />
896 <cfset var parent = "" />
897 <cfset var relative = true />
898 <cfset var nAttrs = 0 />
899
900 <cfset this.circuits = structNew() />
901
902 <!--- pass 1: build the circuits --->
903 <cfloop from="1" to="#n#" index="i">
904 <cfif not structKeyExists(children[i].xmlAttributes,"alias")>
905 <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
906 message="Required attribute is missing"
907 detail="The attribute 'alias' is required, for a 'circuit' declaration in fusebox.xml." />
908 </cfif>
909 <cfif not structKeyExists(children[i].xmlAttributes,"path")>
910 <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
911 message="Required attribute is missing"
912 detail="The attribute 'path' is required, for the declaration of circuit '#children[i].xmlAttributes.alias#' in fusebox.xml." />
913 </cfif>
914 <cfif structKeyExists(children[i].xmlAttributes,"parent")>
915 <cfset parent = children[i].xmlAttributes.parent />
916 <cfset nAttrs = 3 />
917 <cfelse>
918 <cfset parent = "" />
919 <cfset nAttrs = 2 />
920 </cfif>
921 <cfif structKeyExists(children[i].xmlAttributes,"relative")>
922 <cfif listFind("true,false,yes,no",children[i].xmlAttributes.relative) eq 0>
923 <cfthrow type="fusebox.badGrammar.invalidAttributeValue"
924 message="Attribute has invalid value"
925 detail="The attribute 'relative' must either be ""true"" or ""false"", for the declaration of circuit '#children[i].xmlAttributes.alias#' in fusebox.xml." />
926 </cfif>
927 <cfset relative = children[i].xmlAttributes.relative />
928 <cfset nAttrs = nAttrs + 1 />
929 <cfelse>
930 <cfset relative = true />
931 </cfif>
932 <cfif this.strictMode and nAttrs neq structCount(children[i].xmlAttributes)>
933 <cfthrow type="fusebox.badGrammar.unexpectedAttributes"
934 message="Unexpected attributes"
935 detail="Attributes other than 'alias', 'path' and 'parent' were found for the declaration of circuit '#children[i].xmlAttributes.alias#' in fusebox.xml." />
936 </cfif>
937 <cfset alias = children[i].xmlAttributes.alias />
938 <!--- record each circuit load per request - optimization for development-circuit-load mode --->
939 <cfset request.__fusebox.CircuitsLoaded[alias] = true />
940 <cfif structKeyExists(previousCircuits,alias) and
941 children[i].xmlAttributes.path is previousCircuits[alias].getOriginalPath() and
942 parent is previousCircuits[alias].parent and
943 relative eq previousCircuits[alias].getOriginalPathIsRelative()>
944 <!--- old circuit, we can just reload it --->
945 <cfset this.circuits[alias] = previousCircuits[alias].reload(arguments.myFusebox) />
946 <cfelse>
947 <!--- new circuit, we must create it from scratch --->
948 <cfset this.circuits[alias] =
949 createObject("component","fuseboxCircuit")
950 .init(this,alias,children[i].xmlAttributes.path,parent,arguments.myFusebox,relative) />
951 </cfif>
952 </cfloop>
953
954 <!--- pass 2: build the circuit trace for each circuit --->
955 <cfloop collection="#this.circuits#" item="i">
956 <cfset this.circuits[i].buildCircuitTrace() />
957 </cfloop>
958
959 </cffunction>
960
961 <cffunction name="loadLexicons" returntype="void" access="private" output="false"
962 hint="I load any lexicon declarations (both the Fusebox 4.1 style lexicon declarations and the Fusebox 5 style namespace declarations).">
963 <cfargument name="fbCode" type="any" required="true"
964 hint="I am the parsed XML representation of the fusebox.xml file." />
965
966 <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/lexicons/lexicon") />
967 <cfset var i = 0 />
968 <cfset var n = arrayLen(children) />
969 <cfset var aLex = "" />
970 <cfset var attributes = arguments.fbCode.xmlRoot.xmlAttributes />
971 <cfset var attr = "" />
972 <cfset var ns = "" />
973
974 <cfif n gt 0 and this.strictMode>
975 <cfthrow type="fusebox.badGrammar.deprecated"
976 message="Deprecated feature"
977 detail="Using the 'lexicon' declaration in fusebox.xml was deprecated in Fusebox 5." />
978 </cfif>
979
980 <!--- load the legacy FB41 lexicons from the XML --->
981 <cfset variables.fb41Lexicons = structNew() />
982
983 <cfloop from="1" to="#n#" index="i">
984 <cfset aLex = structNew() />
985 <cfset aLex.namespace = children[i].xmlAttributes.namespace />
986 <cfset aLex.path = normalizePartialPath(children[i].xmlAttributes.path) />
987 <!--- FB41 lexicons are deprecated so we don't support absolute / mapped paths: --->
988 <cfset aLex.path = getCoreToAppRootPath() & this.lexiconPath & aLex.path />
989 <cfset variables.fb41Lexicons[children[i].xmlAttributes.namespace] = aLex />
990 </cfloop>
991
992 <!--- now load the new FB5 implicit lexicons from the <fusebox> tag --->
993 <cfset variables.lexicons = structNew() />
994
995 <!--- pass 1: pull out any namespace declarations --->
996 <cfloop collection="#attributes#" item="attr">
997 <cfif len(attr) gt 6 and left(attr,6) is "xmlns:">
998 <!--- found a namespace declaration, pull it out: --->
999 <cfset aLex = structNew() />
1000 <cfset aLex.namespace = listLast(attr,":") />
1001 <cfif aLex.namespace is variables.fuseboxLexicon.namespace>
1002 <cfthrow type="fusebox.badGrammar.reservedName"
1003 message="Attempt to use reserved namespace"
1004 detail="You have attempted to declare a namespace '#aLex.namespace#' (in fusebox.xml) which is reserved by the Fusebox framework." />
1005 </cfif>
1006 <cfset attributes[attr] = normalizePartialPath(attributes[attr]) />
1007 <cfif left(attributes[attr],1) is "/">
1008 <!--- assume mapped / root-relative path --->
1009 <cfset aLex.path = attributes[attr] />
1010 <cfelseif left(this.lexiconPath,1) is "/">
1011 <!--- assume mapped / root-relative path --->
1012 <cfset aLex.path = this.lexiconPath & attributes[attr] />
1013 <cfelse>
1014 <!--- relative paths --->
1015 <cfset aLex.path = getCoreToAppRootPath() &
1016 this.lexiconPath & attributes[attr] />
1017 </cfif>
1018 <cfset variables.lexicons[aLex.namespace] = aLex />
1019 <cfset variables.customAttributes[aLex.namespace] = structNew() />
1020 </cfif>
1021 </cfloop>
1022
1023 <!--- pass 2: pull out any custom attributes --->
1024 <cfloop collection="#attributes#" item="attr">
1025 <cfif listLen(attr,":") eq 2>
1026 <!--- looks like a custom attribute: --->
1027 <cfset ns = listFirst(attr,":") />
1028 <cfif ns is "xmlns">
1029 <!--- special case - need to ignore xmlns:foo="bar" --->
1030 <cfelseif structKeyExists(variables.customAttributes,ns)>
1031 <cfset variables.customAttributes[ns][listLast(attr,":")] = attributes[attr] />
1032 <cfelse>
1033 <cfthrow type="fusebox.badGrammar.undeclaredNamespace"
1034 message="Undeclared lexicon namespace"
1035 detail="The lexicon prefix '#ns#' was found on a custom attribute in the <fusebox> tag but no such lexicon namespace has been declared." />
1036 </cfif>
1037 <cfelseif this.strictMode>
1038 <cfthrow type="fusebox.badGrammar.unexpectedAttributes"
1039 message="Unexpected attributes"
1040 detail="Unexpected attributes were found in the 'fusebox' tag in fusebox.xml." />
1041 </cfif>
1042 </cfloop>
1043
1044 </cffunction>
1045
1046 <cffunction name="loadClasses" returntype="void" access="private" output="false"
1047 hint="I load any class declarations, including custom attributes (based on Fusebox 5 namespace declarations).">
1048 <cfargument name="fbCode" type="any" required="true"
1049 hint="I am the parsed XML representation of the fusebox.xml file." />
1050
1051 <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/classes/class") />
1052 <cfset var i = 0 />
1053 <cfset var n = arrayLen(children) />
1054 <cfset var attribs = 0 />
1055 <cfset var attr = "" />
1056 <cfset var ns = "" />
1057 <cfset var customAttribs = 0 />
1058 <cfset var constructor = "" />
1059 <cfset var type = "" />
1060 <cfset var nAttrs = 0 />
1061
1062 <cfset this.classes = structNew() />
1063
1064 <cfloop from="1" to="#n#" index="i">
1065 <cfset attribs = children[i].xmlAttributes />
1066
1067 <cfif not structKeyExists(attribs,"alias")>
1068 <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
1069 message="Required attribute is missing"
1070 detail="The attribute 'alias' is required, for a 'class' declaration in fusebox.xml." />
1071 </cfif>
1072 <cfif not structKeyExists(attribs,"classpath")>
1073 <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
1074 message="Required attribute is missing"
1075 detail="The attribute 'classpath' is required, for a 'class' declaration in fusebox.xml." />
1076 </cfif>
1077 <cfif structKeyExists(attribs,"constructor")>
1078 <cfset constructor = attribs.constructor />
1079 <cfset nAttrs = 3 />
1080 <cfelse>
1081 <cfset constructor = "" />
1082 <cfset nAttrs = 2 />
1083 </cfif>
1084 <!--- FB5: allow sensible default for type --->
1085 <cfif structKeyExists(attribs,"type")>
1086 <cfset type = attribs.type />
1087 <cfset nAttrs = nAttrs + 1 />
1088 <cfelse>
1089 <cfset type = "component" />
1090 </cfif>
1091
1092 <!--- scan for custom attributes --->
1093 <cfset customAttribs = structNew() />
1094 <cfloop collection="#attribs#" item="attr">
1095 <cfif listLen(attr,":") eq 2>
1096 <cfset nAttrs = nAttrs + 1 />
1097 <!--- looks like a custom attribute: --->
1098 <cfset ns = listFirst(attr,":") />
1099 <cfif structKeyExists(variables.customAttributes,ns)>
1100 <cfset customAttribs[ns][listLast(attr,":")] = attribs[attr] />
1101 <cfelse>
1102 <cfthrow type="fusebox.badGrammar.undeclaredNamespace"
1103 message="Undeclared lexicon namespace"
1104 detail="The lexicon prefix '#ns#' was found on a custom attribute in the <class> tag but no such lexicon namespace has been declared." />
1105 </cfif>
1106 </cfif>
1107 </cfloop>
1108
1109 <cfif this.strictMode and structCount(attribs) neq nAttrs>
1110 <cfthrow type="fusebox.badGrammar.unexpectedAttributes"
1111 message="Unexpected attributes"
1112 detail="Unexpected attributes were found in the '#attribs.alias#' class declaration in fusebox.xml." />
1113 </cfif>
1114
1115 <cfset this.classes[attribs.alias] = createObject("component","fuseboxClassDefinition")
1116 .init(type,attribs.classpath,constructor,customAttribs) />
1117
1118 </cfloop>
1119
1120 </cffunction>
1121
1122 <cffunction name="loadPlugins" returntype="void" access="private" output="false"
1123 hint="I load any plugin declarations.">
1124 <cfargument name="fbCode" type="any" required="true"
1125 hint="I am the parsed XML representation of the fusebox.xml file." />
1126
1127 <!--- TODO: this xpath means that we can't spot bad phase declarations: --->
1128 <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/plugins/phase") />
1129 <cfset var i = 0 />
1130 <cfset var n = arrayLen(children) />
1131 <cfset var j = 0 />
1132 <cfset var nn = 0 />
1133 <cfset var phase = "" />
1134 <cfset var plugin = 0 />
1135
1136 <cfset this.plugins = structNew() />
1137 <cfset this.pluginphases = structNew() />
1138
1139 <cfloop from="1" to="#n#" index="i">
1140 <cfif not structKeyExists(children[i].xmlAttributes,"name")>
1141 <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
1142 message="Required attribute is missing"
1143 detail="The attribute 'name' is required, for a 'plugin' declaration in fusebox.xml." />
1144 </cfif>
1145 <cfset phase = children[i].xmlAttributes.name />
1146 <cfif this.strictMode and structCount(children[i].xmlAttributes) neq 1>
1147 <cfthrow type="fusebox.badGrammar.unexpectedAttributes"
1148 message="Unexpected attributes"
1149 detail="Unexpected attributes were found in the '#phase#' phase declaration in fusebox.xml." />
1150 </cfif>
1151 <cfset nn = arrayLen(children[i].xmlChildren) />
1152 <cfloop from="1" to="#nn#" index="j">
1153 <cfset plugin = createObject("component","fuseboxPlugin").init(phase,children[i].xmlChildren[j],this,variables.lexicons) />
1154 <cfset this.plugins[plugin.getName()][phase] = plugin />
1155 <cfif not structKeyExists(this.pluginphases,phase)>
1156 <cfset this.pluginphases[phase] = arrayNew(1) />
1157 </cfif>
1158 <cfset arrayAppend(this.pluginphases[phase],plugin) />
1159 </cfloop>
1160 </cfloop>
1161
1162 </cffunction>
1163
1164 <cffunction name="loadParameters" returntype="void" access="private" output="false"
1165 hint="I load any parameter declarations (and ensure none of them can overwrite public methods in this object!).">
1166 <cfargument name="fbCode" type="any" required="true"
1167 hint="I am the parsed XML representation of the fusebox.xml file." />
1168
1169 <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/parameters/parameter") />
1170 <cfset var i = 0 />
1171 <cfset var n = arrayLen(children) />
1172 <cfset var p = "" />
1173
1174 <cfloop from="1" to="#n#" index="i">
1175 <cfif not structKeyExists(children[i].xmlAttributes,"name")>
1176 <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
1177 message="Required attribute is missing"
1178 detail="The attribute 'name' is required, for a 'parameter' declaration in fusebox.xml." />
1179 </cfif>
1180 <cfset p = children[i].xmlAttributes.name />
1181 <cfif not structKeyExists(children[i].xmlAttributes,"value")>
1182 <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
1183 message="Required attribute is missing"
1184 detail="The attribute 'value' is required, for the '#p#' parameter declaration in fusebox.xml." />
1185 </cfif>
1186 <cfif this.strictMode and structCount(children[i].xmlAttributes) neq 2>
1187 <cfthrow type="fusebox.badGrammar.unexpectedAttributes"
1188 message="Unexpected attributes"
1189 detail="Unexpected attributes were found in the '#p#' parameter declaration in fusebox.xml." />
1190 </cfif>
1191 <cfif structKeyExists(this,p) and isCustomFunction(this[p])>
1192 <cfthrow type="fusebox.badGrammar.reservedName"
1193 message="Attempt to use reserved parameter name"
1194 detail="You have attempted to set a parameter called '#p#' which is reserved by the Fusebox framework." />
1195 <cfelse>
1196 <cfset this[p] = children[i].xmlAttributes.value />
1197 </cfif>
1198 </cfloop>
1199
1200 </cffunction>
1201
1202 <cffunction name="getDefaultMyself" returntype="string" access="public" output="false"
1203 hint="I return the default value of 'myself' for a given value of 'self'.">
1204 <cfargument name="self" type="string" required="true"
1205 hint="I am the value of 'self' to use." />
1206
1207 <cfreturn arguments.self & this.queryStringStart & this.fuseactionVariable & this.queryStringEqual />
1208
1209 </cffunction>
1210
1211 <cffunction name="loadGlobalProcess" returntype="void" access="private" output="false"
1212 hint="I load the globalfuseaction for the specified processing phase.">
1213 <cfargument name="fbCode" type="any" required="true"
1214 hint="I am the parsed XML representation of the fusebox.xml file." />
1215 <cfargument name="processPhase" type="string" required="true"
1216 hint="I am the name of the processing phase to load (appinit, preprocess or postprocess)." />
1217
1218 <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/globalfuseactions/#arguments.processPhase#") />
1219 <cfset var n = arrayLen(children) />
1220
1221 <cfif n eq 0>
1222 <cfset variables.hasProcess[arguments.processPhase] = false />
1223 <cfelseif n eq 1>
1224 <cfset variables.hasProcess[arguments.processPhase] = true />
1225 <cfset variables.process[arguments.processPhase] =
1226 createObject("component","fuseboxAction")
1227 .init(this,
1228 "$globalfuseaction/#arguments.processPhase#",
1229 "internal",
1230 children[1].xmlChildren,true) />
1231 <cfelse>
1232 <cfthrow type="fusebox.badGrammar.nonUniqueDeclaration"
1233 message="Declaration was not unique"
1234 detail="More than one <#arguments.process#> declaration was found in the <globalfuseactions> section in fusebox.xml." />
1235 </cfif>
1236
1237 </cffunction>
1238
1239 <cffunction name="loadGlobalPreAndPostProcess" returntype="void" access="private" output="false"
1240 hint="I load any globalfuseaction declarations.">
1241 <cfargument name="fbCode" type="any" required="true"
1242 hint="I am the parsed XML representation of the fusebox.xml file." />
1243
1244 <cfset var children = xmlSearch(arguments.fbCode,"/fusebox/globalfuseactions/*") />
1245 <cfset var i = 0 />
1246 <cfset var n = arrayLen(children) />
1247
1248 <cfloop index="i" from="1" to="#n#">
1249 <cfif listFind("appinit,preprocess,postprocess",children[i].xmlName) eq 0>
1250 <cfthrow type="fusebox.badGrammar.illegalDeclaration"
1251 message="Illegal declaration"
1252 detail="The tag '#children[i].xmlName#' was found where 'appinit', 'preprocess' or 'postprocess' was expected in the <globalfuseactions> section in fusebox.xml." />
1253 </cfif>
1254 </cfloop>
1255
1256 <cfset variables.hasProcess = structNew() />
1257 <cfset variables.process = structNew() />
1258 <cfset loadGlobalProcess(arguments.fbCode,"appinit") />
1259 <cfset loadGlobalProcess(arguments.fbCode,"preprocess") />
1260 <cfset loadGlobalProcess(arguments.fbCode,"postprocess") />
1261
1262 </cffunction>
1263
1264 <cffunction name="relativePath" returntype="string" access="public" output="false"
1265 hint="I compute the relative path from one file system location to another.">
1266 <cfargument name="from" type="string" required="true"
1267 hint="I am the full pathname from which the relative path should be computed." />
1268 <cfargument name="to" type="string" required="true"
1269 hint="I am the full pathname to which the relative path should be computed." />
1270
1271 <cfset var relative = "" />
1272 <cfset var fromFirst = listFirst(arguments.from,"/") />
1273 <cfset var fromRest = arguments.from />
1274 <cfset var toFirst = listFirst(arguments.to,"/") />
1275 <cfset var toRest = arguments.to />
1276 <cfset var needSlash = false />
1277
1278 <!--- trap special case first --->
1279 <cfif arguments.from is arguments.to>
1280 <cfreturn "" />
1281 </cfif>
1282
1283 <!--- walk down the common part of the paths --->
1284 <cfloop condition="fromFirst is toFirst">
1285 <cfset needSlash = true />
1286 <cfset fromRest = listRest(fromRest,"/") />
1287 <cfset toRest = listRest(toRest,"/") />
1288 <cfset fromFirst = listFirst(fromRest,"/") />
1289 <cfset toFirst = listFirst(toRest,"/") />
1290 </cfloop>
1291 <!--- at this point the paths differ --->
1292 <cfif not needSlash>
1293 <!--- the paths differed from the top so we need to strip the leading / --->
1294 <cfset toRest = right(toRest,len(toRest)-1) />
1295 </cfif>
1296 <cfset relative = repeatString("../",listLen(fromRest,"/")) & toRest />
1297 <!---
1298 ensure the trailing / is present - strictly speaking this is a bug fix for Railo
1299 but it's probably a good practice anyway
1300 --->
1301 <cfif right(relative,1) is not "/">
1302 <cfset relative = relative & "/" />
1303 </cfif>
1304
1305 <cfreturn relative />
1306
1307 </cffunction>
1308
1309 <cffunction name="getCanonicalPath" returntype="string" access="public" output="false"
1310 hint="I return a canonical file path (with all /../ sections resolved).">
1311 <cfargument name="path" type="string" required="true"
1312 hint="I am the path to resolve." />
1313
1314 <cfset var resolvedPath = replace(arguments.path,"\","/","all") />
1315 <cfset var leadingSlash = left(resolvedPath,1) is "/" />
1316 <!--- UNC fix from Phil Muhm - ticket 239 --->
1317 <cfset var leadingDoubleSlash = left(resolvedPath,2) is "//" />
1318 <cfset var trailingSlash = right(resolvedPath,1) is "/" />
1319 <cfset var segment = ""/>
1320 <cfset var j = 1 />
1321 <cfset var n = 0 />
1322 <cfset var pathParts = arrayNew(1) />
1323 <cfset var delimiter = "" />
1324
1325 <!--- remove pairs of directory/../ --->
1326 <cfloop list="#resolvedPath#" delimiters="/" index="segment">
1327 <cfif segment is ".">
1328 <!--- just ignore this --->
1329 <cfelseif segment is "..">
1330 <cfif j gt 1 and pathParts[j-1] is not "..">
1331 <cfset j = j - 1 />
1332 <cfelse>
1333 <cfset pathParts[j] = segment />
1334 <cfset j = j + 1 />
1335 </cfif>
1336 <cfelse>
1337 <cfset pathParts[j] = segment />
1338 <cfset j = j + 1 />
1339 </cfif>
1340 </cfloop>
1341
1342 <!--- rebuild the path --->
1343 <cfif leadingSlash>
1344 <cfset delimiter = "/" />
1345 </cfif>
1346 <cfset resolvedPath = "" />
1347 <cfset n = j - 1 />
1348 <cfloop from="1" to="#n#" index="j">
1349 <cfset resolvedPath = resolvedPath & delimiter & pathParts[j] />
1350 <cfset delimiter = "/" />
1351 </cfloop>
1352 <cfif trailingSlash>
1353 <cfset resolvedPath = resolvedPath & "/" />
1354 </cfif>
1355 <!--- UNC fix from Phil Muhm - ticket 239 --->
1356 <cfif leadingDoubleSlash>
1357 <cfset resolvedPath = "/" & resolvedPath />
1358 </cfif>
1359
1360 <cfreturn resolvedPath />
1361
1362 </cffunction>
1363
1364 <cffunction name="normalizePartialPath" returntype="string" access="public" output="false"
1365 hint="I return a normalized file path (that has / instead of \ and ends with /).">
1366 <cfargument name="partialPath" type="string" required="true"
1367 hint="I am the path to normalize." />
1368
1369 <cfset var unixPath = replace(arguments.partialPath,"\","/","all") />
1370
1371 <cfif right(unixPath,1) is not "/">
1372 <cfset unixPath = unixPath & "/" />
1373 </cfif>
1374
1375 <cfreturn unixPath />
1376
1377 </cffunction>
1378
1379 <cffunction name="locateCfc" returntype="string" access="public" output="false"
1380 hint="I deduce the dot-separated path to a CFC given its file system path (and a few heuristics).">
1381 <cfargument name="filename" type="string" required="true" />
1382
1383 <cfset var cfcPath = getCanonicalPath(filename) />
1384 <cfset var webRoot = getCanonicalPath(expandPath("/")) />
1385 <cfset var lenWebRoot = len(webRoot) />
1386 <cfset var lenAppRoot = len(getApplicationRoot()) />
1387 <cfset var lenCfcPath = len(cfcPath) />
1388 <cfset var cfcDotPath = "" />
1389 <cfset var firstSlash = 0 />
1390
1391 <cfif lenCfcPath gt lenWebRoot and
1392 left(cfcPath,lenWebRoot) is webRoot>
1393 <!--- looks like it is under the webroot --->
1394 <cfset cfcDotPath = replace(mid(cfcPath,lenWebRoot+1,lenCfcPath-lenWebRoot-4),"/",".","all") />
1395 <cfelse>
1396 <!--- must be mapped --->
1397 <cfif lenCfcPath gt lenAppRoot and
1398 left(cfcPath,lenAppRoot) is getApplicationRoot()>
1399 <!--- looks like it is under the approot - assume approot is a mapping --->
1400 <cfset cfcDotPath = listLast(getApplicationRoot(),"/") & "." &
1401 replace(mid(cfcPath,lenAppRoot+1,lenCfcPath-lenAppRoot-4),"/",".","all") />
1402 <cfelse>
1403 <!--- dot-convert entire path and attempt to create it, dropping leading directories until we succeed --->
1404 <!--- remove stuff up to leading / and also the .cfc at the end --->
1405 <!--- note: this is *expensive* --->
1406 <cfset firstSlash = find("/",cfcPath) />
1407 <cfset cfcDotPath = replace(mid(cfcPath,firstSlash+1,lenCfcPath-firstSlash-4),"/",".","all") />
1408 <cfloop condition="listLen(cfcDotPath) gt 0">
1409 <cftry>
1410 <cfset createObject("component",cfcDotPath) />
1411 <!--- succeeded - return it --->
1412 <cfbreak />
1413 <cfcatch type="any">
1414 <!--- failed - keep searching --->
1415 </cfcatch>
1416 </cftry>
1417 <cfset cfcDotPath = listRest(cfcDotPath,".") />
1418 </cfloop>
1419 </cfif>
1420 </cfif>
1421
1422 <cfreturn cfcDotPath />
1423
1424 </cffunction>
1425
1426 <cffunction name="fileModificationDate" returntype="date" access="public" output="false"
1427 hint="I return the last modified date/time for a file.">
1428 <cfargument name="filePath" type="string" required="false"
1429 hint="I am the full filesystem path to return the modification date." />
1430
1431 <!--- Java timestamp solution provided by Daniel Schmid --->
1432 <cfset var jFile = 0 />
1433 <cfset var dtLastModified = 0 />
1434
1435 <!--- watch out for hosts that have createObject("java") disabled - this is the buggy version from FB5 so it is not perfect --->
1436 <cftry>
1437 <cfset jFile = createObject("java","java.io.File").init(arguments.filePath) />
1438 <cfset dtLastModified = parseDateTime(createObject("java","java.util.Date").init(jFile.lastModified())) />
1439 <cfcatch type="any">
1440 <cfdirectory action="list" directory="#getDirectoryFromPath(arguments.filePath)#" filter="#getFileFromPath(arguments.filePath)#" name="jFile" />
1441 <cfif jFile.recordCount eq 1>
1442 <cfset dtLastModified = parseDateTime(jFile.dateLastModified) />
1443 <cfelse>
1444 <!--- have to assume it was always modified --->
1445 <cfset dtLastModified = now() />
1446 </cfif>
1447 </cfcatch>
1448 </cftry>
1449
1450 <cfreturn dtLastModified />
1451
1452 </cffunction>
1453
1454</cfcomponent>