A recent tasks toolbar.

0 views
Skip to first unread message

Robert Rawlins

unread,
Oct 9, 2008, 7:06:15 AM10/9/08
to model...@googlegroups.com

Morning All,

 

It’s been a while since I’ve had my ColdFusion hat on but at long last I’ve got the chance to jump back into things which is an excellent feeling.

 

I have a challenge today and I’m looking for your input and suggestions on how you think this could be cleanly implemented.

 

We have here a CRM application which is built upon MG:U, CS and Transfer and allows us to manage all our prospect and current customers.

 

I’m looking to construct a ‘Recent Tasks’ type toolbar for the application which can sit in the users menu and lists a bunch of recently performed tasks so they can quick hop back to them. I’m also looking to branch this idea into a ‘Popular Tasks’ toolbar which lists tasks which they perform on a regular basis over an extended period of time.

 

The plan for the list of tasks is that it’ll slowly grow into something like this:

 

·         Added new customer.

·         Edited Customer: Robert Rawlins

·         Viewed Call Log

·         Edited Customer: John Doe

·         Sent Email To: Robert Rawlins

 

As you can see and imagine, some of these tasks are formed by a generic URL, things like ‘Viewed Call Log’ is simply a case of browsing to “?event=call.log” however, some of these tasks have more parameters which need to be taken into account, such as ‘Edited Customer: John Doe’ in which we need to pass his customer_id in the url like ‘?event=edit.customer&customer_id=657’ for instance. And also customise the text for the link to include his name.

 

I want to make this little concept as reusable as possible and I’m thinking that it would really be nice if I could form a solution which was purely based in XML and could be configured solely from model-glue.xml.

 

My current thought is to have a broadcast on any events which I want the system to ‘remember’ as a recent activity, perhaps defining it something like this:

 

<broadcasts>

      <message name="trackEvent">

            <argument name="title" value="Edited Customer: [customer_name]" />

            <argument name="viewstate_variables" value="customer_name, customer_id" />

      </message>       

</broadcasts>

 

The idea behind this is that the message handler for trackEvent compiles a key/value pair into the users session, the key being the title/linktext, which it forms by replacing the [whatever] variable in the string with the corresponding value from the viewstate

 

It then forms the value for the pair, which is the URL for the link, it does this by grabbing the current events name, and appending the viewstate_variables and their values to the URL.

 

I can’t currently foresee any problems with this approach but I wanted to open it up to you guys and get your thoughts, do you think this is the correct way to go about this? I really like the xml only implementation which would allow me to cleanly track the users recent activities and allow them to jump back to it with a single click.

 

Cheers All,

 

Rob

James Allen

unread,
Oct 9, 2008, 7:12:06 AM10/9/08
to model...@googlegroups.com

/me high fives Sir Rawlins

 

Love that idea Rob and reckon you’ve got it spot on.

 

I use something slightly similar on my question and answer website. I have a ‘rememberThis’ broadcast which stores the current URL in session scope (although encapsulated within my URLTools singleton). I can then jump back to various pages which the user needs to be able to go back to after completing an interrupt event.

 

Top idea you have there.. I might have to nick it.. ;)

 

---

James Allen

Robert Rawlins

unread,
Oct 9, 2008, 7:31:49 AM10/9/08
to model...@googlegroups.com

Haha *high fives J,

 

Good to hear from you mate, I thought you’d be lurking around here somewhere. Do you facebook?

 

Capturing the URL in its entirety is a very nice idea, it’s certainly simple, I just wonder if we’re running the risk of missing form values which might be used on that page, this is why I thought copying individual values directly from the viewstate might be good.

 

It would be nice to be able to crop out the second argument of my broadcast so I don’t have to specify the viewstate values to save though, perhaps it’s worth considering saving the entire viewstate, hmm, interesting thought.

 

It should be a droppable little module once it’s all tied up so I’ll be sure to share a copy with you.

 

Rob

James Allen

