Использовать несколько шаблонов / скинов GSP с плагином для нескольких арендаторов Grails?

У меня есть приложение, которое использует плагин для нескольких арендаторов Grails для размещения нескольких версий сайта. Я хочу иметь возможность создавать собственные 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 и контроллерах. (с последующим ощущением "не могу... получить... чистоту")

Мне не удалось найти существующий плагин, который обеспечивает эту функциональность. Кажется, что другие варианты, которые я рассмотрел, включают в себя настройку довольно основных частей grails (тег Render, движок шаблонов и т. Д.), Что заставляет меня нервничать.

Я понимаю, что это довольно широкий вопрос; и конкретные решения и общие предложения приветствуются.

РЕДАКТИРОВАТЬ:

Я нашел другой возможный подход, создав плагин и метапрограммирование на новый метод:

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.view а также args.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 вместо того, чтобы объединить все шаблоны GPS вместе под views/$template/controller/action, Я думаю, что могу жить с этим сейчас.

2 ответа

Решение

Решение, которое я использовал:

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

Другие вопросы по тегам