root / framework / trunk / corefiles / myFusebox.cfc

Revision 763, 20.2 kB (checked in by a.haskell@…, 4 days ago)

Ticket #350. Additions to allow return values the be passed through.

  • Property svn:keywords set to LastChangedRevision
Line 
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 hint="I provide the per-request myFusebox data structure and some convenience methods.">
17        <cfscript>
18        this.version.runtime     = "5.5.1";
19        //this.version.runtime     = "5.5.0.#REReplace('$LastChangedRevision:683 $','[^0-9]','','all')#";
20         
21        this.version.loader      = "unknown";
22        this.version.transformer = "unknown";
23        this.version.parser      = "unknown";
24         
25        this.thisCircuit = "";
26        this.thisFuseaction =  "";
27        this.thisPlugin = "";
28        this.thisPhase = "";
29        this.plugins = structNew();
30        this.parameters = structNew();
31       
32        // the basic default is development-full-load mode:
33        this.parameters.load = true;
34        this.parameters.parse = true;
35        this.parameters.execute = true;
36        // FB5: new execution parameters:
37        this.parameters.clean = false;          // don't delete parsed files by default
38        this.parameters.parseall = false;       // don't compile all fuseactions by default
39         
40        this.parameters.userProvidedLoadParameter = false;
41        this.parameters.userProvidedCleanParameter = false;
42        this.parameters.userProvidedParseParameter = false;
43        this.parameters.userProvidedParseAllParameter = false;
44        this.parameters.userProvidedExecuteParameter = false;
45       
46        // stack frame for do/include parameters:
47        this.stack = structNew();
48       
49        // FB55: ability to turn debug output off per-request:
50        this.showDebug = true;
51        </cfscript>
52       
53        <cffunction name="init" returntype="myFusebox" access="public" output="false"
54                                hint="I am the constructor.">
55                <cfargument name="appKey" type="string" required="true"
56                                        hint="I am FUSEBOX_APPLICATION_KEY." />
57                <cfargument name="attributes" type="struct" required="true"
58                                        hint="I am the attributes (URL and form variables) structure." />
59                <cfargument name="topLevelVariablesScope" type="any" required="true"
60                                        hint="I am the top-level variables scope." />
61               
62                <cfset var theFusebox = structNew() />
63                <cfset var urlParam = "" />
64                <cfset var urlLastArg = "" />
65                <cfset var urlIsArg = true />
66                <cfset var pathInfo = CGI.PATH_INFO />
67               
68                <cfset variables.variablesScope = arguments.topLevelVariablesScope />
69               
70                <cfset variables.created = getTickCount() />
71                <cfset variables.log = arrayNew(1) />
72                <cfset variables.occurrence = structNew() />
73
74                <cfset variables.appKey = arguments.appKey />
75                <cfset variables.attributes = arguments.attributes />
76               
77                <!--- FB5: indicates whether application was started on this request --->
78                <cfset this.applicationStart = false />
79
80                <!--- we can't guarantee the fusebox exists in application scope yet... --->
81                <cfif structKeyExists(application,variables.appKey)>
82                        <cfset theFusebox = application[variables.appKey] />
83                </cfif>
84               
85                <!--- default myFusebox.parameters depending on "mode" of the application set in fusebox.xml --->
86                <cfif structKeyExists(theFusebox,"mode")>
87                        <cfswitch expression="#theFusebox.mode#">
88                        <!--- FB41 backward compatibility - now deprecated --->
89                        <cfcase value="development">
90                                <cfif structKeyExists(theFusebox,"strictMode") and theFusebox.strictMode>
91                                        <!--- since we don't load fusebox.xml if we throw an exception, we must fixup the value for the next run --->
92                                        <cfset theFusebox.mode = "development-full-load" />
93                                        <cfthrow type="fusebox.badGrammar.deprecated"
94                                                        message="Deprecated feature"
95                                                        detail="'development' is a deprecated execution mode - use 'development-full-load' instead." />
96                                </cfif>
97                                <cfset this.parameters.load = true />
98                                <cfset this.parameters.parse = true />
99                                <cfset this.parameters.execute = true />
100                        </cfcase>
101                        <!--- FB5: replacement for old development mode --->
102                        <cfcase value="development-full-load">
103                                <cfset this.parameters.load = true />
104                                <cfset this.parameters.parse = true />
105                                <cfset this.parameters.execute = true />
106                        </cfcase>
107                        <!--- FB5: new option - does not load fusebox.xml and therefore does not (re-)load fuseboxApplication object --->
108                        <cfcase value="development-circuit-load">
109                                <cfset this.parameters.load = false />
110                                <cfset this.parameters.parse = true />
111                                <cfset this.parameters.execute = true />
112                        </cfcase>
113                        <cfcase value="production">
114                                <cfset this.parameters.load = false />
115                                <cfset this.parameters.parse = false />
116                                <cfset this.parameters.execute = true />
117                        </cfcase>
118                        <cfdefaultcase>
119                                <!--- since we don't load fusebox.xml if we throw an exception, we must fixup the value for the next run --->
120                                <cfset theFusebox.mode = "development-full-load" />
121                                <cfthrow type="fusebox.badGrammar.invalidParameterValue"
122                                                message="Parameter has invalid value"
123                                                detail="The parameter 'mode' must be one of 'development-full-load', 'development-circuit-load' or 'production' in the fusebox.xml file." />
124                        </cfdefaultcase>
125                        </cfswitch>
126                </cfif>
127               
128                <!--- handle SES URLs if appropriate --->
129                <cfif structKeyExists(theFusebox,"queryStringStart") and theFusebox.queryStringStart is not "?">
130                        <!--- looks like SES URL generation is enabled, process CGI.PATH_INFO (we add &= to catch improperly formed URLs) --->
131                        <!--- ticket 313 - canonicalize pathInfo for IIS 5 --->
132                        <cfif len(pathInfo) gt len(CGI.SCRIPT_NAME) and left(pathInfo,len(CGI.SCRIPT_NAME)) is CGI.SCRIPT_NAME>
133                                <cfset pathInfo = right(pathInfo,len(pathInfo)-len(CGI.SCRIPT_NAME)) />
134                        </cfif>
135                        <cfloop list="#pathInfo#" index="urlParam"
136                                        delimiters="#theFusebox.queryStringStart##theFusebox.queryStringSeparator##theFusebox.queryStringEqual#&=">
137                           <cfif urlIsArg>
138                              <cfset urlLastArg = urlParam />
139                           <cfelse>
140                              <cfset variables.attributes[urlLastArg] = urlParam />
141                           </cfif>
142                           <cfset urlIsArg = not urlIsArg />
143                        </cfloop>
144                </cfif>
145               
146                <!--- did the user pass in any special "fuseboxDOT" parameters for this request? --->
147                <!--- If so, process them --->
148                <!--- note: only if attributes.fusebox.password matches the application password --->
149                <cfif not structKeyExists(variables.attributes,"fusebox.password")>
150                        <cfset variables.attributes["fusebox.password"] = "" />
151                </cfif>
152                <cfif structKeyExists(theFusebox,"password") and
153                                theFusebox.password is variables.attributes['fusebox.password']>
154                        <!--- FB5: does a load and wipes the parsed files out --->
155                        <cfif structKeyExists(variables.attributes,'fusebox.loadclean') and isBoolean(variables.attributes['fusebox.loadclean'])>
156                                <cfset this.parameters.load = variables.attributes['fusebox.loadclean'] />
157                                <cfset this.parameters.clean = variables.attributes['fusebox.loadclean'] />
158                                <cfset this.parameters.userProvidedLoadParameter = true />
159                                <cfset this.parameters.userProvidedCleanParameter = true />
160                        </cfif>
161                        <cfif structKeyExists(variables.attributes,'fusebox.load') and isBoolean(variables.attributes['fusebox.load'])>
162                                <cfset this.parameters.load = variables.attributes['fusebox.load'] />
163                                <cfset this.parameters.userProvidedLoadParameter = true />
164                        </cfif>
165                        <cfif structKeyExists(variables.attributes,'fusebox.parseall') and isBoolean(variables.attributes['fusebox.parseall'])>
166                                <cfset this.parameters.parse = variables.attributes['fusebox.parseall'] />
167                                <cfset this.parameters.parseall = variables.attributes['fusebox.parseall'] />
168                                <cfif this.parameters.parseall>
169                                        <cfset this.parameters.load = true />
170                                </cfif>
171                                <cfset this.parameters.userProvidedParseParameter = true />
172                                <cfset this.parameters.userProvidedParseAllParameter = true />
173                        </cfif>
174                        <cfif structKeyExists(variables.attributes,'fusebox.parse') and isBoolean(variables.attributes['fusebox.parse'])>
175                                <cfset this.parameters.parse = variables.attributes['fusebox.parse'] />
176                                <cfset this.parameters.userProvidedParseParameter = true />
177                        </cfif>
178                        <cfif structKeyExists(variables.attributes,'fusebox.execute') and isBoolean(variables.attributes['fusebox.execute'])>
179                                <cfset this.parameters.execute = variables.attributes['fusebox.execute'] />
180                                <cfset this.parameters.userProvidedExecuteParameter = true />
181                        </cfif>
182                </cfif>
183               
184                <!---
185                        force a load if the runtime and core versions differ: this allows a new
186                        version to be dropped in and the framework will automatically reload!
187                        note: that we must *force* a load, by pretending this is user-provided!
188                --->
189                <cfif structKeyExists(theFusebox,"getVersion") and
190                                isCustomFunction(theFusebox.getVersion)>
191                        <cfif this.version.runtime is not theFusebox.getVersion()>
192                                <cfset this.parameters.userProvidedLoadParameter = true />
193                                <cfset this.parameters.load = true />
194                        </cfif>
195                <cfelse>
196                        <!--- hmm, doesn't look like the core is present (or it's not FB5 Alpha 2 or higher) --->
197                        <cfset this.parameters.userProvidedLoadParameter = true />
198                        <cfset this.parameters.load = true />
199                </cfif>
200
201                <!--- if the fusebox doesn't already exist we definitely want to reload --->
202                <cfif structKeyExists(theFusebox,"isFullyLoaded") and
203                                theFusebox.isFullyLoaded>
204                        <!--- if fully loaded, leave the load parameter alone --->
205                <cfelse>
206                        <cfset this.parameters.load = true />
207                </cfif>
208               
209                <cfreturn this />
210        </cffunction>
211       
212        <cffunction name="getApplication" returntype="any" access="public" output="false"
213                                hint="I am a convenience method to return the fuseboxApplication object without needing to know reference application scope or the FUSEBOX_APPLICATION_KEY variable.">
214       
215                <!---
216                        this is a bit of a hack since we're accessing application scope directly
217                        but it's probably cleaner than exposing a method to allow fuseboxApplication
218                        to inject itself back into myFusebox during compileRequest()...
219                --->
220                <cfreturn application[variables.appKey] />
221       
222        </cffunction>
223       
224        <cffunction name="getApplicationData" returntype="struct" access="public" output="false"
225                                hint="I am a convenience method to return a reference to the application data cache.">
226       
227                <cfreturn getApplication().getApplicationData() />
228       
229        </cffunction>
230       
231        <cffunction name="getCurrentCircuit" returntype="any" access="public" output="false"
232                                hint="I am a convenience method to return the current Fusebox circuit object.">
233       
234                <cfreturn getApplication().circuits[this.thisCircuit] />
235       
236        </cffunction>
237       
238        <cffunction name="getCurrentFuseaction" returntype="any" access="public" output="false"
239                                hint="I am a convenience method to return the current fuseboxAction (fuseaction) object.">
240       
241                <cfreturn getCurrentCircuit().fuseactions[this.thisFuseaction] />
242       
243        </cffunction>
244       
245        <cffunction name="getOriginalCircuit" returntype="any" access="public" output="false"
246                                hint="I am a convenience method to return the original Fusebox circuit object.">
247       
248                <cfreturn getApplication().circuits[this.originalCircuit] />
249       
250        </cffunction>
251       
252        <cffunction name="getOriginalFuseaction" returntype="any" access="public" output="false"
253                                hint="I am a convenience method to return the original fuseboxAction (fuseaction) object.">
254       
255                <cfreturn getOriginalCircuit().fuseactions[this.originalFuseaction] />
256       
257        </cffunction>
258       
259        <cffunction name="getSelf" returntype="string" access="public" output="false"
260                                hint="I return the 'self' string, e.g., index.cfm.">
261
262                <cfif not structKeyExists(variables,"self")>
263                        <cfset variables.self = getApplication().self />
264                </cfif>
265               
266                <cfreturn variables.self />
267
268        </cffunction>   
269       
270        <cffunction name="setSelf" returntype="void" access="public" output="false"
271                                hint="I override the default value of 'self' and I also reset the value of 'myself'.">
272                <cfargument name="self" type="string" required="true"
273                                        hint="I am the new value of 'self', e.g., /myapp/entry.cfm" />
274               
275                <cfset variables.self = arguments.self />
276                <!--- reset myself for consistency with self --->
277                <cfset variables.myself = getApplication().getDefaultMyself(variables.self) />
278               
279        </cffunction>
280
281        <cffunction name="getMyself" returntype="string" access="public" output="false"
282                                hint="I return the 'myself' string, e.g., index.cfm?fuseaction=.">
283
284                <cfif not structKeyExists(variables,"myself")>
285                        <cfset variables.myself = getApplication().myself />
286                </cfif>
287               
288                <cfreturn variables.myself />
289
290        </cffunction>   
291       
292        <cffunction name="setMyself" returntype="void" access="public" output="false"
293                                hint="I override the default value of 'myself'.">
294                <cfargument name="myself" type="string" required="true"
295                                        hint="I am the new value of 'myself'." />
296               
297                <cfset variables.myself = arguments.myself />
298               
299        </cffunction>
300
301        <cffunction name="do" returntype="any" access="public" output="true"
302                                hint="I compile and execute a specific fuseaction.">
303                <cfargument name="action" type="string" required="true"
304                                        hint="I am the full name of the requested fuseaction (circuit.fuseaction)." />
305                <cfargument name="contentVariable" type="string" default=""
306                                        hint="I indicate an attributes / event scope variable in which to store the output." />
307                <cfargument name="returnOutput" type="boolean" default="false"
308                                        hint="I indicate whether to display output (false - default) or return the output (true)." />
309                <cfargument name="append" type="boolean" default="false"
310                                        hint="I indicate whether to append output (false - default) to the content variable." />
311                <cfargument name="passThroughReturn" type="boolean" default="false"
312                                        hint="I indicate whether to allow for a return to be passed through from an action CFC." />
313
314                <cfset var c = this.thisCircuit />
315                <cfset var f = this.thisFuseaction />
316                <cfset var output = "" />
317                <cfif (arguments.returnOutput or arguments.contentVariable is not "") AND arguments.passThroughReturn>
318                        <cfthrow errorcode="Fusebox.Missmatch_Parameters" message="Paramters are in conflict." detail="Chances are you went and asked Fusebox to output the return as well as pass it back through. I think that's sort of silly and outright dangerous so I made this little error up for you.">
319                </cfif>
320                <cfset output = getApplication().do(
321                                        arguments.action,
322                                        this,
323                                        arguments.returnOutput or arguments.contentVariable is not "",
324                                        arguments.passThroughReturn) />
325       
326                <cfset this.thisFuseaction = f />
327                <cfset this.thisCircuit = c />
328                       
329                <cfif arguments.contentVariable is not "">
330                        <!--- ticket #290 - allow append on content variables --->
331                        <cfif structKeyExists(variables.variablesScope,arguments.contentVariable) and arguments.append>
332                                <cfset variables.variablesScope[arguments.contentVariable] = variables.variablesScope[arguments.contentVariable] & output />
333                        <cfelse>
334                                <cfset variables.variablesScope[arguments.contentVariable] = output />
335                        </cfif>
336                </cfif>
337               
338                <cfreturn output />
339               
340        </cffunction>
341       
342        <cffunction name="relocate" returntype="void" access="public" output="true"
343                                hint="I provide the same functionality as the relocate verb.">
344                <cfargument name="url" type="string" required="false" />
345                <cfargument name="xfa" type="string" required="false" />
346                <cfargument name="addtoken" type="boolean" default="false" />
347                <cfargument name="type" type="string" default="client" />
348
349                <cfset var theUrl = "" />
350               
351                <!--- url/xfa - exactly one is required --->
352                <cfif structKeyExists(arguments,"url")>
353                        <cfif structKeyExists(arguments,"xfa")>
354                                <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
355                                                message="Required attribute is missing"
356                                                detail="Either the attribute 'url' or 'xfa' is required, for a 'relocate' verb in fuseaction #this.thiscircuit#.#this.thisFuseaction#." />
357                        <cfelse>
358                                <cfset theUrl = arguments.url />
359                        </cfif>
360                <cfelseif structKeyExists(arguments,"xfa")>
361                        <cfset theUrl = getMyself() & variables.variablesScope.xfa[arguments.xfa] />
362                <cfelse>
363                        <cfthrow type="fusebox.badGrammar.requiredAttributeMissing"
364                                        message="Required attribute is missing"
365                                        detail="Either the attribute 'url' or 'xfa' is required, for a 'relocate' verb in fuseaction #this.thiscircuit#.#this.thisFuseaction#." />
366                </cfif>
367               
368                <!--- type - server|client|moved - we do not support javascript here --->
369                <cfif arguments.type is "server">
370
371                        <cfset getPageContext().forward(theUrl) />
372
373                <cfelseif arguments.type is "client">
374
375                        <cflocation url="#theUrl#" addtoken="#arguments.addtoken#" />
376
377                <cfelseif arguments.type is "moved">
378
379                        <cfheader statuscode="301" statustext="Moved Permanently" />
380                        <cfheader name="Location" value="#theUrl#" />
381                       
382                <cfelse>
383                        <cfthrow type="fusebox.badGrammar.invalidAttributeValue"
384                                        message="Attribute has invalid value"
385                                        detail="The attribute 'type' must either be ""server"", ""client"" or ""moved"", for a 'relocate' verb in fuseaction #this.thisCircuit#.#this.thisFuseaction#." />
386                </cfif>
387               
388                <cfabort />
389
390        </cffunction>
391       
392        <cffunction name="variables" returntype="any" access="public" output="false" hint="I return the top-level variables scope.">
393       
394                <cfreturn variables.variablesScope />
395       
396        </cffunction>
397       
398        <cffunction name="enterStackFrame" returntype="void" access="public" output="false"
399                                hint="I create a new stack frame (for scoped parameters to do/include).">
400               
401                <cfset var frame = structNew() />
402               
403                <cfset frame.__fuseboxStack = this.stack />
404                <cfset this.stack = frame />
405               
406        </cffunction>
407       
408        <cffunction name="leaveStackFrame" returntype="void" access="public" output="false"
409                                hint="I pop the last stack frame (for scoped parameters to do/include).">
410               
411                <cfset this.stack = this.stack.__fuseboxStack />
412               
413        </cffunction>
414       
415        <cffunction name="trace" returntype="void" access="public" output="false"
416                                hint="I add a line to the execution trace log.">
417                <cfargument name="type" hint="I am the type of trace (Fusebox, Compiler, Runtime are used by the framework)." />
418                <cfargument name="message" hint="I am the message to put in the execution trace." />
419               
420                <cfset addTrace(getTickCount() - variables.created,arguments.type,arguments.message) />
421               
422        </cffunction>
423
424        <cffunction name="addTrace" returntype="void" access="private" output="false"
425                                hint="I add a detailed line to the execution trace log.">
426                <cfargument name="time" hint="I am the time taken to get to this point in the request." />
427                <cfargument name="type" hint="I am the type of trace." />
428                <cfargument name="message" hint="I am the trace message." />
429                <cfargument name="occurrence" default="0" hint="I am a placeholder for part of the struct that is added to the log." />
430               
431                <cfif structKeyExists(variables.occurrence,arguments.message)>
432                        <cfset variables.occurrence[arguments.message] = 1 + variables.occurrence[arguments.message] />
433                <cfelse>
434                        <cfset variables.occurrence[arguments.message] = 1 />
435                </cfif>
436                <cfset arguments.occurrence = variables.occurrence[arguments.message] />
437                <cfset arrayAppend(variables.log,arguments) />
438               
439        </cffunction>
440       
441        <cffunction name="renderTrace" returntype="string" access="public" output="false" hint="I render the trace log as HTML.">
442               
443                <cfset var result = "" />
444                <cfset var i = 0 />
445               
446                <cfif this.showDebug>
447                        <cfsavecontent variable="result">
448                                <style type="text/css">
449                                        .fuseboxdebug {clear:both;padding-top:10px;}
450                                        .fuseboxdebug * {font-family:verdana,sans-serif;}
451                                        .fuseboxdebug h3 {margin:16px 0 16px 0;padding:0;border-bottom:1px solid #CCC;font-size:16px;}
452                                        .fuseboxdebug table th {font-size:11pt;text-align:left;}
453                                        .fuseboxdebug table tr.odd {background:#F9F9F9;}
454                                        .fuseboxdebug table tr.even {background:#FFF;}
455                                        .fuseboxdebug table td {border-bottom:1px solid #CCC;font-size:10pt;text-align:left;vertical-align:top;}
456                                        .fuseboxdebug table td.count {text-align:center;}
457                                </style>
458                                <div class="fuseboxdebug">
459                                        <h3>Fusebox debugging:</h3>
460                                        <table cellpadding="2" cellspacing="0" width="100%">
461                                                <tr>
462                                                        <th>Time</td>
463                                                        <th>Category</td>
464                                                        <th>Message</td>
465                                                        <th>Count</td>
466                                                </tr>
467                                                <cfloop index="i" from="1" to="#arrayLen(variables.log)#">
468                                                        <cfoutput>
469                                                                <cfif i mod 2>
470                                                                        <tr class="odd">
471                                                                <cfelse>
472                                                                        <tr class="even">
473                                                                </cfif>
474                                                                <td>#variables.log[i].time#ms</td>
475                                                                <td>#variables.log[i].type#</td>
476                                                                <td>#variables.log[i].message#</td>
477                                                                <td class="count">#variables.log[i].occurrence#</td>
478                                                        </tr></cfoutput>
479                                                </cfloop>
480                                        </table>
481                                </div>
482                        </cfsavecontent>
483                </cfif>
484               
485                <cfreturn result />
486               
487        </cffunction>
488</cfcomponent>
Note: See TracBrowser for help on using the browser.