unread,
Oct 9, 2008, 7:48:14 AM10/9/08
to model...@googlegroups.com

I’ll definitely be interested in checking it out mate.

 

I like your idea of specifying event variables as that does make it more complete and like you say catches form values etc. Storing the entire values is an option but might lead to lots of extra data being stored that isn’t needed. My viewstate often contains lots of complex objects that wouldn’t need storing for instance.

Robert Rawlins

unread,
Oct 9, 2008, 7:54:26 AM10/9/08
to model...@googlegroups.com

Yeah, I think you’re quite right, the viewstate often contains all kinds of objects which were retrieved by other broadcasts on the event using the viewstate values from any submitted forms and URL’s.

 

I’m now in two minds as to whether I should perhaps make the viewstate_variables argument optional, if it is not supplied then the stored link is formed using all available form and url variables. I could presumably do this by using a structAppend(), however, does MG have any methods for getting the base viewstate, as in, just the FORM and URL variables?

 

Cheers J,

Jared Rypka-Hauer

unread,
Oct 9, 2008, 1:09:45 PM10/9/08
to model...@googlegroups.com
Rob,

As for being able to get the initial event data (i.e. form and url vars) no, MG (I don't think any of the frameworks do) doesn't have a special handler for that. At least I don't think it does... hrm, now I have to go look, damnit... one sec.

>> play Jeopardy theme song <<
>> lost 2 minutes to looking <<

Nope... form and URL are captured per-request and passed into a collection that's passed into the event and thence it makes no distinction between the original sources for the data in the Event. Sorry. This may be something useful for later, though... I'll see about it.

One quick point of clarification... "Viewstate_variables" is actually a misnomer in that the ViewState only exists in views and is an instance of modelglue.bean.GenericCollection, but within the controllers it's actually an instance of modelglue.unity.eventrequest.EventContext. Maybe call it "event_values" or even "eventKeys" or "persistStateKeys" or something? No matter, just being anal, sorry. ;)

In all honesty, since you're going to be capturing only one or two variables for this, you have a couple approaches you could take... an array of structs where each struct has a particular, consistent set of keys... an array of ActivityHistory objects with behaviors for building links, even something like session.ActivityHistoryCollection that wraps an array of ActivityHistory objects... but in any case, you're going to be way better off building this to force the use of the viewstate_values for anything that needs values from the Event... because there's a limit set of values you need from the Event and there's going to be a consistent set of them for every single task in question, it's most likely the safest and easiest way to go.

This is just a suggestion, so take it for what it's worth, but you might consider building this around ModelGlue.onRequestStart instead of every specific event you want to track. I can see the value of just putting the broadcast in each event you're going to be tracking too... this is just a bit more generic and, therefore, a tad more flexible.  You could maintain your idea if you gave it XML of the following sort:

<event-handler name="ModelGlue.onRequestStart">
<broadcasts>
<message name="trackEvent">
<argument name="event" value="customer.save" />
<argument name="title" value="Edited Customer: [customer_name]" />
<argument name="persistState" value="customer_name, customer_id" />
</message>
<message name="trackEvent">
<argument name="event" value="callLog.view" />
<argument name="title" value="Viewed Call Log" />
<argument name="persistState" value="" />
</message>
</broadcasts>
</event-handler>

This will allow you to treat your Recent Activities almost like a preprocessor plugin. The controller method would check event.getArguments("event") against event.getValue("event") on each broadcast to find out if this is something it's been told to track and if so, do it's thang... if not it would just exit. This will allow you to capture the data you want, when you want, without incurring a lot of overhead (because a cfif takes something like 3 nanoseconds to run hehe) and without repeating that XML on every event you're going to be tracking. It it also centralizes your configuration, and, potentially, gives you the option changing <argument name="event" value="event.name" /> to <argument event="events" value="event.name,otherevent.name,somefooevent.name" /> on the off chance that you want to run the same intercept/process on more than one event.


I might suggest that you actually build this as an actionpack and submit it to the project or list it on RIA forge. Actionpacks are very cool and not hard to build... you'd basically build it the way you would normally and then <include /> it into your MG app anyway. I think something like this isn't an uncommon task and is an ideal candidate for an actionpack.

Good idea, btw... I like it.

Laterz,
J

Robert Rawlins

unread,
Oct 9, 2008, 1:23:39 PM10/9/08
to model...@googlegroups.com

Jared,

 

Excellent feedback my man, thank you. I’m all for constructing this as an action pack, I’ve used a few of them in the past and understand the benefits, I’m trying to work as much of my model into little action packs as possible for my own peace of mind, I’ll post them on RIAForge as they become available, I have a few other little concepts brewing in my mind.

 

Your idea about placing this in the onRequestStart makes really great sense, like you say, centralizing it in a location make it much more manageable and easy to find, especially on a larger application it’d be bugger hunting through it all to find them. I’ll definitely take that concept into account. Is that available in MG:U? Something in the back of my brain says its only an MG:G thing?

 

So my next thought on this process is with events which negate certain other events. For instance, my user has a recent events list which contains something like this:

 

·         Viewed Customer: Robert Rawlins

 

But they then go and delete that customer! Now, if they were to click the link in the recent events list they would get an error as the customer no longer exists. Now, sure thing I could just use some tricky which notified them that the user that the customer had been deleted, but it would be SO much better to have that trace removed from the recent tasks list all together.

 

How would you go about approaching that? See if you can figure it out in less than 3 nanoseconds ;-)

 

