component { /* Copyright (c) 2009-2012, Sean Corfield, Ryan Cogswell Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ this.name = hash( getBaseTemplatePath() ); if ( len( getContextRoot() ) ) { variables.cgiScriptName = replace( CGI.SCRIPT_NAME, getContextRoot(), '' ); variables.cgiPathInfo = replace( CGI.PATH_INFO, getContextRoot(), '' ); } else { variables.cgiScriptName = CGI.SCRIPT_NAME; variables.cgiPathInfo = CGI.PATH_INFO; } request._fw1 = { cgiScriptName = CGI.SCRIPT_NAME, cgiRequestMethod = CGI.REQUEST_METHOD, controllers = [ ], requestDefaultsInitialized = false, services = [ ], trace = [ ] }; // do not rely on these, they are meant to be true magic... variables.magicApplicationSubsystem = ']['; variables.magicApplicationController = '[]'; variables.magicApplicationAction = '__'; variables.magicBaseURL = '-[]-'; public void function abortController() { request._fw1.abortController = true; frameworkTrace( 'abortController() called' ); throw( type='FW1.AbortControllerException', message='abortController() called' ); } public boolean function actionSpecifiesSubsystem( string action ) { if ( !usingSubsystems() ) { return false; } return listLen( action, variables.framework.subsystemDelimiter ) > 1 || right( action, 1 ) == variables.framework.subsystemDelimiter; } public void function addRoute( any routes, string target, any methods = [ ], string statusCode = '' ) { if ( !isArray( routes ) ) routes = [ routes ]; if ( !isArray( methods ) ) methods = [ methods ]; param name="variables.framework.routes" default="#[ ]#"; if ( len( statusCode ) ) target = statusCode & ':' & target; for ( var route in routes ) { if ( arrayLen( methods ) ) { for ( var method in methods ) { arrayAppend( variables.framework.routes, { '$#method##route#' = target } ); } } else { arrayAppend( variables.framework.routes, { '#route#' = target } ); } } } /* * buildURL() should be used from views to construct urls when using subsystems or * in order to provide a simpler transition to using subsystems in the future */ public string function buildURL( string action = '.', string path = variables.magicBaseURL, any queryString = '' ) { if ( action == '.' ) action = getFullyQualifiedAction(); if ( path == variables.magicBaseURL ) path = getBaseURL(); var omitIndex = false; if ( path == 'useSubsystemConfig' ) { var subsystemConfig = getSubsystemConfig( getSubsystem( action ) ); if ( structKeyExists( subsystemConfig, 'baseURL' ) ) { path = subsystemConfig.baseURL; } else { path = getBaseURL(); } } if ( path == 'useCgiScriptName' ) { path = request._fw1.cgiScriptName; if ( variables.framework.SESOmitIndex ) { path = getDirectoryFromPath( path ); omitIndex = true; } } else if ( path == 'useRequestURI' ) { path = getPageContext().getRequest().getRequestURI(); if ( variables.framework.SESOmitIndex ) { path = getDirectoryFromPath( path ); omitIndex = true; } } // if queryString is a struct, massage it into a string if ( isStruct( queryString ) && structCount( queryString ) ) { var q = ''; for( var key in queryString ) { if( isSimpleValue( queryString[key] ) ){ q &= '#urlEncodedFormat( key )#=#urlEncodedFormat( queryString[ key ] )#&'; } } queryString = q; } else if ( !isSimpleValue( queryString ) ) { queryString = ''; } if ( queryString == '' ) { // extract query string from action section: var q = find( '?', action ); var a = find( '##', action ); if ( q > 0 ) { if ( q < len( action ) ) { queryString = right( action, len( action ) - q ); } if ( q == 1 ) { action = ''; } else { action = left( action, q - 1 ); } } else if ( a > 0 ) { queryString = right( action, len( action ) - a + 1 ); if ( a == 1 ) { action = ''; } else { action = left( action, a - 1 ); } } } var cosmeticAction = getFullyQualifiedAction( action ); var isHomeAction = cosmeticAction == getFullyQualifiedAction( variables.framework.home ); var isDefaultItem = getItem( cosmeticAction ) == variables.framework.defaultItem; var initialDelim = '?'; var varDelim = '&'; var equalDelim = '='; var basePath = ''; var extraArgs = ''; var queryPart = ''; var anchor = ''; var ses = false; if ( find( '?', path ) > 0 ) { if ( right( path, 1 ) == '?' || right( path, 1 ) == '&' ) { initialDelim = ''; } else { initialDelim = '&'; } } else if ( structKeyExists( request._fw1, 'generateSES' ) && request._fw1.generateSES ) { if ( omitIndex ) { initialDelim = ''; } else { initialDelim = '/'; } varDelim = '/'; equalDelim = '/'; ses = true; } var curDelim = varDelim; if ( usingSubsystems() && getSubsystem( cosmeticAction ) == variables.framework.defaultSubsystem ) { cosmeticAction = getSectionAndItem( cosmeticAction ); } if ( len( queryString ) ) { // extract query part and anchor from query string: q = find( '?', queryString ); if ( q > 0 ) { queryPart = right( queryString, len( queryString ) - q ); if ( q > 1 ) { extraArgs = left( queryString, q - 1 ); } a = find( '##', queryPart ); if ( a > 0 ) { anchor = right( queryPart, len( queryPart ) - a ); if ( a == 1 ) { queryPart = ''; } else { queryPart = left( queryPart, a - 1 ); } } } else { extraArgs = queryString; a = find( '##', extraArgs ); if ( a > 0 ) { anchor = right( extraArgs, len( extraArgs ) - a ); if ( a == 1 ) { extraArgs = ''; } else { extraArgs = left( extraArgs, a - 1 ); } } } if ( ses ) { extraArgs = listChangeDelims( extraArgs, '/', '&=' ); } } if ( ses ) { if ( isHomeAction && extraArgs == '' ) { basePath = path; } else if ( isDefaultItem && extraArgs == '' ) { basePath = path & initialDelim & listFirst( cosmeticAction, '.' ); } else { basePath = path & initialDelim & replace( cosmeticAction, '.', '/' ); } } else { if ( isHomeAction ) { basePath = path; curDelim = '?'; } else if ( isDefaultItem ) { basePath = path & initialDelim & variables.framework.action & equalDelim & listFirst( cosmeticAction, '.' ); } else { basePath = path & initialDelim & variables.framework.action & equalDelim & cosmeticAction; } } if ( extraArgs != '' ) { basePath = basePath & curDelim & extraArgs; curDelim = varDelim; } if ( queryPart != '' ) { if ( ses ) { basePath = basePath & '?' & queryPart; } else { basePath = basePath & curDelim & queryPart; } } if ( anchor != '' ) { basePath = basePath & '##' & anchor; } return basePath; } /* * call this from your Application.cfc methods to queue up additional controller * method calls at the start of the request */ public void function controller( string action ) { var subsystem = getSubsystem( action ); var section = getSection( action ); var item = getItem( action ); var tuple = { }; if ( structKeyExists( request._fw1, 'controllerExecutionStarted' ) ) { raiseException( type='FW1.controllerExecutionStarted', message="Controller '#action#' may not be added at this point.", detail='The controller execution phase has already started. Controllers may not be added by other controller methods.' ); } tuple.controller = getController( section = section, subsystem = subsystem ); tuple.key = subsystem & variables.framework.subsystemDelimiter & section; tuple.subsystem = subsystem; tuple.section = section; tuple.item = item; if ( structKeyExists( tuple, 'controller' ) && isObject( tuple.controller ) && !isNull(tuple.controller)) { frameworkTrace( 'queuing controller', subsystem, section, item ); arrayAppend( request._fw1.controllers, tuple ); } } /* * can be overridden to customize how views and layouts are found - can be * used to provide skinning / common views / layouts etc */ public string function customizeViewOrLayoutPath( struct pathInfo, string type, string fullPath ) { // fullPath is: '#pathInfo.base##type#s/#pathInfo.path#.cfm' return fullPath; } /* * return the action URL variable name - allows applications to build URLs */ public string function getAction() { return variables.framework.action; } /* * returns the base URL for redirects and links etc * can be overridden if you need to modify this per-request */ public string function getBaseURL() { return variables.framework.baseURL; } /* * returns whatever the framework has been told is a bean factory * this will return a subsystem-specific bean factory if one * exists for the current request's subsystem (or for the specified subsystem * if passed in) */ public any function getBeanFactory( string subsystem = '' ) { if ( len( subsystem ) > 0 ) { if ( hasSubsystemBeanFactory( subsystem ) ) { return getSubsystemBeanFactory( subsystem ); } return getDefaultBeanFactory(); } if ( !usingSubsystems() ) { return getDefaultBeanFactory(); } if ( structKeyExists( request, 'subsystem' ) && len( request.subsystem ) > 0 ) { return getBeanFactory( request.subsystem ); } if ( len( variables.framework.defaultSubsystem ) > 0 ) { return getBeanFactory( variables.framework.defaultSubsystem ); } return getDefaultBeanFactory(); } /* * return the framework configuration */ public struct function getConfig() { // return a copy to make it read only from outside the framework: return structCopy( framework ); } /* * returns the bean factory set via setBeanFactory */ public any function getDefaultBeanFactory() { return application[ variables.framework.applicationKey ].factory; } /* * returns the name of the default subsystem */ public string function getDefaultSubsystem() { if ( !usingSubsystems() ) { return ''; } if ( structKeyExists( request, 'subsystem' ) ) { return request.subsystem; } if ( variables.framework.defaultSubsystem == '' ) { raiseException( type='FW1.subsystemNotSpecified', message='No subsystem specified and no default configured.', detail='When using subsystems, every request should specify a subsystem or variables.framework.defaultSubsystem should be configured.' ); } return variables.framework.defaultSubsystem; } /* * override this to provide your environment selector */ public string function getEnvironment() { return ''; } /* * return an action with all applicable parts (subsystem, section, and item) specified * using defaults from the configuration or request where appropriate */ public string function getFullyQualifiedAction( string action = request.action ) { if ( usingSubsystems() ) { return getSubsystem( action ) & variables.framework.subsystemDelimiter & getSectionAndItem( action ); } return getSectionAndItem( action ); } /* * return the local hostname of the server */ public string function getHostname() { return createObject( 'java', 'java.net.InetAddress' ).getLocalHost().getHostName(); } /* * return the item part of the action */ public string function getItem( string action = request.action ) { return listLast( getSectionAndItem( action ), '.' ); } /* * return the current route (if any) */ public string function getRoute() { return structKeyExists( request._fw1, 'route' ) ? request._fw1.route : ''; } /* * return the configured routes */ public array function getRoutes() { return variables.framework.routes; } /* * return the resource route templates */ public array function getResourceRouteTemplates() { return variables.framework.resourceRouteTemplates; } /* * return the section part of the action */ public string function getSection( string action = request.action ) { return listFirst( getSectionAndItem( action ), '.' ); } /* * return the action without the subsystem */ public string function getSectionAndItem( string action = request.action ) { var sectionAndItem = ''; if ( usingSubsystems() && actionSpecifiesSubsystem( action ) ) { if ( listLen( action, variables.framework.subsystemDelimiter ) > 1 ) { sectionAndItem = listLast( action, variables.framework.subsystemDelimiter ); } } else { sectionAndItem = action; } if ( len( sectionAndItem ) == 0 ) { sectionAndItem = variables.framework.defaultSection & '.' & variables.framework.defaultItem; } else if ( listLen( sectionAndItem, '.' ) == 1 ) { if ( left( sectionAndItem, 1 ) == '.' ) { if ( structKeyExists( request, 'section' ) ) { sectionAndItem = request.section & '.' & listLast( sectionAndItem, '.' ); } else { sectionAndItem = variables.framework.defaultSection & '.' & listLast( sectionAndItem, '.' ); } } else { sectionAndItem = listFirst( sectionAndItem, '.' ) & '.' & variables.framework.defaultItem; } } else { sectionAndItem = listFirst( sectionAndItem, '.' ) & '.' & listLast( sectionAndItem, '.' ); } return sectionAndItem; } /* * return the default service result key * override this if you want the default service result to be * stored under a different request context key, based on the * requested action, e.g., return getSection( action ); */ public string function getServiceKey( action ) { return 'data'; } /* * return the subsystem part of the action */ public string function getSubsystem( string action = request.action ) { if ( actionSpecifiesSubsystem( action ) ) { return listFirst( action, variables.framework.subsystemDelimiter ); } return getDefaultSubsystem(); } /* * return the base directory for the current request's subsystem */ public string function getSubsystemBase() { return request.subsystemBase; } /* * return the (optional) configuration for a subsystem */ public struct function getSubsystemConfig( string subsystem ) { if ( structKeyExists( variables.framework.subsystems, subsystem ) ) { // return a copy to make it read only from outside the framework: return structCopy( variables.framework.subsystems[ subsystem ] ); } return { }; } /* * returns the bean factory set via setSubsystemBeanFactory * same effect as getBeanFactory when not using subsystems */ public any function getSubsystemBeanFactory( string subsystem ) { setupSubsystemWrapper( subsystem ); return application[ variables.framework.applicationKey ].subsystemFactories[ subsystem ]; } /* * returns true iff a call to getBeanFactory() will successfully return a bean factory * previously set via setBeanFactory or setSubsystemBeanFactory */ public boolean function hasBeanFactory() { if ( hasDefaultBeanFactory() ) { return true; } if ( !usingSubsystems() ) { return false; } if ( structKeyExists( request, 'subsystem' ) ) { return hasSubsystemBeanFactory(request.subsystem); } if ( len(variables.framework.defaultSubsystem) > 0 ) { return hasSubsystemBeanFactory(variables.framework.defaultSubsystem); } return false; } /* * returns true iff the framework has been told about a bean factory via setBeanFactory */ public boolean function hasDefaultBeanFactory() { return structKeyExists( application[ variables.framework.applicationKey ], 'factory' ); } /* * returns true if a subsystem specific bean factory has been set */ public boolean function hasSubsystemBeanFactory( string subsystem ) { ensureNewFrameworkStructsExist(); return structKeyExists( application[ variables.framework.applicationKey ].subsystemFactories, subsystem ); } /* * layout() may be invoked inside layouts * returns the UI generated by the named layout and body */ public string function layout( string path, string body ) { var layoutPath = parseViewOrLayoutPath( path, 'layout' ); frameworkTrace( 'layout( #path# ) called - rendering #layoutPath#' ); return internalLayout( layoutPath, body ); } /* * it is better to set up your application configuration in * your setupApplication() method since that is called on a * framework reload * if you do override onApplicationStart(), you must call * super.onApplicationStart() first */ public any function onApplicationStart() { setupFrameworkDefaults(); setupRequestDefaults(); setupApplicationWrapper(); } /* * can be overridden, calling super.onError(exception,event) is optional * depending on what error handling behavior you want * note: you need to rename / disable onError() on OpenBD since it does * not seem to be passed exception or event correctly when something fails * in the code... */ public void function onError( any exception, string event ) { try { if ( !structKeyExists( variables, 'framework' ) || !structKeyExists( variables.framework, 'version' ) ) { // error occurred before framework was initialized failure( exception, event, false, true ); return; } // record details of the exception: if ( structKeyExists( request, 'action' ) ) { request.failedAction = request.action; } request.exception = exception; request.event = event; // reset lifecycle flags: structDelete( request, 'layout' ); structDelete( request._fw1, 'controllerExecutionComplete' ); structDelete( request._fw1, 'controllerExecutionStarted' ); structDelete( request._fw1, 'serviceExecutionComplete' ); structDelete( request._fw1, 'overrideViewAction' ); if ( structKeyExists( request._fw1, 'renderData' ) ) { // need to reset the content type as well! try { getPageContext().getResponse().setContentType( 'text/html; charset=utf-8' ); } catch ( any e ) { // but ignore any exceptions } structDelete( request._fw1, 'renderData' ); } // setup the new controller action, based on the error action: request._fw1.controllers = [ ]; // reset services for this new action: request._fw1.services = [ ]; if ( structKeyExists( variables, 'framework' ) && structKeyExists( variables.framework, 'error' ) ) { request.action = variables.framework.error; } else { // this is an edge case so we don't bother with subsystems etc // (because if part of the framework defaults are not present, // we'd have to do a lot of conditional logic here!) request.action = 'main.error'; } // ensure request.context is available if ( !structKeyExists( request, 'context' ) ) { request.context = { }; } if ( !structKeyExists( request, 'base' ) ) { if ( structKeyExists( variables, 'framework' ) && structKeyExists( variables.framework, 'base' ) ) { request.base = variables.framework.base; } else { request.base = ''; } } if ( !structKeyExists( request, 'cfcbase' ) ) { if ( structKeyExists( variables, 'framework' ) && structKeyExists( variables.framework, 'cfcbase' ) ) { request.cfcbase = variables.framework.cfcbase; } else { request.cfcbase = ''; } } frameworkTrace( 'onError( #exception.message#, #event# ) called' ); setupRequestWrapper( false ); onRequest( '' ); frameworkTraceRender(); } catch ( any e ) { failure( e, 'onError' ); failure( exception, event, true ); frameworkTraceRender(); } } /* * this can be overridden if you want to change the behavior when * FW/1 cannot find a matching view */ public string function onMissingView( struct rc ) { // unable to find a matching view - fail with a nice exception viewNotFound(); // if we got here, we would return the string to be rendered // but viewNotFound() throws an exception... // for example, return view( 'main.missing' ); } /* * This can be overridden if you want to change the behavior when * FW/1 encounters an error when trying to populate bean properties * using all of the keys in the request context (rather than a * specific list of keys). By default FW/1 silently ignores these errors. * Available in the arguments are the bean cfc and the property that was * being set when the error occurred as well as the request context structure. * You can also reference the cfcatch variable for details about the error. */ public void function onPopulateError( any cfc, string property, struct rc ) { } /* * not intended to be overridden, automatically deleted for CFC requests */ public any function onRequest( string targetPath ) { var out = 0; var i = 0; var tuple = 0; var _data_fw1 = 0; var once = { }; var n = 0; request._fw1.controllerExecutionStarted = true; try { n = arrayLen( request._fw1.controllers ); for ( i = 1; i <= n; i = i + 1 ) { tuple = request._fw1.controllers[ i ]; // run before once per controller: if ( !structKeyExists( once, tuple.key ) ) { once[ tuple.key ] = i; doController( tuple, 'before', 'before' ); if ( structKeyExists( request._fw1, 'abortController' ) ) abortController(); } doController( tuple, 'start' & tuple.item, 'start' ); if ( structKeyExists( request._fw1, 'abortController' ) ) abortController(); doController( tuple, tuple.item, 'item' ); if ( structKeyExists( request._fw1, 'abortController' ) ) abortController(); } n = arrayLen( request._fw1.services ); for ( i = 1; i <= n; i = i + 1 ) { tuple = request._fw1.services[ i ]; if ( tuple.key == '' ) { // throw the result away: doService( tuple, tuple.item, tuple.args, tuple.enforceExistence ); if ( structKeyExists( request._fw1, 'abortController' ) ) abortController(); } else { _data_fw1 = doService( tuple, tuple.item, tuple.args, tuple.enforceExistence ); if ( structKeyExists( request._fw1, 'abortController' ) ) abortController(); if ( isDefined('_data_fw1') ) { frameworkTrace( 'store service result in rc.#tuple.key#', tuple.subsystem, tuple.section, tuple.item ); request.context[ tuple.key ] = _data_fw1; } else { frameworkTrace( 'service returned no result for rc.#tuple.key#', tuple.subsystem, tuple.section, tuple.item ); } } } request._fw1.serviceExecutionComplete = true; n = arrayLen( request._fw1.controllers ); for ( i = n; i >= 1; i = i - 1 ) { tuple = request._fw1.controllers[ i ]; doController( tuple, 'end' & tuple.item, 'end' ); if ( structKeyExists( request._fw1, 'abortController' ) ) abortController(); if ( once[ tuple.key ] eq i ) { doController( tuple, 'after', 'after' ); if ( structKeyExists( request._fw1, 'abortController' ) ) abortController(); } } } catch ( FW1.AbortControllerException e ) { request._fw1.serviceExecutionComplete = true; } request._fw1.controllerExecutionComplete = true; if ( structKeyExists( request._fw1, 'renderData' ) ) { out = renderDataWithContentType(); } else { buildViewQueue(); frameworkTrace( 'setupView() called' ); setupView(); if ( structKeyExists(request._fw1, 'view') ) { frameworkTrace( 'rendering #request._fw1.view#' ); out = internalView( request._fw1.view ); } else { frameworkTrace( 'onMissingView() called' ); out = onMissingView( request.context ); } buildLayoutQueue(); for ( i = 1; i <= arrayLen(request._fw1.layouts); i = i + 1 ) { if ( structKeyExists(request, 'layout') && !request.layout ) { frameworkTrace( 'aborting layout rendering' ); break; } frameworkTrace( 'rendering #request._fw1.layouts[i]#' ); out = internalLayout( request._fw1.layouts[i], out ); } } writeOutput( out ); setupResponseWrapper(); } /* * if you override onRequestEnd(), call super.onRequestEnd() if you * want tracing functionality to continue working */ public any function onRequestEnd() { frameworkTraceRender(); } /* * it is better to set up your request configuration in * your setupRequest() method * if you do override onRequestStart(), you must call * super.onRequestStart() first */ public any function onRequestStart( string targetPath ) { setupFrameworkDefaults(); setupRequestDefaults(); if ( !isFrameworkInitialized() || isFrameworkReloadRequest() ) { setupApplicationWrapper(); } restoreFlashContext(); // ensure flash context cannot override request action: request.context[variables.framework.action] = request.action; // allow configured extensions and paths to pass through to the requested template. // NOTE: for unhandledPaths, we make the list into an escaped regular expression so we match on subdirectories. // Meaning /myexcludepath will match '/myexcludepath' and all subdirectories if ( listFindNoCase( variables.framework.unhandledExtensions, listLast( targetPath, '.' ) ) || REFindNoCase( '^(' & variables.framework.unhandledPathRegex & ')', targetPath ) ) { structDelete(this, 'onRequest'); structDelete(variables, 'onRequest'); structDelete(this, 'onRequestEnd'); structDelete(variables, 'onRequestEnd'); if ( !variables.framework.unhandledErrorCaught ) { structDelete(this, 'onError'); structDelete(variables, 'onError'); } } else { setupRequestWrapper( true ); } } /* * it is better to set up your session configuration in * your setupSession() method * if you do override onSessionStart(), you must call * super.onSessionStart() first */ public any function onSessionStart() { setupFrameworkDefaults(); setupRequestDefaults(); setupSessionWrapper(); } // populate() may be invoked inside controllers public any function populate( any cfc, string keys = '', boolean trustKeys = false, boolean trim = false, deep = false ) { if ( keys == '' ) { if ( trustKeys ) { // assume everything in the request context can be set into the CFC for ( var property in request.context ) { try { var args = { }; args[ property ] = request.context[ property ]; if ( trim && isSimpleValue( args[ property ] ) ) args[ property ] = trim( args[ property ] ); // cfc[ 'set'&property ]( argumentCollection = args ); // ugh! no portable script version of this?!?! setProperty( cfc, property, args ); } catch ( any e ) { onPopulateError( cfc, property, request.context ); } } } else { var setters = findImplicitAndExplicitSetters( cfc ); for ( var property in setters ) { if ( structKeyExists( request.context, property ) ) { var args = { }; args[ property ] = request.context[ property ]; if ( trim && isSimpleValue( args[ property ] ) ) args[ property ] = trim( args[ property ] ); // cfc[ 'set'&property ]( argumentCollection = args ); // ugh! no portable script version of this?!?! setProperty( cfc, property, args ); } else if ( deep && structKeyExists( cfc, 'get' & property ) ) { //look for a context property that starts with the property for ( var key in request.context ) { if ( listFindNoCase( key, property, '.') ) { try { setProperty( cfc, key, { '#key#' = request.context[ key ] } ); } catch ( any e ) { onPopulateError( cfc, key, request.context); } } } } } } } else { var setters = findImplicitAndExplicitSetters( cfc ); var keyArray = listToArray( keys ); for ( var property in keyArray ) { var trimProperty = trim( property ); if ( structKeyExists( setters, trimProperty ) || trustKeys ) { if ( structKeyExists( request.context, trimProperty ) ) { var args = { }; args[ trimProperty ] = request.context[ trimProperty ]; if ( trim && isSimpleValue( args[ trimProperty ] ) ) args[ trimProperty ] = trim( args[ trimProperty ] ); // cfc[ 'set'&trimproperty ]( argumentCollection = args ); // ugh! no portable script version of this?!?! setProperty( cfc, trimProperty, args ); } } else if ( deep ) { if ( listLen( trimProperty, '.' ) > 1 ) { var prop = listFirst( trimProperty, '.' ); if ( structKeyExists( cfc, 'get' & prop ) ) { setProperty( cfc, trimProperty, { '#trimProperty#' = request.context[ trimProperty ] } ); } } } } } return cfc; } private void function setProperty( struct cfc, string property, struct args ) { if ( listLen( property, '.' ) > 1 ) { var firstObjName = listFirst( property, '.' ); var newProperty = listRest( property, '.' ); args[ newProperty ] = args[ property ]; structDelete( args, property ); if ( structKeyExists( cfc , 'get' & firstObjName ) ) { var obj = getProperty( cfc, firstObjName ); if ( !isNull( obj ) ) setProperty( obj, newProperty, args ); } } else { evaluate( 'cfc.set#property#( argumentCollection = args )' ); } } private any function getProperty( struct cfc, string property ) { if ( structKeyExists( cfc, 'get#property#' ) ) return evaluate( 'cfc.get#property#()' ); } // call from your controller to redirect to a clean URL based on an action, pushing data to flash scope if necessary: public void function redirect( string action, string preserve = 'none', string append = 'none', string path = variables.magicBaseURL, string queryString = '', string statusCode = '302' ) { if ( path == variables.magicBaseURL ) path = getBaseURL(); var preserveKey = ''; if ( preserve != 'none' ) { preserveKey = saveFlashContext( preserve ); } var baseQueryString = ''; if ( append != 'none' ) { if ( append == 'all' ) { for ( var key in request.context ) { if ( isSimpleValue( request.context[ key ] ) ) { baseQueryString = listAppend( baseQueryString, key & '=' & urlEncodedFormat( request.context[ key ] ), '&' ); } } } else { var keys = listToArray( append ); for ( var key in keys ) { if ( structKeyExists( request.context, key ) && isSimpleValue( request.context[ key ] ) ) { baseQueryString = listAppend( baseQueryString, key & '=' & urlEncodedFormat( request.context[ key ] ), '&' ); } } } } if ( baseQueryString != '' ) { if ( queryString != '' ) { if ( left( queryString, 1 ) == '?' || left( queryString, 1 ) == '##' ) { baseQueryString = baseQueryString & queryString; } else { baseQueryString = baseQueryString & '&' & queryString; } } } else { baseQueryString = queryString; } var targetURL = buildURL( action, path, baseQueryString ); if ( preserveKey != '' && variables.framework.maxNumContextsPreserved > 1 ) { if ( find( '?', targetURL ) ) { preserveKey = 'variables.framework.preserveKeyURLKey#=#preserveKey#'; } else { preserveKey = '?#variables.framework.preserveKeyURLKey#=#preserveKey#'; } if ( find( '##', targetURL ) ) { targetURL = listFirst( targetURL, '##' ) & preserveKey & '##' & listRest( targetURL, '##' ); } else { targetURL = targetURL & preserveKey; } } setupResponseWrapper(); if ( variables.framework.trace ) { frameworkTrace( 'redirecting to #targetURL# (#statusCode#)' ); try { session._fw1_trace = request._fw1.trace; } catch ( any _ ) { // ignore exception if session is not enabled } } location( targetURL, false, statusCode ); } // call this to render data rather than a view and layouts public void function renderData( string type, any data ) { request._fw1.renderData = { type = type, data = data }; } // call this from your controller to queue up additional services public void function service( string action, string key, struct args = { }, boolean enforceExistence = true ) { var subsystem = getSubsystem( action ); var section = getSection( action ); var item = getItem( action ); var tuple = { }; if ( structKeyExists( request._fw1, 'serviceExecutionComplete' ) ) { raiseException( type='FW1.serviceExecutionComplete', message="Service '#action#' may not be added at this point.", detail='The service execution phase is complete. Services may not be added by end*() or after() controller methods.' ); } tuple.service = getService(section=section, subsystem=subsystem); tuple.subsystem = subsystem; tuple.section = section; tuple.item = item; tuple.key = key; tuple.args = args; tuple.enforceExistence = enforceExistence; if ( structKeyExists( tuple, 'service' ) && isObject( tuple.service ) ) { frameworkTrace( 'queuing service', subsystem, section, item ); arrayAppend( request._fw1.services, tuple ); } else if ( enforceExistence ) { raiseException( type='FW1.serviceCfcNotFound', message="Service '#action#' does not exist.", detail="To have the execution of this service be conditional based upon its existence, pass in a fourth parameter (or enforceExistence if using named arguments) of 'false'." ); } } /* * call this from your setupApplication() method to tell the framework * about your bean factory - only assumption is that it supports: * - containsBean(name) - returns true if factory contains that named bean, else false * - getBean(name) - returns the named bean */ public void function setBeanFactory( any beanFactory ) { application[ variables.framework.applicationKey ].factory = beanFactory; } /* * use this to override the default layout */ public void function setLayout( string action ) { request._fw1.overrideLayoutAction = validateAction( action ); } /* * call this from your setupSubsystem() method to tell the framework * about your subsystem-specific bean factory - only assumption is that it supports: * - containsBean(name) - returns true if factory contains that named bean, else false * - getBean(name) - returns the named bean */ public void function setSubsystemBeanFactory( string subsystem, any factory ) { ensureNewFrameworkStructsExist(); application[ variables.framework.applicationKey ].subsystemFactories[ subsystem ] = factory; } /* * override this to provide application-specific initialization * if you want the framework to use a bean factory and autowire * controllers and services, call setBeanFactory(factory) in your * setupApplication() method * you do not need to call super.setupApplication() */ public void function setupApplication() { } /* * override this to provide environment-specific initialization * you do not need to call super.setupEnvironment() */ public void function setupEnvironment( string env ) { } /* * override this to provide request-specific initialization * you do not need to call super.setupRequest() */ public void function setupRequest() { } /* * override this to provide request-specific finalization * you do not need to call super.setupResponse() */ public void function setupResponse() { } /* * override this to provide session-specific initialization * you do not need to call super.setupSession() */ public void function setupSession() { } /* * override this to provide subsystem-specific initialization * if you want the framework to use a bean factory and autowire * controllers and services, call * setSubsystemBeanFactory( subsystem, factory ) * in your setupSubsystem() method * you do not need to call super.setupSubsystem( subsystem ) */ public void function setupSubsystem( string subsystem ) { } /* * override this to provide pre-rendering logic, e.g., to * populate the request context with globally required data * you do not need to call super.setupView() */ public void function setupView() { } /* * use this to override the default view */ public void function setView( string action ) { request._fw1.overrideViewAction = validateAction( action ); } /* * returns true if the application is configured to use subsystems */ public boolean function usingSubsystems() { return variables.framework.usingSubsystems; } /* * view() may be invoked inside views and layouts * returns the UI generated by the named view */ public string function view( string path, struct args = { }, any missingView = { } ) { var viewPath = parseViewOrLayoutPath( path, 'view' ); if ( cachedFileExists( viewPath ) ) { frameworkTrace( 'view( #path# ) called - rendering #viewPath#' ); return internalView( viewPath, args ); } else if ( isSimpleValue( missingView ) ) { return missingView; } else { frameworkTrace( 'view( #path# ) called - onMissingView() called' ); return onMissingView( request.context ); } } // THE FOLLOWING METHODS SHOULD ALL BE CONSIDERED PRIVATE / UNCALLABLE private void function autowire( any cfc, any beanFactory ) { var setters = findImplicitAndExplicitSetters( cfc ); for ( var property in setters ) { if ( beanFactory.containsBean( property ) ) { var args = { }; args[ property ] = beanFactory.getBean( property ); // cfc['set'&property](argumentCollection = args) does not work on ACF9 evaluate( 'cfc.set#property#( argumentCollection = args )' ); } } } private void function buildLayoutQueue() { var siteWideLayoutBase = request.base & getSubsystemDirPrefix( variables.framework.siteWideLayoutSubsystem ); var testLayout = 0; // default behavior: var subsystem = request.subsystem; var section = request.section; var item = request.item; var subsystembase = ''; request._fw1.layouts = [ ]; // has layout been overridden? if ( structKeyExists( request._fw1, 'overrideLayoutAction' ) ) { subsystem = getSubsystem( request._fw1.overrideLayoutAction ); section = getSection( request._fw1.overrideLayoutAction ); item = getItem( request._fw1.overrideLayoutAction ); structDelete( request._fw1, 'overrideLayoutAction' ); } subsystembase = request.base & getSubsystemDirPrefix( subsystem ); frameworkTrace( 'building layout queue', subsystem, section, item ); // look for item-specific layout: testLayout = parseViewOrLayoutPath( subsystem & variables.framework.subsystemDelimiter & section & '/' & item, 'layout' ); if ( cachedFileExists( testLayout ) ) { frameworkTrace( 'found item-specific layout #testLayout#', subsystem, section, item ); arrayAppend( request._fw1.layouts, testLayout ); } // look for section-specific layout: testLayout = parseViewOrLayoutPath( subsystem & variables.framework.subsystemDelimiter & section, 'layout' ); if ( cachedFileExists( testLayout ) ) { frameworkTrace( 'found section-specific layout #testLayout#', subsystem, section, item ); arrayAppend( request._fw1.layouts, testLayout ); } // look for subsystem-specific layout (site-wide layout if not using subsystems): if ( request.section != 'default' ) { testLayout = parseViewOrLayoutPath( subsystem & variables.framework.subsystemDelimiter & 'default', 'layout' ); if ( cachedFileExists( testLayout ) ) { frameworkTrace( 'found default layout #testLayout#', subsystem, section, item ); arrayAppend( request._fw1.layouts, testLayout ); } } // look for site-wide layout (only applicable if using subsystems) if ( usingSubsystems() && siteWideLayoutBase != subsystembase ) { testLayout = parseViewOrLayoutPath( variables.framework.siteWideLayoutSubsystem & variables.framework.subsystemDelimiter & 'default', 'layout' ); if ( cachedFileExists( testLayout ) ) { frameworkTrace( 'found #variables.framework.siteWideLayoutSubsystem# layout #testLayout#', subsystem, section, item ); arrayAppend( request._fw1.layouts, testLayout ); } } } private void function buildViewQueue() { // default behavior: var subsystem = request.subsystem; var section = request.section; var item = request.item; var subsystembase = ''; // has view been overridden? if ( structKeyExists( request._fw1, 'overrideViewAction' ) ) { subsystem = getSubsystem( request._fw1.overrideViewAction ); section = getSection( request._fw1.overrideViewAction ); item = getItem( request._fw1.overrideViewAction ); structDelete( request._fw1, 'overrideViewAction' ); } subsystembase = request.base & getSubsystemDirPrefix( subsystem ); frameworkTrace( 'building view queue', subsystem, section, item ); // view and layout setup - used to be in setupRequestWrapper(): request._fw1.view = parseViewOrLayoutPath( subsystem & variables.framework.subsystemDelimiter & section & '/' & item, 'view' ); if ( cachedFileExists( request._fw1.view ) ) { frameworkTrace( 'found view #request._fw1.view#', subsystem, section, item ); } else { frameworkTrace( 'no such view #request._fw1.view#', subsystem, section, item ); request.missingView = request._fw1.view; // ensures original view not re-invoked for onError() case: structDelete( request._fw1, 'view' ); } } private boolean function cachedFileExists( string filePath ) { var cache = application[ variables.framework.applicationKey ].cache; if ( !variables.framework.cacheFileExists ) { return fileExists( expandPath( filePath) ); } param name="cache.fileExists" default="#{ }#"; if ( !structKeyExists( cache.fileExists, filePath ) ) { cache.fileExists[ filePath ] = fileExists( expandPath( filePath ) ); } return cache.fileExists[ filePath ]; } private string function cfcFilePath( string dottedPath ) { if ( dottedPath == '' ) { return '/'; } else { return '/' & replace( dottedPath, '.', '/', 'all' ) & '/'; } } private void function doController( struct tuple, string method, string lifecycle ) { var cfc = tuple.controller; if ( structKeyExists( cfc, method ) ) { try { frameworkTrace( 'calling #lifecycle# controller', tuple.subsystem, tuple.section, method ); evaluate( 'cfc.#method#( rc = request.context )' ); } catch ( any e ) { setCfcMethodFailureInfo( cfc, method ); rethrow; } } else if ( structKeyExists( cfc, 'onMissingMethod' ) ) { try { frameworkTrace( 'calling #lifecycle# controller (via onMissingMethod)', tuple.subsystem, tuple.section, method ); evaluate( 'cfc.#method#( rc = request.context, method = lifecycle )' ); } catch ( any e ) { setCfcMethodFailureInfo( cfc, method ); rethrow; } } else { frameworkTrace( 'no #lifecycle# controller to call', tuple.subsystem, tuple.section, method ); } } private any function doService( struct tuple, string method, struct args, boolean enforceExistence ) { var cfc = tuple.service; if ( structKeyExists( cfc, method ) || structKeyExists( cfc, 'onMissingMethod' ) ) { try { structAppend( args, request.context, false ); frameworkTrace( 'calling service', tuple.subsystem, tuple.section, method ); var _result_fw1 = evaluate( 'cfc.#method#( argumentCollection = args )' ); if ( !isNull( _result_fw1 ) ) { return _result_fw1; } } catch ( any e ) { setCfcMethodFailureInfo( cfc, method ); rethrow; } } else if ( enforceExistence ) { raiseException( type='FW1.serviceMethodNotFound', message="Service method '#method#' does not exist in service '#getMetadata( cfc ).fullname#'.", detail="To have the execution of this service method be conditional based upon its existence, pass in a third parameter of 'false'." ); } } private void function dumpException( any exception ) { writeDump( var = exception, label = 'Exception' ); } private void function ensureNewFrameworkStructsExist() { var framework = application[variables.framework.applicationKey]; if ( !structKeyExists(framework, 'subsystemFactories') ) { framework.subsystemFactories = { }; } if ( !structKeyExists(framework, 'subsystems') ) { framework.subsystems = { }; } } private void function failure( any exception, string event, boolean indirect = false, boolean early = false ) { var h = indirect ? 3 : 1; if ( structKeyExists(exception, 'rootCause') ) { exception = exception.rootCause; } getPageContext().getResponse().setStatus( 500 ); if ( arguments.early ) { writeOutput( '
The action #request.failedAction# failed.
' ); } writeOutput( '#exception.detail# (#exception.type#)
' ); dumpException(exception); } private struct function findImplicitAndExplicitSetters( any cfc ) { var baseMetadata = getMetadata( cfc ); var setters = { }; // is it already attached to the CFC metadata? if ( structKeyExists( baseMetadata, '__fw1_setters' ) ) { setters = baseMetadata.__fw1_setters; } else { var md = { extends = baseMetadata }; do { md = md.extends; var implicitSetters = false; // we have implicit setters if: accessors="true" or persistent="true" if ( structKeyExists( md, 'persistent' ) && isBoolean( md.persistent ) ) { implicitSetters = md.persistent; } if ( structKeyExists( md, 'accessors' ) && isBoolean( md.accessors ) ) { implicitSetters = implicitSetters || md.accessors; } if ( structKeyExists( md, 'properties' ) ) { // due to a bug in ACF9.0.1, we cannot use var property in md.properties, // instead we must use an explicit loop index... ugh! var n = arrayLen( md.properties ); for ( var i = 1; i <= n; ++i ) { var property = md.properties[ i ]; if ( implicitSetters || structKeyExists( property, 'setter' ) && isBoolean( property.setter ) && property.setter ) { setters[ property.name ] = 'implicit'; } } } } while ( structKeyExists( md, 'extends' ) ); // cache it in the metadata (note: in Railo 3.2 metadata cannot be modified // which is why we return the local setters structure - it has to be built // on every controller call; fixed in Railo 3.3) baseMetadata.__fw1_setters = setters; } // gather up explicit setters as well for ( var member in cfc ) { var method = cfc[ member ]; var n = len( member ); if ( isCustomFunction( method ) && left( member, 3 ) == 'set' && n > 3 ) { var property = right( member, n - 3 ); setters[ property ] = 'explicit'; } } return setters; } private void function frameworkTrace( string message, string subsystem = '', string section = '', string item = '' ) { if ( variables.framework.trace ) { try { if ( isDefined( 'session._fw1_trace' ) && structKeyExists( session, '_fw1_trace' ) ) { request._fw1.trace = session._fw1_trace; structDelete( session, '_fw1_trace' ); } } catch ( any _ ) { // ignore if session is not enabled } arrayAppend( request._fw1.trace, { tick = getTickCount(), msg = message, sub = subsystem, s = section, i = item } ); } } private void function frameworkTraceRender() { if ( variables.framework.trace && arrayLen( request._fw1.trace ) ) { var startTime = request._fw1.trace[1].tick; var font = 'font-family: verdana, helvetica;'; writeOutput( '#trace.tick - startTime#ms | ' ); writeOutput( '#action# | ' ); var color = trace.msg.startsWith( 'no ' ) ? '##cc8888' : trace.msg.startsWith( 'onError( ' ) ? '##cc0000' : '##0000'; writeOutput( '#trace.msg# | ' ); writeOutput( '