2

我正在一个 Grails 站点上工作,尽管它需要包含许多类似 CMS 的功能,但尚未(由于各种原因)作为实际 CMS 安装的一部分构建。我需要允许用户做的一件事是编辑 HTML 布局——在 Grails 应用程序中,布局在 GSP 文件中——以及不同页面的其他通常静态元素,它们都不会被绑定到任何特定的域对象,也不构成处理域对象的 CRUD 页面之一的任何部分。

我知道既可以指定视图目录,也可以从应用程序的某处写入正在运行的应用程序中的实际文件。我进一步知道,如果我要这样做,可能需要非常小心,以确保用户不会严重破坏页面的整个布局和内容,我已准备好处理这个问题。但是是否可以允许用户编辑用于创建 Grails 页面的布局和其他 GSP 文件?如果是这样,我该怎么做?

4

5 回答 5

3

如果您将模板/布局存储在数据库中(正如上面的一些建议),您可以使用 GroovyPagesTemplateEngine 呈现它:

package com.mycompany

import groovy.text.Template;
import org.codehaus.groovy.grails.web.pages.GroovyPagesTemplateEngine 

class TemplatingService {
  GroovyPagesTemplateEngine groovyPagesTemplateEngine

  def render(String templateString, Map model) {
    ByteArrayInputStream bais = new ByteArrayInputStream(templateString.getBytes());
    Template template = groovyPagesTemplateEngine.createTemplate(bais)

    try {
      def writer = new StringWriter()
      template.make(model).writeTo(writer)
      return writer.toString()
    } catch (Exception e) {
      // handle exception
      return ""    // or "[an error occurred]" or rethrow the exception
    }
  }
}

现在,虽然您可以这样做,但请务必考虑其中的含义。您可以在 gsp 中执行的任何操作都可以在此上下文中执行。例如写入或删除文件、执行命令等。

我从来没有想过如何利用 SecurityManager 来限制模板中可以做的事情,所以我的解决方案是不允许用户将模板输入数据库。如果我想要一个新模板,我会在检查安全隐患后自己把它放在那里。

您还可以通过缓存结果来提高性能,createTemplate()以避免每次都重新编译。

于 2013-01-25T15:53:18.863 回答
2

一种简单的方法是将 HTML 块作为域对象存储在数据库中。然后,您的视图可以自动拉入 html。可以创建一个简单的界面来允许用户编辑 html。有关此问题的更多讨论,请参阅此线程另一个可能的选择是使用Weceem 插件

于 2013-01-22T20:41:12.333 回答
1

布局也是一个 gsp 视图,根据定义,它负责处理动态视图内容。

但是,直接编辑 gsp 文件并在运行时编译它们并不是一个好习惯。

相反,您可以定义一个域类,其中自定义内容被持久化并从预定义的 gsp 布局加载。

具体来说,您可以定义类似以下域类的内容来保存自定义 Web 元素:

class Module {
    Integer positionLeft
    Integer positionTop
    Integer width
    Integer height
    String  className   //CSS class
    String  border      
    String  borderColor 
    Integer borderSize  
    Integer seq
    String  content
    String  type
    static  hasMany = [components: Component]        //inner elements within a module
    Module  parentModule                    //support nested module structure to ensure flexibility
    User    owner
}

class Component {
    String title
    String content
    String methodName
    String url
    String type     //I-image,A-attachment,L-external link,T-text,G-graph report,D-data table,R RSS FEED...
    String font
    String fontFamily
    String fontBold
    String fontUnderline
    String color    
    String textAlign
    String source      //uri -- enables ajax call to some method to get dynamic data
    Integer seq
}

使用客户端 javascript,该应用程序可以轻松地使用户通过拖动、调整大小、从某些菜单中选择不同的元素类型以及提交给对这些元素执行 CRUD 操作的后端控制器来生成自定义视图。

然后在布局中,将那些自定义的元素一一渲染:

<%@ page import="com.app.Module" %>
<g:each in="${Module.findAllByOwnerAndParentModuleIsNull(session.user,[fetch:[components:'join']]).collect {[
            id:it.id,
            posLeft:it.posLeft,
            posTop:it. posTop,
            width:it.width,
            height:it.height,
            className:it.className,
            border:it.border,
            borderColor:it.borderColor,
            borderSize:it.borderSize,
            seq:it.seq,
            content:it.content,
            type:it.type,
            components:it.components,
            innerModules:Module.findAllByParentModule(it)
        ]}}" var="module" status="i">
    <div class="${module.className?module.className:''}" style="position: absolute; left: ${module.posLeft}px; top: ${module.posTop}px; width: ${module.width}px; height: ${module.height}px; border: ${module.borderColor} ${module.borderSize?(module.borderSize+'px'):''} ${module.border};">
        <g:each in="${module.innerModules}" var="inner">  
             <div class="${inner.className?inner.className:''}"  style="position: absolute; left: ${inner.posLeft-module.posLeft}px; top: ${inner.posTop-module.posTop}px; width: ${inner.width}px; height: ${inner.height}px; border: blue 1px solid;">
                  <g:render template="/home/module" model="[
                      module: inner
                  ]"/>
            </div>
        </g:each> 
        <g:render template="/home/module" model="[
             module: module
        ]"/>
    </div>
</g:each>

这样,每个用户都可以管理其个性化的布局。

于 2013-01-23T06:14:00.693 回答
1

在 Grails 下呈现 GSP 文件的正确方法是使用 Grails 的 GSP bean。有了这个,您可以访问所有标签库功能。以下是我的 Grails 插件中处理 GSP 文件的片段:

GroovyPagesTemplateEngine gsp = (GroovyPagesTemplateEngine)appCtx.getBean(GROOVY_PAGES_TEMPLATE_ENGINE);
Template template = gsp.createTemplate(new ByteArrayResource(bufferStr.getBytes(encoding)), false);
Writable w = template.make();
StringWriter sw = new StringWriter();
w.writeTo(new PrintWriter(sw));
于 2013-01-30T17:06:32.993 回答
1

虽然可能,但我没有将类似 cms 的功能集成到 Grails 应用程序中,而是看到了不同的方法。

一种方法是将 CMS 放在一边并确保它能够:

  • 在转换过程之后读取 gsp,通常是自动或计划的作业

  • 生成 Grails 应用程序使用的 gsp 文件。在这种情况下,正如您提到的,您可以覆盖视图位置。

为了避免我所描述的方法出现严重的 UI 问题或 CMS 限制,您不会使用布局。CMS 供应商提供模块来导入/导出 GSP 文件,如果不是您自己编写该层。

希望对你有帮助,祝你好运。

于 2013-01-23T03:57:21.640 回答