У меня есть приложение, которое использует плагин multi-tenant core 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, движок шаблонов и т. Д.), Что заставляет меня нервничать.
Я понимаю, что это довольно широкий вопрос; приветствуются как конкретные решения, так и общие предложения.
EDIT:
Я нашел другой возможный подход, создав плагин и метапрограммирование по новому методу:
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
вместо объединения всех шаблонов gsps вместе в views/$template/controller/action
. Я думаю, что могу жить с этим сейчас.