Cheers J, I appreciate your thoughts,

 

Rob

 

From: model...@googlegroups.com [mailto:model...@googlegroups.com] On Behalf Of Jared Rypka-Hauer
Sent: 09 October 2008 18:10
To: model...@googlegroups.com
Subject: [Model-Glue] Re: A recent tasks toolbar.

 

Rob,

Chris Blackwell

unread,
Oct 9, 2008, 2:45:02 PM10/9/08
to model...@googlegroups.com
Hi guys, hope i'm not butting in ;)

using the onRequestStart method seems like a good way to centralise the tracking code, but lets say you want to track dozens of different actions, would all these broadcasts not have a performance impact on the application? i relise they would only check the event value and then return without performing any further processing, but it doesn't seem very efficient.

perhaps broadcast a single trackEvent message, which can get its config from a coldspring bean, something like..

    <event-handler name="ModelGlue.onRequestStart">
        <broadcasts>
            <message name="trackEvent" />
        </broadcasts>
    </event-handler>

    <bean id="EventTrackerConfig" class="someGenericConfigBean">
        <constructor-arg name="Events">
            <list>
                <map>
                    <entry key="event"><value>customer.save</value></entry>
                    <entry key="title"><value>Edited Customer: [customer_name]</value></entry>
                    <entry key="persistState"><value>customer_name,customer_id</value></entry>
                </map>
            </list>
        </constructor-arg>

That would then a be a single broadcast, not one for every event that needed tracking. The EmailService actionpack provides its config via ColdSpring, so i'm guessing this is an ok way to structure an actionpack

Cheers, Chris


2008/10/9 Robert Rawlins <robert....@thinkbluemedia.co.uk>

Jared,

 

Excellent feedback my man, thank you. I'm all for constructing this as an action pack, I've used a few of them in the past and understand the benefits, I'm trying to work as much of my model into little action packs as possible for my own peace of mind, I'll post them on RIAForge as they become available, I have a few other little concepts brewing in my mind.

 

Your idea about placing this in the onRequestStart makes really great sense, like you say, centralizing it in a location make it much more manageable and easy to find, especially on a larger application it'd be bugger hunting through it all to find them. I'll definitely take that concept into account. Is that available in MG:U? Something in the back of my brain says its only an MG:G thing?

 

So my next thought on this process is with events which negate certain other events. For instance, my user has a recent events list which contains something like this:

 

·         Viewed Customer: Robert Rawlins

 

