简体   繁体   中英

Using multiple GSP templates/skins with Grails multi-tenant-core plug-in?

I have an application that uses the grails multi-tenant-core plug-in to host multiple versions of a site. I want to be able to create custom GSPs for each site beyond what simple skinning will allow me to do. Essentially, I want to be able to have my grails-app/views folder look something like:

views
|
|__template1
|  |
|  |__layouts
|  |  |
|  |  |__main.gsp
|  |  
|  |__controller1
|     |    
|     |__index.gsp
|
|__template 2
   |
   |__layouts
   |  |
   |  |__main.gsp
   |  
   |__controller1
      |    
      |__index.gsp

And then configure a specific tenant to use a specific set of GSPs. I am using a DNS resolver that's persisted in my database, so I could presumably add a property to the DomainTenantMap domain class that assigns a templateDir property to a particular tenant and sprinkle conditional logic all over the place in my GSPs and controllers. (followed by that 'can't...get...clean' feeling)

I haven't had any success finding an existing plug-in that provides this functionality. The other options I have considered seem to involve customizing fairly core pieces of grails (render tag, template engine, etc.) which makes me nervous.

I realize this is a fairly broad question; both specific solutions and general suggestions are appreciated.

EDIT:

I found another possible approach by creating a plug-in and meta-programming on a new method:

def configureTemplateRenderer(application, applicationContext) {
    for (controllerClass in application.controllerClasses) {

        controllerClass.metaClass.newRender = { args ->
            println 'something'
            if(args.view) {
                args.view = "/somedir/${args.view}"
            }
            if(args.template) {
                args.template = "/somedir/${args.template}"
            }
            delegate.render(args)
        }
    }
}

This is just a proof of concept to see if I can invoke the standard render method through my new one (I can). Ideally, I could override the render method entirely to modify the args.view and args.template property based on some sort of tenant/template mapping (not shown). However, I haven't been able to override the render method successfully so this solution is really only marginally better than just sprinkling in some path variable to calls to render .

A Solution!

I ended up creating a separate plug-in that essentially boils down to overriding the render method with one that checks a tenant/template map. Still testing but so far it looks promising, here's the gist of it:

def overrideRender = { application ->
    for (controllerClass in application.controllerClasses) {
        def original = controllerClass.metaClass.getMetaMethod("render", [Map] as Class[])
        def originalRender = original.getClosure()

        controllerClass.metaClass.originalRender = originalRender

        controllerClass.metaClass.render = { Map atts ->
            def templatePath = // some code to lookup against a TenantTenantMap
            if(templatePath) {
                if(atts.view) {
                    atts.view = "${templatePath}${atts?.view}"
                }
                if(atts.template) {
                    atts.template = "${templatePath}${atts?.template}"
                }
            }
            delegate.originalRender(atts)
        }
    }
}

The only downside is that I have to use a slightly uglier directory structure than I wanted: views/controller/$template/action instead of combining all the template gsps together under views/$template/controller/action . I think I can live with that for now.

The solution I used:

def overrideRender = { application ->
    for (controllerClass in application.controllerClasses) {
        def original = controllerClass.metaClass.getMetaMethod("render", [Map] as Class[])
        def originalRender = original.getClosure()

        controllerClass.metaClass.originalRender = originalRender

        controllerClass.metaClass.render = { Map atts ->
            def templatePath = // some code to lookup against a TenantTenantMap
            if(templatePath) {
                if(atts.view) {
                    atts.view = "${templatePath}${atts?.view}"
                }
                if(atts.template) {
                    atts.template = "${templatePath}${atts?.template}"
                }
            }
            delegate.originalRender(atts)
        }
    }
}

您可以尝试实现自己的Spring ViewResolver: http ://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/web/servlet/ViewResolver.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM