Jpress博客无法编辑内容包含HTML或XML代码的bug

高新技术

2018-03-01

152

0

目录


JPress是基于JFinal框架开发的java博客系统,也是本站采用的博客系统,功能强大而且快速,详细见官网:http://jpress.io

问题描述

采用jpress以来,发现一个比较严重的问题:当博客内容包含html代码或者xml代码时(确切的说是包含符号“>”或“<”),编辑博客内容时就无法正常显示(过滤掉了"<>"之间的内容)。这个问题困扰了我很久,不过一直没有心思来解决,最近突然心血来潮突然又想再定制下我的网站,顺便把这个问题解决了。采用的JPress版本为:0.5.0,不知道后边的版本是否修复。

解决思路

要解决问题当然还是要看jpress源码。

跟踪jpress源码,发现编辑文章采用的富文本组件是tinymce(markdown暂时忽略),而tinymce初始化内容是从textarea中获取的,而jpress使用的前端渲染模板是freemarker,textarea获取内容代码如下:

模板位置:/WEB-INFO/admin/content/_index_include.html

<div  class="box-body no-padding">
  <textarea id="textarea"  name="content.text" >${(content.text)!}</textarea>
</div>

对应的后台处理器为:_ContentController.java,编辑文章时调用的是其edit()方法:

@Override
public void edit() {
	String moduleName = getModuleName();
	BigInteger contentId = getParaToBigInteger("id");
	Content content = ContentQuery.me().findById(contentId);
	if (content != null) {
		setAttr("content", content);
		moduleName = content.getModule();
	}
	TplModule module = TemplateManager.me().currentTemplateModule(moduleName);
	setAttr("module", module);
	String _editor = getCookie("_editor", "tinymce");
	setAttr("_editor", _editor);
	setAttr("urlPreffix", ContentRouter.getContentRouterPreffix(module.getName()));
	setAttr("urlSuffix", ContentRouter.getContentRouterSuffix(module.getName()));
	setSlugInputDisplay(moduleName);
	String templateHtml = String.format("admin_content_edit_%s.html", moduleName);
	for (int i = 0; i < 2; i++) {
		if (TemplateManager.me().existsFile(templateHtml)) {
			setAttr("include", TemplateManager.me().currentTemplatePath() + "/" + templateHtml);
			return;
		}
		templateHtml = templateHtml.substring(0, templateHtml.lastIndexOf("_")) + ".html";
	}
	setAttr("include", "_edit_include.html");
}

由于默认采用Freemarker模板渲染页面,文章内容包含在上述代码的Content对象中。对比了数据库存储的文章html内容、content对象的文章内容和页面上textarea标签中文章内容,发现结果如下:

  • 数据库文章内容与content对象中的一致,内容中包含"<>"符号的均被转义为&lt;&gt;,其他排版的html标签不变。
  • 页面上textarea中的内容与其他两者不一致,所有html标签"<>"符号全部被转义为&lt;&gt;

 图上为数据库存储内容,但是在页面的textarea中,所有"<>"符号均被转义。

于是猜测应该是在页面渲染的时候,将HTML转义了,但是已经被转义的内容没有再进行深层次转义:即将&转义为&amp;。

具体代码逻辑不详细描述了,跟踪代码,发现确实是freemarker在渲染时将html内容全部进行了转义,关键代码如下:

private String getHtmlContent(Map data) {
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	OutputStreamWriter osWriter = null;
	try {
		osWriter = new OutputStreamWriter(baos, Consts.CHARTSET_UTF8);
		Template template = getConfiguration().getTemplate(view);
		template.process(data, osWriter);
		osWriter.flush();
		return baos.toString(Consts.CHARTSET_UTF8);
	} catch (Exception e) {
		if (Jpress.isDevMode()) {
			e.printStackTrace();
		}
		throw new RenderException(e);
	} finally {
		close(baos);
		close(osWriter);
	}
}

注意template.process(data, osWriter)方法,该方法将数据和模板进行合并,得到转染的结果页面内容。

最终结果与猜测一致, 只要将被转义的属于文章内容的"<>"做深层次转义即可解决问题。于是打算将查询出来的文章内容做修改,将被转义的<>符号做再次转义,即:将&lt;或&gt;中的&替换为&amp;。但是仔细想了下,在freemarker中应该会考虑到这样的功能,查询freemarker文档,果然,freemarker的#escape指令就是用于这种情况该指令说明如下:

当你使用 escape 指令包围模板中的一部分时,在块中出现的插值( ${...} )会和
转义表达式自动结合。这是一个避免编写相似表达式的很方便的方法。它不会影响在字符串
形式的插值(比如在 <#assign x = "Hello ${user}!"> )。而且,它也不会影
响数值插值( #{...} )。

 

解决方案

很简单,在模板上将textarea的内容加上#escape指令即可:

<div class="box-body no-padding">
  <textarea id="textarea"  name="content.text" ><#escape x as x?html>${(content.text)!}</#escape></textarea>
</div>

最终在页面上textarea中的内容为:

这样tinymce组件加载内容时就能够正确显示属于文章内容本身的html代码了。


前一篇:股票是什么
后一篇:Spring事务管理一:Spring事务简介

belonk

轻轻地我走了,正如我轻轻地来,我挥一挥衣袖,不带走一片云彩