But they then go and delete that customer! Now, if they were to click the link in the recent events list they would get an error as the customer no longer exists. Now, sure thing I could just use some tricky which notified them that the user that the customer had been deleted, but it would be SO much better to have that trace removed from the recent tasks list all together.

 

How would you go about approaching that? See if you can figure it out in less than 3 nanoseconds ;-)

 

Cheers J, I appreciate your thoughts,

 

Rob

 

From: model...@googlegroups.com [mailto:model...@googlegroups.com] On Behalf Of Jared Rypka-Hauer


Sent: 09 October 2008 18:10

To: model...@googlegroups.com
Subject: [Model-Glue] Re: A recent tasks toolbar.

 

Rob,

Robert Rawlins

unread,
Oct 10, 2008, 6:00:38 AM10/10/08
to model...@googlegroups.com

Chris,

 

That’s a very interesting point. You’re right in that with a large number of tracked events it might become a little intensive for the framework, whilst I don’t quite know ‘how’ intensive, it’s probably worth taking your idea into consideration.

 

This is a similar method to that used in EventGuard which uses a ColdSpring configuration to list events which need guarding, then the onRequestStart  method checks those events.

 

Nice concept, I’ll be sure to look at implementing something like this.

 

Rob

Jared Rypka-Hauer

unread,
Oct 10, 2008, 12:17:15 PM10/10/08
to model...@googlegroups.com
You're not butting in at all. It's an open list... go for it.

I like that it's a bit more elegant in that it runs once to check the
current event against a list of pre-configured events... it's
certainly more elegant than my brute-force approach hehe. Considering
the limitations of MG's XML (like the fact that arguments can only be
simple values), I was going by Robert's explicit goal to configure
this thru ModelGlue.xml exclusively.

You could use ColdSpring's XML to create a map (a struct, really) of
events with other maps (aka structs) of values assosciated with them,
you'd have to do something like this in your controller/service/
configuration bean rig, probably either in a service (if you used
one) or in the controller (if you had no service class):

In the config bean:

<cffunction name="eventIsConfigured">
<cfargument name="eventName" />
<cfreturn structKeyExists
(variables.configuredEvents,arguments.eventName) />
</cffunction>

In the controller:
<cffunction name="trackEvent">
<cfargument name="event" />
<cfif EventTrackerConfig.eventIsConfigured(event.getValue("event"))>
...do stuff...
</cfif>
</cffuntction>

The more I think about this, though, the more I like the idea of
actually creating micro-framework to handle this, including a class
to encapsulate each event you want to configure to listen for and a
wrapper to hold a collection of the event-based classes. So you have
an EventTrackerContext that wraps each set of event, title, and
persistState values:

<bean id="trackCustomerSaveEvent"
class="com.rawlins.eventtracker.EventTrackerContext">
<property name="eventName" value="customer.save" />
<property name="title" value="Saved customer record: [customer
name]" />
<property name="persistEventState" value="eventKey1,eventKey2" />
</bean>
<bean id="trackCustomerWibbleEvent"
class="com.rawlins.eventtracker.EventTrackerContext">
<property name="eventName" value="customer.save" />
<property name="title" value="Saved customer record: [customer
name]" />
<property name="persistEventState" value="eventKey1,eventKey2" />
</bean>
<bean id="trackViewCallLog"
class="com.rawlins.eventtracker.EventTrackerContext">
<property name="eventName" value="customer.save" />
<property name="title" value="Saved customer record: [customer
name]" />
<property name="persistEventState" value="eventKey1,eventKey2" />
</bean>

<bean id="EventTrackerConfig"
class="com.rawlins.eventtracker.EventTrackerConfig">
<property name="configuredEvents">
<map>
<entry key="customer.save"><ref bean="trackCustomerSaveEvent" /></
entry>
<entry key="customer.wibble"><ref bean="trackCustomerSaveEvent" /
></entry>
<entry key="callLog.view"><ref bean="trackCustomerSaveEvent" /></
entry>
</map>
</property>
</bean>

