RE: [coldbox:17170] Why don't modules support "external locations"?


That is an extension point for where the primary application can find the modules, but it does not allow (so far as I can tell) a way to set up an “viewsExternalLocation” value for the module.

When the module looks for a handler, it only looks in its local handlers folder (which could be redefined as a convention, but not extended with an “external” location).

When the module looks for a view/layout, it goes through the discovery process as outlined in the docs, but that discovery process only includes the base convention locations and not the “external” locations.

Does that make sense?

– Jeff

Correct me if I am wrong but views if not found in the Module, will default back to non module conventions. So if it is not in the views/module/ location, then it should be in the externalviews/module/ location.

Jeff – Modules do allow external Locations (for views/layouts), but not for handlers.

You have to enable viewParentLookup and layoutParentLookup.

Then, in your viewsExternalLocation, you have:







However, one thing to note – this only works with event.setView. renderView(name=”myview”,module=”mymodule”) is seen as implicit – and therefore won’t check the parent locations. I didn’t like that, so I changed the core to use the conventions instead.



I think if layoutParentLookup is set to true it tries, in order, 1.parent externalLayoutLocation, 2.parent Layoutlocation, 3.module layoutLocation….


The whole “discovery” process in the locateModuleView and locateModuleLayout methods in the Renderer do look at the default convention locations for layouts and views. They do not look at the external locations however.

As Tom mentioned, there also appears to be no capability for an external handler or external plugin path in the evaluation path.

– Jeff

Ok I took a guess on that as it has been a long time since I played with the renderer plugin.

They do not look at the external locations however.

They do, I use it all the time. The only time they don’t is when you implicitly render a module view using renderView().

You can remove this behaviour easily, by simply commenting out explicitModule = true; on line 113 of the Render.cfc plugin in the core: (Line 113).

Then it will work as (what I think is expected – i.e obey the parent module conventions).

As for handlers – I have adjusted this too by adjusting the HandlerService.cfc to use the module convention “handlerParentLookup”. I can share this code with you if you want.




Maybe I am missing something here, but in the locateModuleLayout method in the version of the Renderer.cfc you referenced (line 508), the paths defined only use the base layout convention value. They do not check the external layout path.

Just above this on line 499 in the standard locateLayout method, it has a check in place to see if the reference layout does not exist in the convention path, but it does exist in the external path, then use the external path. This check is not in place for the locateModuleLayout path - it only returns the convention path so I don’t know how you are getting the external path without customizing this.

Am I missing something?

– Jeff

I see what you’re saying, but I can’t see a reason why I’d ever want that.

It sounds to me like you’ve got your external locations and conventions the wrong way around.

“ So, if the parent application has external paths defined (i.e. a template or skin), then those paths are not used for modules. “

Can you copy and paste your externalLocations and conventions?

A template or skin should be in the conventions, not the external locations – that’s where the core app goes. I have to admit, when I started customising I fell into exactly the same trap as you….


I think we are talking the same thing. My Coldbox.cfc looks like this for the conventions …

// coldbox directives coldbox = {
// “external” locations have to be the common/base code
handlersExternalLocation = “app.www.handlers”,
layoutsExternalLocation = “/app/www/layouts”,
modulesExternalLocation = [ “templates/test/modules”, “/app/www/modules” ],
pluginsExternalLocation = “app.www.plugins”,
viewsExternalLocation = “/app/www/views”

// base conventions are the overloaded template settings
conventions = {
handlersLocation = “templates/test/handlers”,
layoutsLocation = “templates/test/layouts”,
pluginsLocation = “templates/test/plugins”,
viewsLocation = “templates/test/views”

So the “external” locations actually point to the base files in the parent application. The convention locations point to the template. The key here is a view (handler, layout, plugin) may or may not exist in the template. It will only exist there if we want to override the base one. So, the check must be in place (and it is for the parent app) to see if the view exists in the template location before checking the base convention location before throwing an error.

This all works just fine for the parent application. It is within the modules where this breaks down. I have rewritten the locateModuleView and locateModuleLayout methods in the Renderer to check the external location like the non-module methods do, but this does not address the same (mis)behavior for the handlers, plugins, or other defined external locations.

– Jeff

That looks correct to me.

So what you’re trying to do is over-ride the module layout with the external layout location, not the template layout? I guess I’ve never wanted to do that. I can see what you’re saying though – especially if you’re using third party modules – you want to over-ride the module layout, and then possibly over-ride that yet again with a client specific layout….


Yes. The core application is a SAS style application and will be made up of multiple modules. The end goal here is to have the views/layouts at a minimum be able to be overridden for the entire application - modules included. Ideally, I would like to have the flexibility to override a handler or plugin because you know the requirements are always going to creep, but the views/layouts were the most important part.

I was(am) just confused as to why the module, when billed as a Coldbox app in its own, does not support the external locations that a parent Coldbox app would. I did not know if there was a reason for this or whether it had just never been thought of or used.

– Jeff