This is a repost from my post over at GroovyMag.
I’m planning to do this on a couple other projects, and thought I’d share this with you. It’s not specifically something related to GroovyMag magazine, but is a technique I’m using in a Grails project to make it easy to switch layouts based on the user.
All the default generated views use the meta tag ‘layout’ to point to a GSP file which will get merged with your view file (using sitemesh) to generate the final rendered HTML. Unfortunately these are always hardcoded to ‘main’ rather than a configurable option. So, I’ve taken to modifying the templates used during generation to point to a configuration value.
Run “grails install-templates” then look in src/templates/scaffolding. You’ll see the create/edit/list/show GSP files which are used as the basis for generation.
Edit the files such that the meta line
<meta name=”layout” content=”main” />
looks like this
<meta name=”layout” content=”\${session?.layout?:grailsApplication.config.sitemesh.layout}” />
(Note the Elvis
operator above)
Then edit your grails-app/conf/Config.groovy file to have a line that reads
sitemesh.layout = “main”
If you then generate views for an app, everything will continue to work as it normally does by default; specifically, all the views will use the grails-app/views/layouts/main.gsp as a sitemesh wrapper for rendering.
However, you’re now able to set the layout to something different for each user by setting the session.layout value.
If a user has authenticated and is an administrator, perhaps you’d want to have
session.layout = “admin”
in a controller action, which would use the grails-app/views/layouts/admin.gsp file for rendering views for an admin.
If views for entire controllers require a different view, you should be able to set the global config value for all users by doing something like
grailsApplication.config.sitemesh.layout = “layout2″
in a controller action.
Hi there,
I’m trying to solve an similar problem and would be interested you’re views:
My app needs to support multiple customers at run time. They will all be displaying the same data, using the same controllers, services but with different layouts, css and images.
My basic idea is as follows:
1) Have a filter to authenticate and set a config value in the session
2) My controller code will call something along these lines:
render(view:”/${session.userContext}/test/test”)
So for customer1 we will render: views/customer1/test/test.gsp
Ideally I would like to have the all custom views and layouts in the /views/customer1/ directory but site mesh looks for layouts in the /views/layouts directory and I can’t find a way around this. In this sense the solution is a wee bit clunky as I don’t get the level of isolation I want.
What would be ideal is if I could set the root of the grails render method at runtime e.g. instead of starting at views, look in views/customer1 alas I have no idea how to do this but his seems like the smartest solution.
Cheers,
Gav
I’m not sure how you’d do that either.
Having ‘layouts’ be in ‘views’ is, imo, clunky at best. They are view technology, but they are not ‘views’ themselves – they serve a specific purpose, and should be in a different directory (again, imo).
If your controller’s *views* are the same, and you’re only changing layouts/css (and perhaps images, if controlled by css), then the technique I outlined should work well enough.
I don’t know if there’s a ‘pre-view-render’ hook that could be tied in to to modify the view at runtime, but without having to hardcode it in to the controller. If so, that’d be ideal. I suspect there’s not – I seem to remember looking for something recently like that that didn’t exist.
Have you asked on the mailing list yet?
Hey there,
Been doing a bit of asking around and agree with what you’re saying. The whole solution just smelt a bit funny!
Anyway, I’ve been directed to the following plug-in: http://www.grails.org/plugin/multi-tenant and been informed that I can create a subclass of GrailsLayoutDecoratorMapper so this gives me something to go on. I have a feeling that the second solution will be of more use since the application I am writing is a data warehousing application.
Cheers,
Gav