Having a wrapper class for the events you want to track works out
really well because then you can have logic at every level of the
stack. Then again I love building libraries, so what can I say? ;)

The thing I like about this being made into a framework/library/
actionpack kinda thing is that it can be used with MG, or ColdBox or
Fusebox OR Mach-II... being able to use the actionpack immediately
with MG would be cool but being able to use the underlying library
with the other frameworks would be cooler still.

In any case... I think we're getting somewhere with this...

Laterz,
J

On Oct 9, 2008, at 1:45 PM, Chris Blackwell wrote:

> Hi guys, hope i'm not butting in ;)
>
> using the onRequestStart method seems like a good way to centralise
> the tracking code, but lets say you want to track dozens of
> different actions, would all these broadcasts not have a
> performance impact on the application? i relise they would only
> check the event value and then return without performing any
> further processing, but it doesn't seem very efficient.
>
> perhaps broadcast a single trackEvent message, which can get its

> config from a coldspring bean, something like.. ...

James Allen

unread,
Oct 10, 2008, 12:37:57 PM10/10/08
to model...@googlegroups.com
I'm starting to get abit lost now (all this talk about context's. :) ) but I
do like the sounds of where this is going, especially the library /
cross-framework elements.

Think I'd need to see it in action to fully 'get it', but definitely
something to think about.

I like how this discussion has got me thinking of different ways to approach
things in MG.. Superb.

---
James Allen

-----Original Message-----
From: model...@googlegroups.com [mailto:model...@googlegroups.com] On
Behalf Of Jared Rypka-Hauer
Sent: 10 October 2008 17:17
To: model...@googlegroups.com
Subject: [Model-Glue] Re: A recent tasks toolbar.

Jared Rypka-Hauer

unread,
Oct 10, 2008, 12:55:21 PM10/10/08
to model...@googlegroups.com
Maybe "Context" is the wrong term, but basically it is just a CFC
that wraps the struct that contains the info about a specific
event... the various attributes it needs (like event name, event keys
to use and the title to display in the HTML) to do its job.

Think of it this way: when you are building controller methods in MG,
the Event object that's available in all of them is actually an
instance of modelglue.unity.eventrequest.EventContext. It contains
all the arguments, results, and event values that are passed into the
controller with methods to access and manipulate them.

Check it out as an example. Since an EventTrackerContext would have
to contain a collection of values like name, trigger event, event
keys to use and title, along with possibly other things like a DAO to
access the DB to save EventTracker's data to the DB, and any other
number of other important items, it seemed to me like a context
object. You could just as easily implement the Command Pattern here
and have each one have an execute() method to do the job of tracking
the access to the event.

;)

Anyway... glad this convo is working out for you. I'm enjoying it too.

J

Robert Rawlins

unread,
Oct 13, 2008, 3:48:57 AM10/13/08
to model...@googlegroups.com
Good morning chaps!

Ah, tell you what, this whole conversation has reinvigorated my brain, I've
been stuck in the bloody Python community for the past couple of months and
you just don't get these types of bonding sessions over there, I've missed
you guys.

Jared, this concept of a little sub-framework/library with its own
non-framework specific configuration file is certainly an attractive
concept. And in fact, the more I think about it the more I like that
detachment from the framework, it would be nice not having to clutter up my
ColdSpring or MG xml files with this kind of work. (I might even move my
event guard config into its own definition on the back of the same principle
:-).

I like the idea of perhaps using some form of 'handler' objects in order to
store the tracked data into different locations, effectively growing the
concept into a bit of an audit trail, so you can log the data to files,
database, session or any number of locations. I don't know, perhaps that's
too much? Heh, ideas are racing through my mind.

Jared, did you see my pervious thread comment about exemption rules? Where
by one event means another event should not be displayed/tracked?

What would be your approach on that challenge?

Cheers,

Rob

Reply all
Reply to author
Forward
0 new messages