Overriding Views/Layouts for a Module

I am working on a SAS style hosted application and I need to have the ability for different “sites” within the application to have a different UI design. So, what I want is to have a common “template” folder structure which could contain optional views and layouts that would override views or layouts in modules or the core application. In digging through the documentation for modules, I think what I need is an extra step in the layout/view discovery functionality as documented for modules. Is there a way to alter this discovery process?

For example, if I had a “templates” folder structure like this …

/templates

/templateA

/html

/com_contact <== this would be a module name

/layouts

/views

/templateB

/html

/com_users <== this would be a different module name that is overridden for this template

/layouts

/views

SiteA would use TemplateA and SiteB would use TemplateB. When rendering an event, the logic would need to determine which template the current site is using, then check for an overridden view/layout in the corresponding template, then follow the standard layout/view discovery routes if none was discovered.

Does that make sense? Do you have any suggestions on how to add this extra path to the discovery functionality?

TIA!

Yes, a simple skin system will work.

Before modules came along I would hook into the preEvent interception, and change the convention for views / layouts etc. Now the tricky part is how do you switch, when you designed this and I am guessing you have this already in place, so you can then modify the info on the fly based on that.

Now I side bonus to this is that you can then have core code, and user specific as well. Which means your handlers / models / services can just extend the core to provide the same or override the functionality based on your needs.

Andrew - You said “before modules came along …”. So does that mean modules would change your approach to this?

The layout/view discovery sounds like exactly what I need, but I just need to add in another place for the process to look along the way and I am still trying to figure out how to do that cleanly.

Or, is there another suggestion?

Thanks!

Continuing my investigation here … in the ColdBox Renderer.cfc, it appears the 4 possible paths for views and layouts are hard coded into the locateView, locateModuleView, locateLayout, and locateModuleLayout methods. So, without completely overriding these methods, it does not appear to be possible to add another path here to search.

I am by no means a ColdBox expert, so am I going down the wrong path here? Is there a simpler way of defining an optional view/layout location for an “application skin”?

Thanks!

I think it all depends on your finally outcome, and goals.

Yes, I did use modules by reference, I just haven’t seen a need at the moment to do this with them…

Yet…

But the trick should still work with modules, as each module is bound by the conventions, so changing the view dynamically would still apply.

No its possible, let me see if I have the code handy for you.

Sorry Andrew, I am having a hard time following you here.

The requirement here is to allow a non-CF / very junior CF developer to create a set of HTML files in a defined folder structure (a “skin”) which would override the default views/layouts for various parts of the application. If you know the Joomla CMS at all, the template functionality is what I need to replicate with this application.

Given that these “skins” need to be somewhat removed from the core code / conventions of the application, I was expecting to need a new custom view path. Are you saying that that should be able to be handled using an interceptor for the “preEvent” broadcast?

Thanks!

Yes I follow you, but like I stated very clearly all you need to do is change the conventions on the fly.

I dug up an interceptor that I used, it changed this based on the preEvent interception point.

controller.setSetting(‘ViewsExternalLocation’, ‘/#instance.skinPath#/#instance.skinName#/Views’);
controller.setSetting(‘LayoutsExternalLocation’, ‘/#instance.skinPath#/#instance.skinName#/Layouts’);

Modules is a little different as you need to get the module, then get the convention and change it.

If you look at the code it is actually that simple, because if you read the code in the renderer, they pull the conventions path out and use each and every time, so manipulating when you need too is as simple as described.

now how you manage the information to be able to change and what too is going to be what you have in place, or what will fit into what you have already.

Andrew - Thanks, I am slowly wrapping my head around this. I was able to get the custom “template” views and layouts working at the primary application level based upon the snippet you provided and that was pretty simple.

The modules on the other hand are not being quite that trivial. In the ModuleConfig.cfc, I added a preEvent() method with the following code:

if( directoryExists( expandPath( ‘#templateSettings.basePath#/#siteSettings.templateName#/html/helloWorld’ ) ) ){
controller.setSetting( ‘viewsLocation’, ‘#templateSettings.basePath#/#siteSettings.templateName#/html/helloWorld/views’ );
controller.setSetting( ‘layoutsLocation’, ‘#templateSettings.basePath#/#siteSettings.templateName#/html/helloWorld/layouts’ );
}

I have traced the code, the paths exist and the settings are set into the controller. However, once it gets to the locateModuleView() method in the Renderer, these settings have reverted back to the defaults. I am still tracing, but can anybody point out what I am missing here?

Also, is “preEvent” the best place for this? I am going to have different requests using different templates, so I need to make sure this check is low enough in the process chain to account for that.

Thanks!

Module Config is not an interceptor, so you will need to create a core interceptor or module specific. From memory, I can’t seem to find how I did eventually get it to work with modules, which I am sure I posted here in the group at one time.

But it is along the lines of getSetting(“modules”); and then something like modules['moudleName"] will get all the related module information.

But I am glad you got it up and running to some degree, just need to sort the ModuleConfig.cfc problem out, as this is a configuration component it will not listen or be registered as an interceptor until you tell the module to register its own interceptor.

Hope that helps some more.