Elliott's Blog | Life Through Math, Algorithms and Code

Jul/07

17

Getting the Expected Results for GetCurrentTemplatePath() in a Custom Tag.

While working on the template system used for the conference websites I ran across a problem where I needed the path to the template that called a custom tag. The first thing I tried was getCurrentTemplatePath() thinking that it might return that since the documentation makes no mention of custom tags. Instead, however, the function returns the path to the custom tag itself.

Ben Nadel noticed some of this odd behavior as well.

I spent a long time trying to figure out how to get the caller template path, including what Ben did which was to add a special function to the caller scope.

<cfscript>
function getCallerTemplatePath() {
    return getCurrentTemplatePath();
}
caller.getCallerTemplatePath = getCallerTemplatePath;
path = caller.getCallerTemplatePath();
</cfscript>

This doesn’t work though. Instead I still got the template path of the custom tag!

I dug around in the PageContext (which is returned from getPageContext() if you’re not familiar) with no luck and finally gave up resorting to this…

/**
    Monumental hack, but the only way I could figure out how to do 
    a getCurrentTemplatePath() like call that resolves to the page 
    that called this custom tag.
*/
function getCallerTemplatePath() {
    try {
       error;
    } catch( any cfcatch ) {
        return cfcatch.tagContext[3].template;
    }
}

Which worked but really felt like a hack since it means throwing an exception on every request. So I kept an eye out as I dug around in the internals of the CF engine for various other things, and today I was rewarded with an awesome solution.

/** Gets the path to the page that called this custom tag. */
function getCallerTemplatePath() {
    var field = getMetaData(caller).getDeclaredField("pageContext");
    field.setAccessible(true);
    return field.get(caller).getPage().getCurrentTemplatePath();
}

Now to get at why and how this kind of thing works…

Inside the ColdFusion runtime the foundation unit for all scripts, components and tags is the coldfusion.runtime.CFPage object, and the getCurrentTemplatePath() function is really identical to…

function getCurrentTemplatePath() {
    return getPageContext().getPage().getCurrentTemplatePath();
}

After realizing this it dawned on me that the custom tags, cfcs, and pages all have their own PageContext and Page objects, and as such the template path is going to be different, or rather bound, to the page in which the function is called from, not where it’s defined.

Knowing this I was able to grab the page context out of the caller scope, which is the page context of the caller, and not the current page, and use that to get the current template path of the page for which that page context operates.

Also, for those who aren’t familiar, the getMetaData() function can be used to return the java.lang.Class instance for most objects you wouldn’t normally be able to call getClass() on in ColdFusion. For instance you can call getMetaData(variables).getName() and you’ll get coldfusion.runtime.VariableScope.

Doing this really made my code feel less icky, so I hope this is useful to someone else.

(PS, Tested and works on CF6+ and CF7+, anyone have CF8?)

No tags

9 comments

  • Ben Nadel · July 17, 2007 at 5:56 pm

    Elliott,

    This is cool stuff. Just the other day, I also saw someone mess around with the … oh wait, I just checked and that was YOU also :) Dang, you really get this PageContext structure.

    Reply

  • Ben Nadel · January 10, 2008 at 9:05 am

    Elliott,

    I know we haven’t touched on this in a while, but someone asked me a related question. With a custom tag, you can easily grab the context of the calling template using the CALLER scope. However, do you know if there is anything in the page context that gives that information such that it can be used from template to template without custom tags?

    Is there any sense of “caller” stored in the page context itself?

    Reply

  • Author comment by Elliott · January 11, 2008 at 6:22 am

    Ben,
    To be honest I’m not sure. I assume you mean like if you cfinclude the other template? Or do you mean cfcs?

    I’ll dig around in there to see where that information comes from.

    Reply

  • Ben Nadel · April 24, 2008 at 9:50 am

    @Elliot,

    Any ideas on how to get the Page object of the calling template from within a CFC? I am trying to get at the internal functions, which are available via:

    GetPageContext().GetPage()

    Ex.

    GetPageContext().GetPage().ImageBlur()

    However, these seem to only be available from a CFM page, not a CFC. In the CFC, the GetPage() object does not have these built-in CF functions.

    I need to access “ImageBlur()” in a dynamic way from within a CFC (otherwise I would just call the built-in method directly).

    Thanks.

    Reply

  • Vid · August 21, 2008 at 10:43 pm

    Very nice.
    It works in CF8 on IIS too.
    I’ve had to rely on CGI.PATH_TRANSLATED in the past.

    Reply

  • Gary Gilbert · November 21, 2008 at 8:37 am

    I blogged about something similar to this back in February but dealing with functions in components calling a function in another component.

    http://www.garyrgilbert.com/blog/index.cfm/2008/2/4/What-Function-Called-My-Function

    But your solution is a lot more elegant and you don’t need to artificially throw an error to get it.

    Reply

  • Ben Nadel · January 14, 2010 at 11:08 am

    Elliott, I know it’s been like 2 years since this blog post; but, I just used it in a new and (what I think) exciting way:

    http://www.bennadel.com/blog/1813-Invoking-A-ColdFusion-Function-With-A-Closure-Like-CALLER-Based-Variable-Binding.htm

    Reply

  • Steve Bryant · January 15, 2010 at 5:23 pm

    This is really nice! This solves a small issue that I have been having for some time.

    Thanks Elliott (and Ben for linking to it)!

    Reply

  • Tweets that mention Getting the Expected Results for GetCurrentTemplatePath() in a Custom Tag. - Elliott's Development Blog -- Topsy.com · April 27, 2010 at 10:37 pm

    […] This post was mentioned on Twitter by aanooj. aanooj said: @aanooj Getting the Expected Results for GetCurrentTemplatePath() in a Custom Tag. – Elliott's Development Blog: http://bit.ly/cmPArO […]

    Reply

<<

>>

Theme Design by devolux.nh2.me