使用多个GSP模板/皮肤和Grails多租户核心插件



我有一个应用程序,它使用grails multi-tenant-core插件来托管一个站点的多个版本。我希望能够为每个站点创建自定义gsp,而不仅仅是简单的皮肤将允许我这样做。本质上,我想让我的grails-app/views文件夹看起来像这样:

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

然后配置一个特定的租户来使用一组特定的gsp。我正在使用一个持久化在我的数据库中的DNS解析器,因此我可以向DomainTenantMap域类添加一个属性,该属性将templateDir属性分配给一个特定的租户,并在我的gsp和控制器中到处散布条件逻辑。(后面跟着can't…get…清洁的感觉)

我还没有找到任何提供此功能的现有插件。我考虑过的其他选项似乎涉及到自定义grails的核心部分(渲染标记、模板引擎等),这让我很紧张。

我知道这是一个相当广泛的问题;不管是具体的解决方案还是一般性的建议,我们都非常欢迎。

编辑:

我发现了另一种可能的方法,即在一个新方法上创建一个插件和元编程:

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)
        }
    }
}

这只是一个概念证明,看看我是否可以通过我的新方法调用标准的render方法(我可以)。理想情况下,我可以完全覆盖render方法,以基于某种租户/模板映射(未显示)修改args.viewargs.template属性。然而,我还没有能够成功地覆盖render方法,所以这个解决方案实际上只是比仅仅在一些路径变量中点缀render调用好一点点。

解决方案!

我最终创建了一个单独的插件,本质上归结为用一个检查租户/模板映射的插件重写呈现方法。仍在测试中,但到目前为止看起来很有希望,以下是它的要点:

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)
        }
    }
}

唯一的缺点是我必须使用一个比我想要的稍微难看的目录结构:views/controller/$template/action,而不是在views/$template/controller/action下将所有模板gsps组合在一起。我想我现在可以接受了。

我使用的解决方案:

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

最新更新