MuraCon Notes - XRay Vision into the Application Lifecycle

October 02, 2013

XRay Vision into the Application Lifecycle - Cameron Childress

CFML Lifecycle Phase
- what happens when and in what order?

System Hooks
-primarily event handlers

(All into is based on Mura 6.0.5375)

(6.1...DI/1 is in, ColdSpring is OUT! ack!)

first request
application init
-- pseudo constructor "this" variables get set
then goes into the onRequest stuff, etc.

Mura Application.cfc
everything broken out into sections, in its own file(s) in the /config/appcfc

App initialization
pseudo-constructor
sets "this" scope variables
creates settings.ini.cfm if needed
reads mapping files
should sessions be turned on?
define custom tags paths
turn on client management if needed
some ORM setup
read plugins/cfapplication.cfm file
read config/cfapplication.cfm file

Application Startup - onApplicationStart() method
first - detects if Mura is/isn't set up
if it's not, puts you into the /setup folder to take care of that
reads the settings.ini.cfm file AGAIN

Note, can use environment specific values in this file!!!!
[settings]
....
[staging]
...
[development]
...
can make different setting variables for the different servers!

Read ColdSpring config (in 6.1 is reads DI/1 instead probably)
looks for database updates
-- /dbupdates folder
-- files for the different versions of Mura
-- basically a bunch of if() statements, runs them ALL, IN ORDER
-- so you get all the updates applied during upgrades, etc
but here's the problem: every time the app startup process runs, it does ALL of those updates
there is a request variable that turns this off. but as soon as you upgrade, you need it ON again in case new tables need to be created

loads
- Mura services (content manager, etc) pulls them out of coldspring/di1
- "bad words" list
-- <script and <form tags
...not a "bad words" list as in keeping profanity out (but that'd make a good plugin!)

- class extension mgr
- resource bundle factory
- rebuilds main mappings AGAIN

auto-discovers plugins
(if you have autodiscoverPlugins=true)
if there's a new plugin, it auto-registers the plugin
(still have to activate it for each site, but at least it's installed)

Reset Application variables (cfstatic has a var there, so this gets reset, etc)
creates files if they don't exists
-plugins folder
-robots.txt
-web.config
(template for these files are in /config/templates/*)
it's actually safer to change the real files NOT the templates. templates get redone in Mura upgrades. these "real files" don't change or get rewritten if they already exist

executes onApplicationLoad() for plugins
- if plugins want to do something now that Mura has loaded, they can do it now
loops all sites
- runs config.xml.cfm
- contains the Extended Attributes for your different objects, users, pages, etc.

site's onApplicationLoad()
Theme's onApplicaitonLoad()

order is important!!! (and not always the same. some areas of Mura run these methods in a different order -- use showtrace to make sure things fire in the order you expect)

completes other misc tasks
cleans out some temple files
autodeploys bundles (if it exists in a particular place)
unthrottles session scope
-- variable that makes sure 2 requests aren't going on at the same time while the app set up is running. after setup is done, that flag gets turned off so the site runs normally again
all this is in the file config/applicationSetup include files

Request Initialization ----
first, check that the app has been loaded. if not, it does all that AppInit stuff we just saw
this is where the "reload application" magic happens
(just includes the same file as the ApplicationCFC included before)

turns on Showtrace if it's present in the URL
showtrace
- can display all the things going on in Mura
- events being thrown, etc
creates some default app vars / cookies
also a cookie that contains all my auth info (for front end or back end)
if this cookie isn't formatted correctly, assumes something is wrong with your session and logs you OUT
mobile browser detection
creates config/cfapplication.cfm if needed

manages / publish change sets (if needed)

onGlobalSessionStart() - where Mura REALLY does onSessionStart() stuff. it doesn't really use the normal ColdFusion onSessionStart at all
then
onGlobalRequestStart()
-- if any plugins/themes need to do stuff for this request, they do it now

Scirpt protection runs (url, form, cookie, cgi)
- uses PortCullis under the covers (John Mason wrote) looks for xss and sql injection attacks, etc. prohibits them from being in any of the above 4 scopes
---- onGlobalThreatDetect() might run

--- CONTENT RENDERING --
index.cfm contains 2 lines of code
but that's where ALL the rabbit-hole mess is for how Mura renders content
could spend a week looking at this

THEN
onGlobalRequestEnd()
Mura just broadcasts this event in case you're listening for it (i.e. for logging) but Mura doesn't use it really

onSiteSessionEnd()
for when a session ends

ok that was LIFECYCLE REQUESTS, now let's do EVENTS...

what is an event?
(same concept as in FW/1)
starts as all the url/form variables, then Mura stuffs a bunch of other stuff in there too
$.event() -- gets you this event object
want a variable out of it, do:
$event().getValue( "foo" );

Two categories of Event Handlers
1. lifecycle events
- request start, app starts, request occurs, session ends, etc.
- things happening during regular life cycle of a request
- only about 5 of them (for front-end requests)
- some others that hook into the Admin

2. contextual events
only run when needed. triggered under the covers by an if() statement or as the result of a specific content rendering
Application
Admin Rendering
Staging -> Production (!!!)
Content
Comment, etc
...can run events before and after these context things happen

On prefix -- ADDS functionality (my plugin does something THEN the theme does something, etc)
Standard prefix - REPLACE functionality

ex:
onSiteRequestInit()
standardSetContentHandler()

* there are 50 documented LifeCycle Events in the Mura docs
(and lots more in the source code)

ShowTrace
shows everything that's happening inside
?showtrace=true
must be logged in as admin role for this to work
(timing | cumulative time up to that point)

then do ?appreload=true and you can see ALL the steps that run when the app starts up

so how do you hook into event handlers?
via EventHandler.cfc
Three different places
1. site specific
/site/includes/eventHandler.cfc
2. Theme
/[site]/includes/themes/theme/eventHandler.cfc
3. plugins
/plugins/[plugin]/includes/eventHandler.cfc
can register things to use later in your app, language translator, etc
makes them available in the other locations

"Fancy" contextual events
ex: if you have a user (of a type or subtype, or page, or subtype of page)
if I have type of page called Resume:
onBeforePageSave()
you get 3 things in $
- new Bean - newly created, about to be saved
- contentBean - what it USED to look like, so you can compare before/after
-active Bean = currently "active"
---- whatever "version" is currently active for that page, USUALLY this is the most recent version if if you rolled back to an older one, it will show the correct one here

(http://localtest.me -- always points to localhost, so you can use that instead of editing your hosts file, makes building development sites easier)

or for a specific TYPE of page i made called Resume:
onBeforePageResumeSave()
...can do the same thing but will ONLY fire for pages that are that TYPE
...will run BOTH the Page AND the PageResume() event handlers (because "Resume" is also a "Page")
so make sure they're running in the order you want to get the results you want, etc.

that was an "on" event,

"standard" events -- for REPLACING functionality
/requirements/mura/handler/standardEventsHandler.cfc
...something already iN Mura they already have code for
so go to this file first, copy what Mura does, put the handler in my custom eventHandler, and change it there
so i start with what Mura does already, and i can make whatever changes i need from there.

standardRequireLoginValidator() -- decides whether you need to log in or not
...can modify to check for, say, if someone is not coming from the office IP, make them log in first, etc.

do extended find for 'announceEvent' in the codebase, and you can find all the events in the app
lots of undocumented ones
(there are even MORE undocumented ones in the MuraBootStrap theme)