PageRenderTime 37ms CodeModel.GetById 10ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 1ms

/wheels/global/internal.cfm

http://cfwheels.googlecode.com/
ColdFusion | 954 lines | 915 code | 39 blank | 0 comment | 79 complexity | a0b7a56d31ee1edb5ed29faef9b32980 MD5 | raw file
  1<cffunction name="$compactOutput" returntype="string" access="public" output="false">
  2	<cfargument name="output" type="string" required="true">
  3	<cfscript>
  4		var loc = {};
  5		if (!StructKeyExists(application.wheels.vendor, "compactor"))
  6		{
  7			loc.filePaths = [];
  8			loc.filePaths[1] = ExpandPath("wheels/vendor/compactor/compactor.jar");
  9			loc.javaLoader = CreateObject("component", "#application.wheels.wheelsComponentPath#.vendor.javaloader.JavaLoader").init(loc.filePaths);
 10			application.wheels.vendor.compactor = loc.javaLoader.create("com.mindprod.compactor.Compactor");
 11		}
 12	</cfscript>
 13	<cfreturn application.wheels.vendor.compactor.compactString(arguments.output, "") />
 14</cffunction>
 15
 16<cffunction name="$htmlFormat" returntype="string" access="public" output="false">
 17	<cfargument name="string" type="string" required="true" />
 18	<cfscript>
 19		var loc = {};
 20		if (!StructKeyExists(application.wheels.vendor, "stringEscapeUtils"))
 21		{
 22			loc.filePaths = [];
 23			loc.filePaths[1] = ExpandPath("wheels/vendor/commons-lang/commons-lang-2.5.jar");
 24			loc.javaLoader = CreateObject("component", "#application.wheels.wheelsComponentPath#.vendor.javaloader.JavaLoader").init(loc.filePaths);
 25			application.wheels.vendor.stringEscapeUtils = loc.javaLoader.create("org.apache.commons.lang.StringEscapeUtils");
 26		}
 27	</cfscript>
 28	<cfreturn application.wheels.vendor.stringEscapeUtils.escapeHtml(arguments.string) />
 29</cffunction>
 30
 31<cffunction name="$initializeRequestScope" returntype="void" access="public" output="false">
 32	<cfscript>
 33		if (!StructKeyExists(request, "wheels"))
 34		{
 35			request.wheels = {};
 36			request.wheels.vendor = {};
 37			request.wheels.routes = {};
 38			request.wheels.params = {};
 39			request.wheels.cache = {};
 40			
 41			// create a structure to track the transaction status for all adapters
 42			request.wheels.transactions = {};
 43	
 44			// store cache info for output in debug area
 45			request.wheels.cacheCounts = {};
 46			request.wheels.cacheCounts.hits = 0;
 47			request.wheels.cacheCounts.misses = 0;
 48			request.wheels.cacheCounts.culls = 0;
 49		}
 50	</cfscript>
 51</cffunction>
 52
 53<cffunction name="$toXml" returntype="xml" access="public" output="false">
 54	<cfargument name="data" type="any" required="true">
 55	<cfscript>
 56		// only instantiate the toXml object once per request
 57		if (!StructKeyExists(request.wheels.vendor, "toXml"))
 58			request.wheels.vendor.toXml = $createObjectFromRoot(path="#application.wheels.wheelsComponentPath#.vendor.toXml", fileName="toXML", method="init");
 59	</cfscript>
 60	<cfreturn request.wheels.vendor.toXml.toXml(arguments.data) />
 61</cffunction>
 62
 63<cffunction name="$convertToString" returntype="string" access="public" output="false">
 64	<cfargument name="value" type="Any" required="true">
 65	<cfscript>
 66		if (IsBinary(arguments.value))
 67			return ToString(arguments.value);
 68		else if (IsDate(arguments.value))
 69			return CreateDateTime(year(arguments.value), month(arguments.value), day(arguments.value), hour(arguments.value), minute(arguments.value), second(arguments.value));
 70	</cfscript>
 71	<cfreturn arguments.value>
 72</cffunction>
 73
 74<cffunction name="$listClean" returntype="any" access="public" output="false" hint="removes whitespace between list elements. optional argument to return the list as an array.">
 75	<cfargument name="list" type="string" required="true">
 76	<cfargument name="delim" type="string" required="false" default=",">
 77	<cfargument name="returnAs" type="string" required="false" default="string">
 78	<cfargument name="defaultValue" type="any" required="false" default="">
 79	<cfscript>
 80		var loc = {};
 81		loc.list = ListToArray(arguments.list, arguments.delim);
 82		loc.iEnd = ArrayLen(loc.list);
 83		for (loc.i = 1; loc.i lte loc.iEnd; loc.i++)
 84		{
 85			loc.list[loc.i] = Trim(loc.list[loc.i]);
 86		}
 87
 88		switch (arguments.returnAs)
 89		{
 90			case "array":
 91			{// already an array so just break out
 92				break;
 93			}
 94			case "struct":
 95			{
 96				loc.s = {};
 97				for (loc.i = 1; loc.i lte loc.iEnd; loc.i++)
 98				{
 99					loc.s[loc.list[loc.i]] = arguments.defaultValue;
100				}
101				loc.list = loc.s;
102				break;
103			}
104			default:
105			{// create a list using the supplied delimeter
106				loc.list = ArrayToList(loc.list, arguments.delim);
107				break;
108			}
109		}
110	</cfscript>
111	<cfreturn loc.list>
112</cffunction>
113
114<cffunction name="$simpleHashedKey" returntype="string" access="public" output="false" hint="Same as $hashedKey but cannot handle binary data in queries.">
115	<cfargument name="value" type="any" required="true">
116	<cfscript>
117		var returnValue = "";
118		returnValue = SerializeJSON(arguments.value);
119		// remove the characters that indicate array or struct so that we can sort it as a list below
120		returnValue = ReplaceList(returnValue, "{,},[,]", ",,,");
121		returnValue = ListSort(returnValue, "text");
122		return returnValue;
123	</cfscript>
124</cffunction>
125
126<cffunction name="$hashedKey" returntype="string" access="public" output="false" hint="Creates a unique string based on any arguments passed in (used as a key for caching mostly).">
127	<cfscript>
128		var loc = {};
129		loc.returnValue = "";
130
131		// we need to make sure we are looping through the passed in arguments in the same order everytime
132		loc.values = [];
133		loc.keyList = ListSort(StructKeyList(arguments), "textnocase", "asc");
134		loc.iEnd = ListLen(loc.keyList);
135		for (loc.i = 1; loc.i <= loc.iEnd; loc.i++)
136			ArrayAppend(loc.values, arguments[ListGetAt(loc.keyList, loc.i)]);
137
138		if (!ArrayIsEmpty(loc.values))
139		{
140			// this might fail if a query contains binary data so in those rare cases we fall back on using cfwddx (which is a little bit slower which is why we don't use it all the time)
141			try
142			{
143				loc.returnValue = $simpleHashedKey(loc.values);
144			}
145			catch (Any e)
146			{
147				loc.returnValue = $wddx(input=loc.values);
148			}
149		}
150		return Hash(loc.returnValue);
151	</cfscript>
152</cffunction>
153
154<cffunction name="$timeSpanForCache" returntype="any" access="public" output="false">
155	<cfargument name="cache" type="any" required="true">
156	<cfargument name="defaultCacheTime" type="numeric" required="false" default="#application.wheels.defaultCacheTime#">
157	<cfargument name="cacheDatePart" type="string" required="false" default="#application.wheels.cacheDatePart#">
158	<cfscript>
159		var loc = {};
160		loc.cache = arguments.defaultCacheTime;
161		if (IsNumeric(arguments.cache))
162			loc.cache = arguments.cache;
163		loc.list = "0,0,0,0";
164		loc.dateParts = "d,h,n,s";
165		loc.iEnd = ListLen(loc.dateParts);
166		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
167			if (arguments.cacheDatePart == ListGetAt(loc.dateParts, loc.i))
168				loc.list = ListSetAt(loc.list, loc.i, loc.cache);
169		return CreateTimeSpan(ListGetAt(loc.list, 1),ListGetAt(loc.list, 2),ListGetAt(loc.list, 3),ListGetAt(loc.list, 4));
170	</cfscript>
171</cffunction>
172
173<cffunction name="$combineArguments" returntype="void" access="public" output="false">
174	<cfargument name="args" type="struct" required="true">
175	<cfargument name="combine" type="string" required="true">
176	<cfargument name="required" type="boolean" required="false" default="false">
177	<cfargument name="extendedInfo" type="string" required="false" default="">
178	<cfscript>
179		if (StructKeyExists(arguments.args, ListGetAt(arguments.combine, 2)))
180		{
181			arguments.args[ListGetAt(arguments.combine, 1)] = arguments.args[ListGetAt(arguments.combine, 2)];
182			StructDelete(arguments.args, ListGetAt(arguments.combine, 2));
183		}
184		if (arguments.required && application.wheels.showErrorInformation)
185			if (!StructKeyExists(arguments.args, ListGetAt(arguments.combine, 2)) && !Len(arguments.args[ListGetAt(arguments.combine, 1)]))
186				$throw(type="Wheels.IncorrectArguments", message="The `#ListGetAt(arguments.combine, 2)#` or `#ListGetAt(arguments.combine, 1)#` argument is required but was not passed in.", extendedInfo="#arguments.extendedInfo#");
187	</cfscript>
188</cffunction>
189
190<!--- helper method to recursively map a structure to build mapping paths and retrieve its values so you can have your way with a deeply nested structure --->
191<cffunction name="$mapStruct" returntype="void" access="public" output="false" mixin="dispatch">
192	<cfargument name="map" type="struct" required="true" />
193	<cfargument name="struct" type="struct" required="true" />
194	<cfargument name="path" type="string" required="false" default="" />
195	<cfscript>
196		var loc = {};
197		for (loc.item in arguments.struct)
198		{
199			if (IsStruct(arguments.struct[loc.item])) // go further down the rabit hole
200			{
201				$mapStruct(map=arguments.map, struct=arguments.struct[loc.item], path="#arguments.path#[#loc.item#]");
202			}
203			else // map our position and value
204			{
205				arguments.map["#arguments.path#[#loc.item#]"] = {};
206				arguments.map["#arguments.path#[#loc.item#]"].value = arguments.struct[loc.item];
207			}
208		}
209	</cfscript>
210</cffunction>
211
212<cffunction name="$structKeysExist" returntype="boolean" access="public" output="false" hint="Check to see if all keys in the list exist for the structure and have length.">
213	<cfargument name="struct" type="struct" required="true" />
214	<cfargument name="keys" type="string" required="false" default="" />
215	<cfscript>
216		var loc = {};
217		loc.returnValue = true;
218		loc.iEnd = ListLen(arguments.keys);
219		for (loc.i = 1; loc.i lte loc.iEnd; loc.i++)
220		{
221			if (!StructKeyExists(arguments.struct, ListGetAt(arguments.keys, loc.i)) || (IsSimpleValue(arguments.struct[ListGetAt(arguments.keys, loc.i)]) && !Len(arguments.struct[ListGetAt(arguments.keys, loc.i)])))
222			{
223				loc.returnValue = false;
224				break;
225			}
226		}
227	</cfscript>
228	<cfreturn loc.returnValue />
229</cffunction>
230
231<cffunction name="$cgiScope" returntype="struct" access="public" output="false" hint="This copies all the variables Wheels needs from the CGI scope to the request scope.">
232	<cfargument name="keys" type="string" required="false" default="request_method,http_x_requested_with,http_referer,server_name,path_info,script_name,query_string,remote_addr,server_port,server_port_secure,server_protocol,http_host,http_accept,content_type">
233	<cfscript>
234		var loc = {};
235		loc.returnValue = {};
236		loc.iEnd = ListLen(arguments.keys);
237		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
238			loc.returnValue[ListGetAt(arguments.keys, loc.i)] = cgi[ListGetAt(arguments.keys, loc.i)];
239	</cfscript>
240	<cfreturn loc.returnValue>
241</cffunction>
242
243<cffunction name="$dollarify" returntype="struct" access="public" output="false">
244	<cfargument name="input" type="struct" required="true">
245	<cfargument name="on" type="string" required="true">
246	<cfscript>
247		var loc = {};
248		for (loc.key in arguments.input)
249		{
250			if (ListFindNoCase(arguments.on, loc.key))
251			{
252				arguments.input["$"&loc.key] = arguments.input[loc.key];
253				StructDelete(arguments.input, loc.key);
254			}
255		}
256	</cfscript>
257	<cfreturn arguments.input>
258</cffunction>
259
260<cffunction name="$abortInvalidRequest" returntype="void" access="public" output="false">
261	<cfscript>
262		var applicationPath = Replace(GetCurrentTemplatePath(), "\", "/", "all");
263		var callingPath = Replace(GetBaseTemplatePath(), "\", "/", "all");
264		if (ListLen(callingPath, "/") GT ListLen(applicationPath, "/") || GetFileFromPath(callingPath) == "root.cfm")
265		{
266			$header(statusCode="404", statusText="Not Found");
267			$includeAndOutput(template="#application.wheels.eventPath#/onmissingtemplate.cfm");
268			$abort();
269		}
270	</cfscript>
271</cffunction>
272
273<cffunction name="$URLEncode" returntype="string" access="public" output="false">
274	<cfargument name="param" type="string" required="false" default="">
275	<cfscript>
276		var returnValue = "";
277		returnValue = URLEncodedFormat(arguments.param);
278		returnValue = ReplaceList(returnValue, "%24,%2D,%5F,%2E,%2B,%21,%2A,%27,%28,%29", "$,-,_,.,+,!,*,',(,)"); // these characters are safe so set them back to their original values.
279	</cfscript>
280	<cfreturn returnValue>
281</cffunction>
282
283<cffunction name="$routeVariables" returntype="string" access="public" output="false">
284	<cfscript>
285		var loc = {};
286		loc.route = $findRoute(argumentCollection=arguments);
287		loc.returnValue = loc.route.variables;
288	</cfscript>
289	<cfreturn loc.returnValue>
290</cffunction>
291
292<cffunction name="$findRoute" returntype="struct" access="public" output="false">
293	<cfscript>
294		var loc = {};
295
296		// throw an error if a route with this name has not been set by developer in the config/routes.cfm file
297		if (application.wheels.showErrorInformation && !StructKeyExists(application.wheels.namedRoutePositions, arguments.route))
298			$throw(type="Wheels.RouteNotFound", message="Could not find the `#arguments.route#` route.", extendedInfo="Create a new route in `config/routes.cfm` with the name `#arguments.route#`.");
299
300		loc.routePos = application.wheels.namedRoutePositions[arguments.route];
301		
302		if (ArrayLen(loc.routePos) gt 1)
303		{
304			// get our routes - we cache them in the request.wheels.routes scope to save time on subsequent calls to $findRoute
305			if (StructKeyExists(request.wheels.routes, arguments.route))
306			{
307				loc.routeArray = request.wheels.routes[arguments.route];
308			}
309			else
310			{
311				loc.routeArray = [];
312				for (loc.i = 1; loc.i lte ArrayLen(loc.routePos); loc.i++)
313					loc.routeArray[loc.i] = application.wheels.routes[loc.routePos[loc.i]];
314				request.wheels.routes[arguments.route] = loc.routeArray;
315			}
316			
317			loc.foundRoute = false;
318			while (!loc.foundRoute) // need to use a while loop here so we don't loop through all of the routes
319			{
320				if (application.wheels.showErrorInformation && !ArrayLen(loc.routePos))
321					$throw(type="Wheels.RouteMatchNotFound", message="Could not find a match for the `#arguments.route#` route.");
322
323				// we always try to find the route on the first position because we are cleaning the array everytime we don't find a match
324				loc.returnValue = loc.routeArray[1];
325				loc.foundRoute = true;
326				
327				for (loc.i = 1; loc.i lte ListLen(loc.returnValue.variables); loc.i++)
328				{
329					loc.variable = ListGetAt(loc.returnValue.variables, loc.i);
330					if (!StructKeyExists(arguments, loc.variable) || !Len(arguments[loc.variable]))
331					{
332						loc.foundRoute = false;
333						break;
334					}
335				}
336				
337				// clean the array of all routes that contain the variable that failed
338				if (!loc.foundRoute)
339					for (loc.i = ArrayLen(loc.routeArray); loc.i gte 1; loc.i--)
340						if (ListFindNoCase(loc.routeArray[loc.i].variables, loc.variable))
341							ArrayDeleteAt(loc.routeArray, loc.i);
342			}
343		}
344		else
345		{
346			loc.returnValue = application.wheels.routes[loc.routePos[1]];
347		}
348	</cfscript>
349	<cfreturn loc.returnValue>
350</cffunction>
351
352<cffunction name="$cachedModelClassExists" returntype="any" access="public" output="false">
353	<cfargument name="name" type="string" required="true">
354	<cfscript>
355		var returnValue = false;
356		if (StructKeyExists(application.wheels.models, arguments.name))
357			returnValue = application.wheels.models[arguments.name];
358	</cfscript>
359	<cfreturn returnValue>
360</cffunction>
361
362<cffunction name="$constructParams" returntype="string" access="public" output="false">
363	<cfargument name="params" type="string" required="true">
364	<cfargument name="$URLRewriting" type="string" required="false" default="#application.wheels.URLRewriting#">
365	<cfscript>
366		var loc = {};
367		arguments.params = Replace(arguments.params, "&amp;", "&", "all"); // change to using ampersand so we can use it as a list delim below and so we don't "double replace" the ampersand below
368		// when rewriting is off we will already have "?controller=" etc in the url so we have to continue with an ampersand
369		if (arguments.$URLRewriting == "Off")
370			loc.delim = "&";
371		else
372			loc.delim = "?";
373		loc.returnValue = "";
374		loc.iEnd = ListLen(arguments.params, "&");
375		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
376		{
377			loc.temp = listToArray(ListGetAt(arguments.params, loc.i, "&"), "=");
378			loc.returnValue = loc.returnValue & loc.delim & loc.temp[1] & "=";
379			loc.delim = "&";
380			if (ArrayLen(loc.temp) == 2)
381			{
382				loc.param = $URLEncode(loc.temp[2]);
383				if (application.wheels.obfuscateUrls && !ListFindNoCase("cfid,cftoken", loc.temp[1]))
384					loc.param = obfuscateParam(loc.param);
385				loc.returnValue = loc.returnValue & loc.param;
386			}
387		}
388	</cfscript>
389	<cfreturn loc.returnValue>
390</cffunction>
391
392<cffunction name="$args" returntype="any" access="public" output="false">
393	<cfargument name="args" type="struct" required="true">
394	<cfargument name="name" type="string" required="true">
395	<cfargument name="reserved" type="string" required="false" default="">
396	<cfargument name="combine" type="string" required="false" default="">
397	<cfargument name="cachable" type="boolean" required="false" default="false">
398	<cfscript>
399		var loc = {};
400		
401		// if function result caching is enabled globally, the calling function is cachable and we're not coming from a recursive call we return the result from the cache (setting the cache first when necessary)
402		if (application.wheels.cacheFunctions && arguments.cachable && !StructKeyExists(arguments.args, "$recursive"))
403		{
404			// create a unique key based on the arguments passed in to the calling function
405			// we use the simple version of the $hashedKey function here for performance reasons (we know that we'll never have binary query data passed in anyway so we don't need to deal with that)
406			loc.functionHash = $simpleHashedKey(arguments.args);
407			
408			// if the function result is not already in the cache we'll call the function and place the result in the cache
409			loc.functionResult = $getFromCache(key=loc.functionHash);
410			if (IsBoolean(loc.functionResult) && !loc.functionResult)
411			{
412				arguments.args.$recursive = true;
413				loc.functionResult = $invoke(method=arguments.name, invokeArgs=arguments.args);
414				$addToCache(key=loc.functionHash, value=loc.functionResult);
415			}
416			return loc.functionResult;
417		}
418		
419		if (Len(arguments.combine))
420		{
421			loc.iEnd = ListLen(arguments.combine);
422			for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
423			{
424				loc.item = ListGetAt(arguments.combine, loc.i);
425				loc.first = ListGetAt(loc.item, 1, "/");
426				loc.second = ListGetAt(loc.item, 2, "/");
427				loc.required = false;
428				if (ListLen(loc.item, "/") > 2)
429				{
430					loc.required = true;
431				}
432				$combineArguments(args=arguments.args, combine="#loc.first#,#loc.second#", required=loc.required);
433			}
434		}
435		if (application.wheels.showErrorInformation)
436		{
437			if (ListLen(arguments.reserved))
438			{
439				loc.iEnd = ListLen(arguments.reserved);
440				for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
441				{
442					loc.item = ListGetAt(arguments.reserved, loc.i);
443					if (StructKeyExists(arguments.args, loc.item))
444						$throw(type="Wheels.IncorrectArguments", message="The `#loc.item#` argument cannot be passed in since it will be set automatically by Wheels.");
445				}
446			}
447		}
448		if (StructKeyExists(application.wheels.functions, arguments.name))
449			StructAppend(arguments.args, application.wheels.functions[arguments.name], false);
450	</cfscript>
451</cffunction>
452
453<cffunction name="$createObjectFromRoot" returntype="any" access="public" output="false">
454	<cfargument name="path" type="string" required="true">
455	<cfargument name="fileName" type="string" required="true">
456	<cfargument name="method" type="string" required="true">
457	<cfscript>
458		var returnValue = "";
459		arguments.returnVariable = "returnValue";
460		arguments.component = ListChangeDelims(arguments.path, ".", "/") & "." & ListChangeDelims(arguments.fileName, ".", "/");
461		arguments.argumentCollection = Duplicate(arguments);
462		StructDelete(arguments, "path");
463		StructDelete(arguments, "fileName");
464	</cfscript>
465	<cfinclude template="../../root.cfm">
466	<cfreturn returnValue>
467</cffunction>
468
469<cffunction name="$debugPoint" returntype="void" access="public" output="false">
470	<cfargument name="name" type="string" required="true">
471	<cfscript>
472		var loc = {};
473		if (!StructKeyExists(request.wheels, "execution"))
474			request.wheels.execution = {};
475		loc.iEnd = ListLen(arguments.name);
476		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
477		{
478			loc.item = ListGetAt(arguments.name, loc.i);
479			if (StructKeyExists(request.wheels.execution, loc.item))
480				request.wheels.execution[loc.item] = GetTickCount() - request.wheels.execution[loc.item];
481			else
482				request.wheels.execution[loc.item] = GetTickCount();
483		}
484	</cfscript>
485</cffunction>
486
487<cffunction name="$cachedControllerClassExists" returntype="any" access="public" output="false">
488	<cfargument name="name" type="string" required="true">
489		<cfscript>
490			var returnValue = false;
491			if (StructKeyExists(application.wheels.controllers, arguments.name))
492				returnValue = application.wheels.controllers[arguments.name];
493		</cfscript>
494	<cfreturn returnValue>
495</cffunction>
496
497<cffunction name="$fileExistsNoCase" returntype="boolean" access="public" output="false">
498	<cfargument name="absolutePath" type="string" required="true">
499	<cfscript>
500		var loc = {};
501
502		// break up the full path string in the path name only and the file name only
503		loc.path = GetDirectoryFromPath(arguments.absolutePath);
504		loc.file = Replace(arguments.absolutePath, loc.path, "");
505
506		// get all existing files in the directory and place them in a list
507		loc.dirInfo = $directory(directory=loc.path);
508		loc.fileList = ValueList(loc.dirInfo.name);
509
510		// loop through the file list and return true if the file exists regardless of case (the == operator is case insensitive)
511		loc.iEnd = ListLen(loc.fileList);
512		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
513			if (ListGetAt(loc.fileList, loc.i) == loc.file)
514				return true;
515
516		// the file wasn't found in the directory so we return false
517		return false;
518	</cfscript>
519</cffunction>
520
521<cffunction name="$objectFileName" returntype="string" access="public" output="false">
522	<cfargument name="name" type="string" required="true">
523	<cfargument name="objectPath" type="string" required="true">
524	<cfargument name="type" type="string" required="true" hint="Can be either `controller` or `model`." />
525	<cfscript>
526		var loc = {};
527		loc.objectFileExists = false;
528
529		// if the name contains the delimiter let's capitalize the last element and append it back to the list
530		if (ListLen(arguments.name, "/") gt 1)
531			arguments.name = ListInsertAt(arguments.name, ListLen(arguments.name, "/"), capitalize(ListLast(arguments.name, "/")), "/");
532		else
533			arguments.name = capitalize(arguments.name);
534
535		// we are going to store the full controller path in the existing / non-existing lists so we can have controllers in multiple places
536		loc.fullObjectPath = arguments.objectPath & "/" & arguments.name;
537
538		if (!ListFindNoCase(application.wheels.existingObjectFiles, loc.fullObjectPath) && !ListFindNoCase(application.wheels.nonExistingObjectFiles, loc.fullObjectPath))
539		{
540			if (FileExists(ExpandPath("#loc.fullObjectPath#.cfc")))
541				loc.objectFileExists = true;
542			if (application.wheels.cacheFileChecking)
543			{
544				if (loc.objectFileExists)
545					application.wheels.existingObjectFiles = ListAppend(application.wheels.existingObjectFiles, loc.fullObjectPath);
546				else
547					application.wheels.nonExistingObjectFiles = ListAppend(application.wheels.nonExistingObjectFiles, loc.fullObjectPath);
548			}
549		}
550		if (ListFindNoCase(application.wheels.existingObjectFiles, loc.fullObjectPath) || loc.objectFileExists)
551			loc.returnValue = arguments.name;
552		else
553			loc.returnValue = capitalize(arguments.type);
554	</cfscript>
555	<cfreturn loc.returnValue>
556</cffunction>
557
558<cffunction name="$createControllerClass" returntype="any" access="public" output="false">
559	<cfargument name="name" type="string" required="true">
560	<cfargument name="controllerPaths" type="string" required="false" default="#application.wheels.controllerPath#">
561	<cfargument name="type" type="string" required="false" default="controller" />
562	<cfscript>
563		var loc = {};
564
565		// let's allow for multiple controller paths so that plugins can contain controllers
566		// the last path is the one we will instantiate the base controller on if the controller is not found on any of the paths
567		for (loc.i = 1; loc.i lte ListLen(arguments.controllerPaths); loc.i++)
568		{
569			loc.controllerPath = ListGetAt(arguments.controllerPaths, loc.i);
570			loc.fileName = $objectFileName(name=arguments.name, objectPath=loc.controllerPath, type=arguments.type);
571
572			if (loc.fileName != "Controller" || loc.i == ListLen(arguments.controllerPaths))
573			{
574				application.wheels.controllers[arguments.name] = $createObjectFromRoot(path=loc.controllerPath, fileName=loc.fileName, method="$initControllerClass", name=arguments.name);
575				loc.returnValue = application.wheels.controllers[arguments.name];
576				break;
577			}
578		}
579	</cfscript>
580	<cfreturn loc.returnValue>
581</cffunction>
582
583<cffunction name="$addToCache" returntype="void" access="public" output="false">
584	<cfset application.wheels.cache.add(argumentCollection=arguments)>
585</cffunction>
586
587<cffunction name="$getFromCache" returntype="any" access="public" output="false">
588	<cfscript>
589		var cacheItem = application.wheels.cache.get(argumentCollection=arguments);
590		
591		if (application.wheels.showDebugInformation)
592		{
593			if (IsSimpleValue(cacheItem) && IsBoolean(cacheItem) && !cacheItem)
594			{
595				request.wheels.cacheCounts.misses = request.wheels.cacheCounts.misses + 1;
596			}
597			else
598			{
599				request.wheels.cacheCounts.hits = request.wheels.cacheCounts.hits + 1;
600			}
601		}
602	</cfscript>
603	<cfreturn application.wheels.cache.get(argumentCollection=arguments)>
604</cffunction>
605
606<cffunction name="$removeFromCache" returntype="void" access="public" output="false">
607	<cfset application.wheels.cache.remove(argumentCollection=arguments)>
608</cffunction>
609
610<cffunction name="$cacheCount" returntype="numeric" access="public" output="false">
611	<cfreturn application.wheels.cache.count(argumentCollection=arguments)>
612</cffunction>
613
614<cffunction name="$clearCache" returntype="void" access="public" output="false">
615	<cfreturn application.wheels.cache.clear(argumentCollection=arguments)>
616</cffunction>
617
618<cffunction name="$createModelClass" returntype="any" access="public" output="false">
619	<cfargument name="name" type="string" required="true">
620	<cfargument name="modelPaths" type="string" required="false" default="#application.wheels.modelPath#">
621	<cfargument name="type" type="string" required="false" default="model" />
622	<cfscript>
623		var loc = {};
624		// let's allow for multiple controller paths so that plugins can contain controllers
625		// the last path is the one we will instantiate the base controller on if the controller is not found on any of the paths
626		for (loc.i = 1; loc.i lte ListLen(arguments.modelPaths); loc.i++)
627		{
628			loc.modelPath = ListGetAt(arguments.modelPaths, loc.i);
629			loc.fileName = $objectFileName(name=arguments.name, objectPath=loc.modelPath, type=arguments.type);
630
631			if (loc.fileName != arguments.type || loc.i == ListLen(arguments.modelPaths))
632			{
633				application.wheels.models[arguments.name] = $createObjectFromRoot(path=loc.modelPath, fileName=loc.fileName, method="$initModelClass", name=arguments.name);
634				loc.returnValue = application.wheels.models[arguments.name];
635				break;
636			}
637		}
638	</cfscript>
639	<cfreturn loc.returnValue>
640</cffunction>
641
642<!---
643Used to announce to the developer that a feature they are using will be removed at some point.
644DOES NOT work in production mode.
645
646To use call $deprecated() from within the method you want to deprecate. You may pass an optional
647custom message if desired. The method will return a structure containing the message and information
648about where the deprecation occurrs like the called method, line number, template name and shows the
649code that called the deprcated method.
650
651Example:
652
653Original foo()
654<cffunction name="foo" returntype="any" access="public" output="false">
655	<cfargument name="baz" type="numeric" required="true">
656	<cfreturn baz++>
657</cffunction>
658
659Should now call bar() instead and marking foo() as deprecated
660<cffunction name="foo" returntype="any" access="public" output="false">
661	<cfargument name="baz" type="numeric" required="true">
662	<cfset $deprecated("Calling foo is now deprecated, use bar() instead.")>
663	<cfreturn bar(argumentscollection=arguments)>
664</cffunction>
665
666<cffunction name="bar" returntype="any" access="public" output="false">
667	<cfargument name="baz" type="numeric" required="true">
668	<cfreturn ++baz>
669</cffunction>
670 --->
671<cffunction name="$deprecated" returntype="struct" access="public" output="false">
672	<!--- a message to display instead of the default one. --->
673	<cfargument name="message" type="string" required="false" default="You are using deprecated behavior which will be removed from the next major or minor release.">
674	<!--- should you announce the deprecation. only used when writing tests. --->
675	<cfargument name="announce" type="boolean" required="false" default="true">
676	<cfset var loc = {}>
677	<cfset loc.ret = {}>
678	<cfset loc.tagcontext = []>
679	<cfif not application.wheels.showDebugInformation>
680		<cfreturn loc.ret>
681	</cfif>
682	<!--- set return value --->
683	<cfset loc.data = []>
684	<cfset loc.ret = {message=arguments.message, line="", method="", template="", data=loc.data}>
685	<!---
686	create an exception so we can get the TagContext and display what file and line number the
687	deprecated method is being called in
688	 --->
689	<cftry>
690		<cfthrow type="Expression">
691		<cfcatch type="any">
692			<cfset loc.exception = cfcatch>
693		</cfcatch>
694	</cftry>
695	<cfif StructKeyExists(loc.exception, "tagcontext")>
696		<cfset loc.tagcontext = loc.exception.tagcontext>
697	</cfif>
698	<!---
699	TagContext is an array. The first element of the array will always be the context for this
700	method announcing the deprecation. The second element will be the deprecated function that
701	is being called. We need to look at the third element of the array to get the method that
702	is calling the method marked for deprecation.
703	 --->
704	<cfif isArray(loc.tagcontext) and arraylen(loc.tagcontext) gte 3 and isStruct(loc.tagcontext[3])>
705		<!--- grab and parse the information from the tagcontext. --->
706		<cfset loc.context = loc.tagcontext[3]>
707		<!--- the line number --->
708		<cfset loc.ret.line = loc.context.line>
709		<!--- the deprecated method that was called --->
710		<cfif StructKeyExists(server, "railo")>
711			<cfset loc.ret.method = rereplacenocase(loc.context.codePrintPlain, '.*\<cffunction name="([^"]*)">.*', "\1")>
712		<cfelse>
713			<cfset loc.ret.method = rereplacenocase(loc.context.raw_trace, ".*\$func([^\.]*)\..*", "\1")>
714		</cfif>
715		<!--- the user template where the method called occurred --->
716		<cfset loc.ret.template = loc.context.template>
717		<!--- try to get the code --->
718 		<cfif len(loc.ret.template) and FileExists(loc.ret.template)>
719			<!--- grab a one line radius from where the deprecation occurred. --->
720			<cfset loc.startAt = loc.ret.line - 1>
721			<cfif loc.startAt lte 0>
722				<cfset loc.startAt = loc.ret.line>
723			</cfif>
724			<cfset loc.stopAt = loc.ret.line + 1>
725			<cfset loc.counter = 1>
726			<cfloop file="#loc.ret.template#" index="loc.i">
727				<cfif loc.counter gte loc.startAt and loc.counter lte loc.stopAt>
728					<cfset arrayappend(loc.ret.data, loc.i)>
729				</cfif>
730				<cfif loc.counter gt loc.stopAt>
731					<cfbreak>
732				</cfif>
733				<cfset loc.counter++>
734			</cfloop>
735		</cfif>
736		<!--- change template name from full to relative path. --->
737		<cfset loc.ret.template = listchangedelims(removechars(loc.ret.template, 1, len(expandpath(application.wheels.webpath))), "/", "\/")>
738	</cfif>
739	<!--- append --->
740	<cfif arguments.announce>
741		<cfset arrayappend(request.wheels.deprecation, loc.ret)>
742	</cfif>
743	<cfreturn loc.ret>
744</cffunction>
745
746<cffunction name="$loadRoutes" returntype="void" access="public" output="false">
747	<cfscript>
748		// clear out the route info
749		ArrayClear(application.wheels.routes);
750		StructClear(application.wheels.namedRoutePositions);
751
752		// load developer routes first
753		$include(template="#application.wheels.configPath#/routes.cfm");
754
755		// add the wheels default routes at the end if requested
756		if (application.wheels.loadDefaultRoutes)
757			addDefaultRoutes();
758
759		// set lookup info for the named routes
760		$setNamedRoutePositions();
761		</cfscript>
762</cffunction>
763
764<cffunction name="$setNamedRoutePositions" returntype="void" access="public" output="false">
765	<cfscript>
766		var loc = {};
767		loc.iEnd = ArrayLen(application.wheels.routes);
768		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
769		{
770			loc.route = application.wheels.routes[loc.i];
771			if (StructKeyExists(loc.route, "name") && len(loc.route.name))
772			{
773				if (!StructKeyExists(application.wheels.namedRoutePositions, loc.route.name))
774					application.wheels.namedRoutePositions[loc.route.name] = ArrayNew(1);
775				ArrayAppend(application.wheels.namedRoutePositions[loc.route.name], loc.i);
776			}
777		}
778		</cfscript>
779</cffunction>
780
781<cffunction name="$clearModelInitializationCache">
782	<cfset StructClear(application.wheels.models)>
783</cffunction>
784
785<cffunction name="$clearControllerInitializationCache">
786	<cfset StructClear(application.wheels.controllers)>
787</cffunction>
788
789<cffunction name="$loadPlugins" returntype="void" access="public" output="false">
790	<cfscript>
791	var loc = {};
792	application.wheels.plugins = {};
793	application.wheels.incompatiblePlugins = "";
794	application.wheels.mixableComponents = "application,dispatch,controller,model,cache,base,connection,microsoftsqlserver,mysql,oracle,postgresql,h2";
795	application.wheels.mixins = {};
796	application.wheels.dependantPlugins = "";
797	loc.pluginFolder = GetDirectoryFromPath(GetBaseTemplatePath()) & "plugins";
798
799	// get a list of plugin files and folders
800	loc.pluginFolders = $directory(directory=loc.pluginFolder, type="dir");
801	loc.pluginFiles = $directory(directory=loc.pluginFolder, filter="*.zip", type="file", sort="name DESC");
802
803	// delete plugin directories if no corresponding plugin zip file exists
804	if (application.wheels.deletePluginDirectories)
805	{
806		loc.iEnd = loc.pluginFolders.recordCount;
807		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
808		{
809			loc.name = loc.pluginFolders["name"][loc.i];
810			loc.directory = loc.pluginFolders["directory"][loc.i];
811			if (Left(loc.name, 1) != "." && !ListContainsNoCase(ValueList(loc.pluginFiles.name), loc.name & "-"))
812			{
813				loc.directory = loc.directory & "/" & loc.name;
814				$directory(action="delete", directory=loc.directory, recurse=true);
815			}
816		}
817	}
818
819	// create directory and unzip code for the most recent version of each plugin
820	if (loc.pluginFiles.recordCount)
821	{
822		loc.iEnd = loc.pluginFiles.recordCount;
823		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
824		{
825			loc.name = loc.pluginFiles["name"][loc.i];
826			loc.pluginName = ListFirst(loc.name, "-");
827			if (!StructKeyExists(application.wheels.plugins, loc.pluginName))
828			{
829				loc.pluginVersion = Replace(ListLast(loc.name, "-"), ".zip", "", "one");
830				loc.thisPluginFile = loc.pluginFolder & "/" & loc.name;
831				loc.thisPluginFolder = loc.pluginFolder & "/" & LCase(loc.pluginName);
832				if (!DirectoryExists(loc.thisPluginFolder))
833					$directory(action="create", directory=loc.thisPluginFolder);
834
835				// unzip the plugin to its directory unless the developer has told us not to
836				// we don't use the overwrite attribute on cfzip since it's been reported that it updates the date on the files on railo
837				if (application.wheels.overwritePlugins)
838					$zip(action="unzip", destination=loc.thisPluginFolder, file=loc.thisPluginFile, overwrite=true);
839
840				loc.fileName = LCase(loc.pluginName) & "." & loc.pluginName;
841				loc.plugin = $createObjectFromRoot(path=application.wheels.pluginComponentPath, fileName=loc.fileName, method="init");
842				loc.plugin.pluginVersion = loc.pluginVersion;
843				if (!StructKeyExists(loc.plugin, "version") || ListFind(loc.plugin.version, SpanExcluding(application.wheels.version, " ")) || application.wheels.loadIncompatiblePlugins)
844				{
845					application.wheels.plugins[loc.pluginName] = loc.plugin;
846					if (StructKeyExists(loc.plugin, "version") && !ListFind(loc.plugin.version, SpanExcluding(application.wheels.version, " ")))
847						application.wheels.incompatiblePlugins = ListAppend(application.wheels.incompatiblePlugins, loc.pluginName);
848				}
849			}
850		}
851		// store plugin injection information in application scope so we don't have to run this code on each injection
852		loc.iEnd = ListLen(application.wheels.mixableComponents);
853		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
854		{
855			application.wheels.mixins[ListGetAt(application.wheels.mixableComponents, loc.i)] = {};
856		}
857		loc.iList = StructKeyList(application.wheels.plugins);
858		loc.iEnd = ListLen(loc.iList);
859		for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
860		{
861			loc.iItem = ListGetAt(loc.iList, loc.i);
862			loc.pluginMeta = GetMetaData(application.wheels.plugins[loc.iItem]); // grab meta data of the plugin
863			if (!StructKeyExists(loc.pluginMeta, "environment") || ListFindNoCase(loc.pluginMeta.environment, application.wheels.environment))
864			{
865				loc.pluginMixins = "global"; // by default and for backwards compatibility, we inject all methods into all objects
866				if (StructKeyExists(loc.pluginMeta, "mixin"))
867					loc.pluginMixins = loc.pluginMeta["mixin"]; // if the component has a default mixin value, assign that value
868				// loop through all plugin methods and enter injection info accordingly (based on the mixin value on the method or the default one set on the entire component)
869				loc.jList = StructKeyList(application.wheels.plugins[loc.iItem]);
870				loc.jEnd = ListLen(loc.jList);
871				for (loc.j=1; loc.j <= loc.jEnd; loc.j++)
872				{
873					loc.jItem = ListGetAt(loc.jList, loc.j);
874					if (IsCustomFunction(application.wheels.plugins[loc.iItem][loc.jItem]) && loc.jItem != "init")
875					{
876						loc.methodMeta = GetMetaData(application.wheels.plugins[loc.iItem][loc.jItem]);
877						loc.methodMixins = loc.pluginMixins;
878						if (StructKeyExists(loc.methodMeta, "mixin"))
879							loc.methodMixins = loc.methodMeta["mixin"];
880						// mixin all methods except those marked as none
881						if (loc.methodMixins != "none")
882						{
883							loc.kEnd = ListLen(application.wheels.mixableComponents);
884							for (loc.k=1; loc.k <= loc.kEnd; loc.k++)
885							{
886								loc.kItem = ListGetAt(application.wheels.mixableComponents, loc.k);
887								if (loc.methodMixins == "global" || ListFindNoCase(loc.methodMixins, loc.kItem))
888									application.wheels.mixins[loc.kItem][loc.jItem] = application.wheels.plugins[loc.iItem][loc.jItem];
889							}
890						}
891					}
892				}
893			}
894		}
895		// look for plugins that are incompatible with each other
896		loc.addedFunctions = "";
897		for (loc.key in application.wheels.plugins)
898		{
899			for (loc.keyTwo in application.wheels.plugins[loc.key])
900			{
901				if (!ListFindNoCase("init,version,pluginVersion", loc.keyTwo))
902				{
903					if (ListFindNoCase(loc.addedFunctions, loc.keyTwo))
904						$throw(type="Wheels.IncompatiblePlugin", message="#loc.key# is incompatible with a previously installed plugin.", extendedInfo="Make sure none of the plugins you have installed override the same Wheels functions.");
905					else
906						loc.addedFunctions = ListAppend(loc.addedFunctions, loc.keyTwo);
907				}
908			}
909		}
910		// look for plugins that depend on other plugins that are not installed
911		for (loc.key in application.wheels.plugins)
912		{
913			loc.pluginInfo = GetMetaData(application.wheels.plugins[loc.key]);
914			if (StructKeyExists(loc.pluginInfo, "dependency"))
915			{
916				loc.iEnd = ListLen(loc.pluginInfo.dependency);
917				for (loc.i=1; loc.i <= loc.iEnd; loc.i++)
918				{
919					loc.iItem = ListGetAt(loc.pluginInfo.dependency, loc.i);
920					if (!StructKeyExists(application.wheels.plugins, loc.iItem))
921						application.wheels.dependantPlugins = ListAppend(application.wheels.dependantPlugins, Reverse(SpanExcluding(Reverse(loc.pluginInfo.name), ".")) & "|" & loc.iItem);
922				}
923			}
924		}
925	}
926	</cfscript>
927</cffunction>
928
929<cffunction name="$checkMinimumVersion" access="public" returntype="boolean" output="false">
930	<cfargument name="version" type="string" required="true">
931	<cfargument name="minversion" type="string" required="true">
932	<cfscript>
933	var loc = {};
934
935	// remove periods and commas from the version and minimum version
936	arguments.version = ListChangeDelims(arguments.version, "", ".,");
937	arguments.minversion = ListChangeDelims(arguments.minversion, "", ".,");
938
939	// make version and minversion the same length pad zeros to the end
940	loc.a = max(len(arguments.version), len(arguments.minversion));
941
942	arguments.version = arguments.version & RepeatString("0", loc.a - len(arguments.version));
943	arguments.minversion = arguments.minversion & RepeatString("0", loc.a - len(arguments.minversion));
944
945	// make sure the version is an integer
946	if (IsNumeric(arguments.version) && IsNumeric(arguments.minversion) && arguments.version >= arguments.minversion)
947	{
948		return true;
949	}
950
951	return false;
952	</cfscript>
953	<cfreturn >
954</cffunction>