Breaking your page up into individual pieces, and then including those pieces later on as needed is not a new thing for most of us. That is precisely what Model Glue allows us to do as well. Is it ever required that we divide our code into individual templates? Nay, and neither does Model Glue require it. It is, however, a very good idea in most instances, and MG gives us a very easy way to do this.
Picture if you will, a template.
We have dsp_main.cfm that will act as our layout, containing only DIV tags with appropriate IDs. Dsp_main.cfm also includes our external style sheet to provide appropriate positioning and look and feel to whatever content we put into those DIVs. Before Model Glue, we'd do it like this:
<head>
<title>My Composite Template</title>
<link rel="stylesheet" type="text/css" href="css/styles.css" media="screen" />
</head>
<body>
<div id="header"><cfinclude template="dsp_header.cfm"></div>
<div id="nav"><cfinclude template="dsp_nav.cfm"></div>
<div id="body"><cfinclude template="dsp_body.cfm"></div>
<div id="footer"><cfinclude template="dsp_footer.cfm"></div>
</body>
</html>
Nothing new there, right? With Model Glue, the same dsp_main.cfm looks like this:
<cfset header = viewCollection.getView("header") />
<cfset footer = viewCollection.getView("footer") />
<cfset body = viewCollection.getView("body") />
<cfset nav = viewCollection.getView("nav") />
<html>
<head>
<title>MG View Demo</title>
<link rel="stylesheet" type="text/css" href="css/styles.css" media="screen" />
</head>
<body>
<div id="header"><cfoutput>#header#</cfoutput></div>
<div id="nav"><cfoutput>#nav#</cfoutput></div>
<div id="body"><cfoutput>#body#</cfoutput></div>
<div id="footer"><cfoutput>#footer#</cfoutput></div>
</body>
</html>
With Model Glue, rather than include those templates within the page itself, the templates were pre-rendered (almost exactly like using a CFSAVECONTENT), the rendered html stuffed into what's called the ViewCollection, and then we simply grab that rendered HTML out of the ViewCollection and output it in a very familiar fashion.
Now, how were those individual templates included? We told Model Glue to do it within the Modelglue.XML file that defines our individual events. For instance, let's say the event we called was 'main.landing' (http://www.mysite.com/index.cfm?event=main.landing). The relevant XML looks like this:
<event-handler name="main.landing">
<broadcasts />
<results />
<views>
<include name="body" template="dsp_content.cfm" /><!-- available in the viewcollection as 'body' -->
<include name="nav" template="dsp_nav.cfm" /><!-- available in the viewcollection as 'nav' -->
<include name="footer" template="dsp_footer.cfm" /><!-- available in the viewcollection as 'footer' -->
<include name="header" template="dsp_header.cfm" /><!-- available in the viewcollection as 'header' -->
<include name="main" template="dsp_main.cfm" /><!-- container template to layout individual parts -->
</views>
</event-handler>
Using the 'include' tag within the 'views' tag, we told Modelglue which templates to render. Of note is the fact that the order in which they are rendered is completely irrelevant, WITH ONE EXCEPTION: the template that acts as the layout container MUST BE RENDERED LAST.
Also of note is the fact that each of your view templates should act independently of one another. For example, let's say that a Model Glue variable is needed in the dsp_nav.cfm and that same variable is also used somewhere in the dsp_content.cfm template. It would be bad practice for you to retrieve that variable within dsp_nav.cfm and then attempt to access the retrieved instance from dsp_content.cfm; each template should be retrieving it for themselves.
That's it, boys and girls. Nothing to it, and very little difference from the way you've been using <CFINCLUDE> all along!
It consists of a function that generates a question that must be answered, and the answer to that question. It will ask the commenter to figure out what letter is exactly X number of places before or after a randomly selected letter in the alphabet, then directs them to type their answer exactly Y number of times in the answer box. Upon page load the correct answer is saved to a persistent variable, then when the comment is submitted the answer typed (form.answer) is compared to the stored answer (session.answer). Simple enough I think, but with enough randomness to make it something that can't be automatically breached without some real effort.
Here's the code for the function:
<cfset var stReturn = structnew() />
<cfset var firstnum = randrange(1,4) />
<cfset var secondnum = randrange(1,4) />
<cfset var letter = randrange(5,20) />
<cfset var where = randrange(1,2) />
<cfset var answer = "" />
<cfset var i = "" />
<cfset variables.numbers = "one,two,three,four" />
<cfset variables.letters = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z" />
<cfset variables.beforeafter = "before,after" />
<CFSET stReturn.times = randrange(1,4) />
<cfset stReturn.question = "What letter comes " & listgetat(variables.numbers,firstnum) & " places " & listgetat(variables.beforeafter,where) & " the letter " & listgetat(variables.letters,letter) & "?<br>Type your answer exactly " & listgetat(variables.numbers,secondnum) & " times in the box below." />
<cfif where eq 1>
<cfset letter = letter - firstnum />
<cfelse>
<cfset letter = letter + firstnum />
</cfif>
<cfloop index="i" from="1" to="#secondnum#" >
<cfset answer = answer & listgetat(variables.letters,letter)/>
</cfloop>
<cfset stReturn.answer = answer />
<cfreturn stReturn />
</cffunction>
(the function returns a structure containing the keys "question" and "answer")
Here's some starter code for utilizing it:
Code to evaluate saving a comment...
<cfif form.answer neq session.answer>
<cfset errorMessage = errorMessage & "<li>Invalid anti-spam key. Please try again.</li>#Chr(10)#">
<cfelse>
<!--- perform comment saving here --->
</cfif>
</cfif>
Code for displaying question and capturing answer:
<h3>Please answer the following question:</h3>
<CFOUTPUT><STRONG>#variables.spamquestion.question#</STRONG></CFOUTPUT><br>
Type in the answer to the question you see above:
<input type="text" name="answer" size="6" maxlength="6" required="Yes" Message="You must complete the anti-spam field.">
<!--- save the answer to session for evaluation after the form is submitted... --->
<cfset session.answer = variables.spamquestion.answer>
Hope it helps! :)
<TABLE CELLPADDING="1" CELLSPACING="1">
<tr>
<td align="right" valign="bottom">You:</td><td><input type="text" name="search" size="17" /></td>
</tr>
<tr>
<td align="right" valign="bottom">Your Web Site:</td><td valign="bottom"><input type="text" name="resource" size="17" /></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" name="fj" value="Find Your EgoSurf™ Score!" /></td>
</tr>
</table>
<input type="hidden" name="ds" value="1" />
<input type="hidden" name="e-g" value="1" />
<input type="hidden" name="c-g" value=".com" />
<input type="hidden" name="c-y" value=".com" />
<input type="hidden" name="c-m" value=".com" />
</form>
We just moved into a new house. Fairly large, 2,500 square foot, two story house nestled (very tightly!) in a new subdivision in north
HOW TO BUILD A PATIO
Items Needed
- One Long-Handled Flat Blade Spade
- Two each 40 pound bags of course sand
- One each 40 pound bag of crushed (or decayed) granite
- Thirty three bricks
- Your spousal unit, if applicable, and all the kids you got
Directions
1) Gather your family together and discuss the idea of making a patio for your BBQ or outdoor fireplace. If you don’t own a BBQ or outdoor fireplace, discuss getting one. As the father, husband, and guide of your household, ensure that the conversation ends up as all in favor of the project by using your experience, wisdom, and skills in gentle persuasion.
).- A waterboy/girl and general purpose gopher or two;
- Someone to provide and maintain the background music;
- Mud puppies (kiddos to help remove mud/dirt from the hole and to dig through clods already removed to find worms and grubs to study);
- A surveyor (someone to help you measure and layout the patio boundaries)
10) Instruct the waterboys/girls to bring refreshments to the other workers and themselves. Make sure the radio station selected is one that the majority enjoy, and break out the chilled blush for you and the spousal unit.
11) Chillax for a half hour, making sure that everybody gets praise for a job well done. Talk about how the patio will be used, imagining scenarios out loud and even planning as a group your first usage of it. Also, plant the thought of coming up with a next family project.
CONCLUSION
Few things promote such sincere togetherness and family unity as do joint projects, and this was no exception. So many good things came from this task...the cooperation, the sense of accomplishment, the planning and procurement skills, the practicing of the mindset to not be afraid of hard work, the sweat equity that everybody contributed and thus a stake in seeing it succeed, the exercising of love and patience, the overall feeling of ‘togetherness’ that was promoted; And more than that, the hope that this patio represents: the future memories we’ll all make out here BBQing as a family.
WHAT I THOUGHT IT WAS
Based solely on the info I managed to gather from "between the lines", I knew that JSON stood for JavaScript Object Notation, and that it was an alternative to XML when dealing with the results of Ajax calls. Knowing that much, I could deduce that it was basically "data in a string". But what it looked like, how to handle it, and why I would want to do it were still questions in my mind.
WHAT I FOUND IT TO BE
JSON is indeed a string representation of simple or complex data, just as is XML, only without the tags AND without the need to treat that string as a document type in order to transverse it elegantly. JSON contains two indicators: curly braces to indicate that a structure follows, and square brackets to indicate an array. Here's a structure written in JSON: {key1:"val1",key2:"val2",key3:"val3"} and a one dimensional array: ["val1","val2","val3"]. You can nest these types within one another, too; for example, the value of a structure key could be an array, and would be written as this: {key1:["val1","val2","val3"],key2:"val2"}. Pretty cool, eh?
Now, how to get data back and forth between JSON and raw CF types. It's a no-brainer using a tag made available as an open source project on RIA Forge by Andrew Powell: CFJSON . I downloaded it and was using it in less than two minutes on a test template. Very simple, very easy.
All of this good information led me to pose more questions, which I went on to get answers to.
- Were there any performance advantages of using JSON over XML?
- Was there significant differences in the length of the string produced by a JSON conversion as opposed to an XML conversion (significant in the realm of web services where that string will have to be sent over the wire)?
- Were there any compelling reasons to adopt JSON in leiu of XML or WDDX?
Following are the results of me finding out the answers to those questions.
THE EXPERIMENT
Working with a 500 row recordset, each row containing 52 fields of mixed data types, including some sql text, convert it to both JSON and XML and record the results of the time it took and the resulting string length. Do this in both a local template/cfc environment AND over the wire via a web service. I used CFJSON as the JSON converter, and Ray Camden's XML.cfc as the XML converter.
THE RESULTS
| LOCAL TESTS | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
||||||||||||||||||||||||||||||||||||
| WEB SERVICE TESTS | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
||||||||||||||||||||||||||||||||||||
The string size was consistent, as expected. JSON produced a string that was 57% smaller than the same data represented as XML. Even so, the actual difference in the time it takes to send the larger string doesn't even come close to compensating for the additional time it took to produce the shorter JSON string. It took 1,370% LONGER to produce the JSON string than it did the XML string. Wow, at this point it's almost a no-brainer that I would NOT want to use JSON for much if anything. But this huge difference got me to thinking: What was so vastly different about the way the XML was being produced and the way the JSON was being produced? So I dug into the CFCs to find out.
THE DIFFERENCE
Both CFC methods make use of lots and lots of looping and string concatenation. The only glaring difference was in the way this was done. XML.CFC leverages the Java.lang.stringbuffer object and CFJSON uses straight CF string manipulation. So, I altered the portion of the method in CFJSON.CFC that converts queries to utilize the java.lang.stringbuffer object as well. The results were VERY favorable! check out these time tests after I made the switch:
| CFJSON.CFC TESTS AFTER STRINGBUFFER CONVERSION | ||
|---|---|---|
| TEST | LOCAL TIME(ms) | WEB SERVICE TIME(ms) |
| 1 | 2156 | 4437 |
| 2 | 2125 | 3843 |
| 3 | 2031 | 4022 |
Wow. The conversion to JSON now runs neck in neck with the XML conversion, simply by leveraging the stringbuffer object. Sweeeeet.
Okay, now that I know that JSON data strings are significantly smaller, just as fast to create, how about manipulation? How painful is that? To find out I decided I was going to use my JSON data in a javascript function. I scaled down the size of the data set to only five records and a handful of fields for this test. Check out how simple it was to 'dump' my data into javascript and to access it afterwards:
function showMeSomeJSON(){
var objQuery = <CFOUTPUT>#jsondata#</CFOUTPUT>;
var i = 0;
for(i=0;i<objQuery.recordcount;++i){
alert(objQuery.data.due_date[i]);
}
}
</script>
CONCLUSION
I have come to no solid conclusion as of yet, but I must say that I'm leaning heavily towards JSON in lieu of XML. It's got a smaller footprint all the way around, has the ability to capture complex nesting, and is really straightforward to navigate using Javascript. Additionally, the same way CFJSON can encode complex data, it also DECODES it on the receiving end, turning it back into something that CF recognizes.
Oh, and I think I'm going to email the CFJSON guys and let them know about the significant performance improvement I got when switching to stringbuffer. They may have already experimented in this arena, but just in case, I'll let them know.
That's it for my personal "JSON Demystification". :)
Doug out.
P.S. If you would like to see an actual JSON representation of my five record query, here it is:
{"recordcount":5,"columnlist":"complete,date_desired,dept,details,developer,due_date,est_hours","data":{"complete":["100%","100%","0%","100%","0%"],"date_desired":["Less Than 2 weeks","Less Than 1 week","More Than 2 weeks","Less Than 1 week","Less Than 1 week"],"dept":["Eligibility","Marketing","Benplan_com","Repricing","EDI"],"details":["1. For rehires, if a rehire date is present on the file and the rehire date is greater than the date of hire, then I need to use it.\r\n\r\n2. Cobra reason - make sure the term reason for a benefit is being put on the AE2 file. This is tied in with Scott\'s work.\r\n\r\n3. Some dependents have termed benefits but no effective dates, also no effective date for the employee. I need to find out where these are coming from.\r\n\r\n4. Terms need to be removed from the file 60 days after the employment term date. This will actually be added to the TSS AE2 file as well.","Dave Lawson, SWI, would like for us to send him the annual report we created for him that gives him EE and Dependent data for Group #s 90947 and 90947P.","- We will send?? or receive daily RX claims\/deductible file updates to one or two PBM\'s--Innoviant for sure and maybe Catalyst.\r\n"," 1) Show correct network for each group. 2) Change elapsed days to start counting on the day the claim was sent out. ","Correct the SSN validation on inbound EDI returned repriced claims. "],"developer":["Dan Crouch","Kelly Young","Gemma Anthony","Kelly Billen","Kelly Billen"],"due_date":["09\/09\/2004","8\/19\/04","NA","08\/30\/2004","08\/13\/2004"],"est_hours":["",1,400,1,1 ]}}
I recently moved from the house I had been renting for two years to a larger one, and of course had to go through the whole "return of deposit" routine. As has been my experience, it went sour near the very end. While at first the landlady was amicable and friendly (by all appearances), when it came time to discuss the deposit she suddenly returned to me a laundry list of items that she said justified her keeping it all. Most of the items were normal wear and tear, which legally the tenant is not liable for covering financially, such as walls needing fresh paint, carpet getting worn, etc. One of the items on her list was, and I quote, "aquarium pebbles on the lawn". It got ridiculous, to say the least. So this time I decided to push the issue and formally request that she and the true owner (my landlady is a proxy) reconsider their position for several good reasons. I did this via a letter sent certified mail.
Since it took me several hours to draft and perfect this letter, I thought I'd share it here with you all in case anybody else is ever looking for a good "return of deposit dispute" letter template. Do with it what you will, and good luck getting your deposit back!
P.S. IF the lessor decides NOT to reconsider, and you feel that you have a very good case as to why you SHOULD receive some or all of your deposit back, the next step to take is to document everything that occurred (conversations, dates, times, etc.), go down to your county courthouse, and file a small claims suit naming your lessor as the defendant. I'll see in ten business days from now if that's what I need to do or not.
THE LETTER
Doug Boude
[my new address]July 3, 2007
[insert ignorant landlady's name here]
Dear Miss Ignorant Landlady:
I received what I can only assume is your handwritten letter postmarked June 29, 2007 which cited a lease agreement signed by myself on July 2, 2005 and stated that due to failure to provide thirty days written notice, all security deposits had been forfeited. This letter appeared to be a copy of a faxed document, and was signed by a person unknown to me and claiming to be co-owner of the property at [my old address]
I respectfully request that you and the true home owner, [true homeowner's name], reconsider your decision to withhold the entire amount of my nine hundred forty dollar deposit, paid by check on July 2, 2005 for the following reasons:
1. As of July 2, 2006, the lease agreement I signed on July 2, 2005 and all terms therein expired; no other lease agreement was ever signed. The supposed reason for withholding my deposit cited, vaguely, an expired agreement.
2. I was a very good tenant at all times, caring for the property as if it were my own and always with the mindset that if the homeowner were to show up at any time, she would not be disappointed with the state of the property. I paid the full amount of my rent every month, and only deviated from the acceptable window of payment when previous arrangements were made with you; hence the fact that there is no back rent due at this time. In addition, I performed countless hours of maintenance and repairs (approximately valued at $1,195), many at my own expense for supplies and all with my own time, in order to maintain the property in good working order. AC filters were bought and changed regularly; lawn maintenance was above par (mowed and edged, yard fertilized, trees fed/spiked, flowers planted, shrubbery trimmed, grass watered and kept green – I even contracted with a lawn care company for a time in order to keep the lawn healthy), lawn equipment (which came with the house) was maintained exquisitely (mower blade changed, cracked fuel tank replaced, synthetic oil used in the motor), toilet internal parts were replaced, loose towel racks repaired, shower fixtures repaired or replaced, incandescent light bulbs replaced with low wattage energy saving fluorescent (all of which I left with the house), exterminator called as needed, garbage disposal repaired, clogged toilets and sinks unclogged…and any other thing that needed attention: I took care of it all, and willingly so.
3. I am uncertain as to who is actually making the decisions and judgments that are affecting the return of my good faith deposit. Since I was given no contact information for the home owner (who I understand to be in the military) and was directed to deal with you, her proxy, I could not contact the owner directly to discuss the terms of the return of my deposit. I then requested of you, in writing, to be put in communication with the actual home owner on June 14, 2007 and received no response whatsoever to date, effectively and purposefully preventing me from contacting her. Even the handwritten letter I received on July 1 omitted any return address nor did the letter itself contain any contact information (though the postmark shows it was sent from
4. My failure to give a full thirty days notice was not done maliciously nor was it premeditated. The circumstances surrounding it were strictly financial, wherein a plan that would have allowed me to meet my financial needs was suddenly and unexpectedly disrupted. As soon as I was aware that I would not be able to pay the rent for the following month of June, I contacted you in writing to let you know and even went so far as to put the “for rent” sign out into the yard for you and repair it when the wind had knocked it down. I also answered inquiries from passers-by who were interested in the house and took them on tours of it in order to assist you in finding subsequent tenants. I acted with my, yours, and the homeowners best interest in mind and with fidelity at all times, as my deeds do attest.
5. I did everything in my power to leave the house in as good or better condition than I received it, with the one exception of repainting three or four walls back to the original color. The repainting issue was discussed with you, I informed you that I would not be able to do that, and so there was no miscommunication on that matter. Everything else, however, I did do. I cleaned the house in its entirety, including kitchen, floors, and refrigerator. I disposed of all trash in the bins and even came back after I had moved out to ensure that the bins were set on the curb to be emptied. I’m sure that there was yet more cleaning that could have been done in order to pass the white glove test, but due to my grandfather having a stroke on May 27th and me being the only family he has locally, my cleaning efforts were delayed. I informed you of that situation in writing on May 31st. The hole in the garage which you brought to my attention as not being pre-existing, I purchased the supplies for and performed the repair of. The process of repairing such a hole is two step, and since the second step of sanding and painting had to be performed after my move-out date, I informed you of that need as well and offered to return to complete it. Once I had received from you a rough list of items that you deemed as deductible from my deposit (including items deemed by the attorney general as ‘normal wear and tear’), I offered to return and take care of any of them that you wanted me to. I received no response in the affirmative or negative…there was no communication from you. Again, as with the general care of the property while living there, my intentions and mindset regarding the state of the property at the time of my departure were completely upright and with the owner’s and my best interest in mind, even informing you that I was making myself available after my departure if it was desirable for me to return and take care of any outstanding matters.
For the reasons stated above, it is my contention that withholding of the full deposit amount is absolutely unfounded, morally wrong, and is not a just reciprocation for the very good tenant I have been for the past two years. If there were legal precedent that enabled a landlord to withhold the entire amount of a tenant’s good faith deposit when no valid lease agreement is in place due solely to the lack of a full thirty day written notice to vacate, that does NOT justify the doing of it when all other factors cast the tenant in so favorable a light.
My desire at this time is to be in communication directly with the home owner herself and not her proxy, in order to come to an amicable, fair, and just agreement regarding this matter as soon as possible.
I respectfully request that this letter be immediately forwarded to [homeowner] and that she contact me at the address in this letter’s header section within ten business days, in writing, regarding a fair and equitable agreement as to what portion of the deposit should be returned to me.
PREVIOUS TENANT, [my old address]
(JULY 2005 – MAY 2007)
Understanding and being able to visualize the life-cycle of a Model-Glue event is a prerequisite to really grasping event security, so let me share my take on what a brief overview of that life-cycle is.
- A request is made, notifying model-glue to execute a specific event (eg; http://www.somesite.com/?event=fireinthehole )
- MG looks in its modelglue.xml file to find out what messages to broadcast to listening controllers ( <broadcast><message name="blowitup" /></broadcast> ) for the 'fireinthehole' event
- controllers listening for the "blowitup" message execute their corresponding functions ( <controller name="bombController" type="controller.eodGuy"><message-listener message="blowitup" function="BlowInPlace" /></controller> )
- MG executes any relevant result actions (acts as an 'if' statement almost) if they exist
- MG renders any views that are defined for this event
- The event lifecycle is over.
So, now that we know the flow of a named event within Model-Glue, it's time to add in a security check to make sure the current user has permission to execute that event. In my scenarios typically I have private and public events (those that can be executed without being logged in (such as the 'login' event itself), and those that require previous authentication (such as 'manageAccount')) and events requiring a specific role (such as viewing billing reports).
What we will effectively do is slide in some functionality between the event request and the execution of the event itself by leveraging OnRequestStart. This functionality will either allow the named event to pass on through OR redirect the user to the event we want them to arrive at. For instance, if they attempt to access an event that requires login and we intercept that event, we'll redirect them to the login page.
Here's the process for putting the named event check functionality in place:
1.Create a function that checks the current event name against a given list of event names;
2.Register that function to be called at 'onRequestStart';
3.In the Modelglue.xml file, create an event called "modelglue.OnRequestStart";
4.Within the modelglue.OnRequestStart event, register named results and provide appropriate redirection values;
What's going to happen then during the event life-cycle is that, before the actual named event executes, any controllers listening for the 'onRequestStart' message will execute their functions. One of those functions will have the sole job of verifying that the event being requested is allowed to be called in the current session state (logged in, not logged in, is an admin, etc.) If the event is good to go, the function is finished. If the event should NOT be allowed to execute, the function will set a model-glue named result. Next, since we defined an event called modelglue.OnRequestStart, that event is evaluated before any named events. The only thing we have defined for it to do is to look for specific named results and if one of them is found, perform the appropriate redirection. If none of the results being watched for are present, the named event executes normally.
I know at this point there must be a lot of questions on how to actually implement what I've been describing at a high level, so here are the same steps with snippets you can use:
1.Create a function that checks the current event name against a given list of event names;
In our scenario, let's assume two things: that we have events we want to require security, and events that we want to require that the person logged in also be an administrator.
<CFARGUMENT name="event" type="any">
<CFSET var eventname = arguments.event.getValue(arguments.event.getValue("eventValue")) />
<CFSET var user = arguments.event.getValue("currentUserObject") />
<CFIF not user.getUserID() and not listFindNoCase("login,signup,forgotpassword,sendpassword", eventname)>
<!--- if we aren't logged in AND the event we're calling is NOT a public event... --->
<CFSET arguments.event.addResult("LoginNeeded") />
<cfelse><!--- we are logged in. If this event is in our list of events requiring admin login, check to see our user is an admin. if not, redirect them to home. --->
<cfif listFindNoCase("admin.home,admin.billing,admin.creditAccount", eventname)
and not user.getRole("Admin")>
<CFSET arguments.event.addResult("AdminNeeded") />
</cfif>
</CFIF>
</CFFUNCTION>
2.Register that function to be called at 'onRequestStart';
<message-listener message="OnRequestStart" function="checkEvent" />
</controller>
3.In the Modelglue.xml file, create an event called "modelglue.OnRequestStart";
and
4.Within the modelglue.OnRequestStart event, register named results and provide appropriate redirection values;
<results>
<result name="LoginNeeded" do="login" redirect="true" />
<result name="AdminNeeded" do="home" redirect="true" />
</results>
</event-handler>
As with anything Model-Glue or OO, there's always greater levels of detail to be expounded upon, but these snippets are more for illustrative purposes than actual 'out of the box' code, so I'm leaving a lot of the details to you. Such as where you should really store your lists of named events, or how your user object is always present, empty or populated, regardless of whether or not the user is authenticated, etc.
Anyway, hope this helps get somebody over the hump!
I was once conducted through a phone interview for a technical job and, as interviews will go, I was presented several questions which were nothing more than scenarios, with my response to be the approach I would take in resolving them. One question in particular involved the situation where I was project lead and had two separate groups between which I was liaison. The two groups had opposing opinions as to what decision should be made on a particular aspect of the project. The question posed to me: "How would I resolve it"? It didn't take me long to come up with an answer, because what I imagined in the scenario looked exactly like other scenarios I had encountered in life, and I recognized the pattern: that of two (or more) people who had opposing viewpoints, with each refusing to budge from their position and yet both having the need for agreement. My answer: to make sure that both sides were heard by the opposing side.
It may seem by all outward appearances that conflicts of viewpoint are all about whose viewpoint is better, and that resolution can only be had by compromise or relinquishment of one or both views. This is not true. In scenarios between my children where I have played diplomat and in scenarios between myself and my significant other (where I WISH I had a diplomat!), I have seen the same recurring pattern, and it was never one side managing to out muscle the other side that resulted in peace and accord. The true answer in mending discord lies in something so much more simple: fulfilling the need to be heard.
I type those words slowly, I say them in my mind slowly and with reverence as I read them, because they are so fundamentally important. Hours, days, years even of stubborn silence can be avoided if the parties involved would just recognize what the true need of the opposite side is and fulfill it. The other side really only wants to "have the floor", if you will, long enough to have expressed their opinion fully and, (this is the MOST important part, O Best Beloved) to KNOW that they were truly heard. Once a person has been given opportunity to speak uninterrupted and they are made to know that their side had truly fallen on open ears and an open mind, the fire amazingly just fizzles out. They still have their opinion, of course; but the ire that drove them and their inability to see beyond their own cause just melts away, because the true root need has been satiated. Ah, and the doors to communication that are immediately opened in a nearly miraculous way! Suddenly, whereas this individual was seemingly incapable of hearing a word the other side had to say, now they can hear with clarity and attention, and truly consider their opponent's viewpoint.
Simple enough, right? Just be quiet while the other side talks! Not so, O Best Beloved. A simple stay of the tongue does not a truly hearing opponent make. You see, once one side does agree to give the other the floor and hold their tongue while the other speaks, the speaker now will be examining every minute detail of everything that is occuring while he or she is laying things out. When I say 'everything', I mean absolutely EVERYTHING. It likely won't be done consciously, but without a DOUBT they will be noting every twitch of the listener's facial expressions, the movement of their eyes, their body posture, movements, shifting of body weight, and most of all where their attention is at all times. What they are doing is looking for the one thing they need, evidence that they have been truly heard. Now, although how a person listens is vital, even more vital and necessary to this process is how the listener then responds. The very next thing that comes out of their mouth will either make the exercise a success or total failure. Remember, it isn't agreement that fulfills this pattern in human behavior, it's listening, so it isn't required that one's response be to the effect that they agree. Anything along the lines of "I can see your point", or "I hadn't looked at it like that" can suffice. Be warned though: truly listening is an impossible thing to fake. I can't tell you the innumerable times I've been involved in this scenario and the other side, though seeming to have listened and even responding with something like "I see your point", IMMEDIATELY blew the whole thing out of the water by adding on the word "but". Look at this: "I really do see your point, BUT...." What just happened there? The speaker's viewpoint was instantly invalidated and minimized. What follows the 'but' is irrelevant, and the other person isn't going to be able to hear it anyway, because that 'but' told them that their honest outpour had fallen on ears that never intended to hear them in the first place. Back to square one for everybody.
Learn to recognize the human conflict pattern. It comes in many forms and has varying degrees of intensity, but it's always the same. When you DO recognize the pattern, just remember that the true key to resolution lies in you making sure that you give your opponent the opportunity to fully express himself, you truly open your ears and mind and hear him out, and above all cause him to know that you heard him. Do NOT allow yourself to let the word 'BUT' be part of anything you respond with, or you will have exposed yourself as someone who pretended to hear but really had no interest at all, leaving your opponent's one true need yet unfulfilled. There really is something to the phrase we've heard throughout our life, that 'communication is as much listening as talking'. Practice your listening skills, thus fulfilling your opponent's true need, and you'll find yourself spending a lot less time immersed in life's daily dose of human conflicts.
Doug out.
