ThinkCMF整合Editor.md编辑器挖坑补坑指南

最近倒腾了一个网站,使用的是ThinkCMF基于ThinkPHP的框架,自带的编辑器是百度的UEditor,这是百度的一个开源的富文本编辑器,前些年的时候界面是很合适的,那个时候用的比较多,随着时间的推移,UEditor的界面显得有些过时了,自从common-fileupload.jar包存在ddos漏洞, php的代码会存在 ssrf 的安全漏洞,外加隔壁MarkDown之风盛行,UEditor用的人也越来越少了,项目也年久失修,存在的问题不少。
ThinkCMF默认使用的就是UEditor,早前ThinkCMF的UEditor也是问题一大堆,比如视频不能二次保存啥的,但是随着不断的更新,问题也越来越少了。

20190226174647.png

修复百度编辑器视频上传

ThinkCMF的编辑器问题以前早已折腾过,很熟悉了,估摸着可以换个好点的,颜值高点的编辑器,问题应该也不大。
于是乎看中了Editor.md,基于JavaScript,能实现Markdown语法,看着好看,就行了。没有这么多要求,谁知道坑一大堆。。。。。怪我。。

ThinkCMF的UEditor

20190226161636.png

1.下载

去项目首页下载就好了,Editor.md,下载最后一个版本,1.5的。可惜已经很多年未维护了。

2.调用css,js文件

从解压的文件中找到,jquery.min.js,但是ThinkCMF中已经有了,我们就不额外调用了,到时候注意一下顺序就好了。
如下:

editormd.min.js或者editormd.js
editormd.min.css或者editormd.css
lib目录(必要)
plugins目录(可选)

我将其上传到了项目路径+public\themes\admin_simpleboot3\public\assets\editormd,在assets中新建了目录,名称为:editormd。根据你自己的选择全部扔进去。因为我只是将Editor.md编辑器用于后台,前台用的另外的编辑器(为后面埋下了麻烦)。你也可以传到public\static\js\ueditor,UEditor的目录。

更改public/themes/admin_simpleboot3/portal中的调用即可。

比如我们修改文章编辑的页面。

原先的UEditor应该注释掉,或者删除。

<script type="text/javascript" src="__STATIC__/js/ueditor/ueditor.config.js"></script>
<script type="text/javascript" src="__STATIC__/js/ueditor/ueditor.all.min.js"></script>

    editorcontent = new baidu.editor.ui.Editor();
    editorcontent.render('content');
    try {
        editorcontent.sync();
    } catch (err) {
    }

<script type="text/plain" id="content" name="post[post_content]">{$post.post_content}</script>

我们要增加Editor.md的调用,你可以:

大于ThinkPHP 5.0.4版本可以直接使用
__ROOT__: 网站根目录,不带/;

__WEB_ROOT__: 网站资源根目录,不带/,如果以前版本用__ROOT__来定位网站资源,方便以后cdn切换

__TMPL__: 当前模板根目录,不带/; 如:前台 simpleboot3模板根目录是 public/themes/simpleboot3
后台admin_simpleboot3模板根目录是public/themes/admin_simpleboot3

__STATIC__: public/static目录,不带/;

我写的是这样:

    <link href="__TMPL__/public/assets/editormd/css/editormd.min.css" rel="stylesheet">
    <script src="__TMPL__/public/assets/editormd/js/editormd.min.js"></script> 

然后还需要创建Editor.md编辑器,我不太推荐直接写到公共的header中,我将其写到了edit.html的头部中。

    <script type="text/javascript">        
        $(function() {
            var testEditor = editormd("test-editormd", {
                saveHTMLToTextarea: true,//保存为html(!!!)
                syncScrolling : "single",
                path    : "__TMPL__/public/assets/editormd/lib/",//路径,同上面的路径配置
                watch : false,//是否有预览
                width   : "100%",
                height  : 640,
                htmlDecode: "style,script,iframe|on*"//html过滤
            });
        });
    </script>

里面的是配置信息,酌情选择。

关于是否开启HTML解析,官网是这样写的:

注:虽然此功能能极大地扩展 Markdown 语法,但也面临着安全上的风险,所以默认是不开启的。

Update: 可以通过设置 `settings.htmlDecode =
"style,script,iframe|on*"`来实现过滤指定标签及属性的解析,提高安全性;

test-editormd务必和下面的div中的id相同。

差不多是下面这样:
20190226170155.png

然后修改下面的调用位置:

20190226163753.png

<div class="editormd" id="test-editormd">                                
    <textarea class="editormd-markdown-textarea" name="my-editormd-markdown-doc" style="display:none;">{$post.post_content}</textarea>                             
    <textarea class="editormd-html-textarea" name="post[post_content]"></textarea>
</div>

20190226170332.png

3.图片上传

需要在上面的配置中加入下面的配置:

     imageUpload : true,
     imageFormats : ["jpg", "jpeg", "gif", "png", "bmp", "webp"],
     imageUploadURL : "__TMPL__/public/assets/editormd/upload/"

Editor.md默认提供了简单的PHP上传后端,在editor.md-master\examples\php\中,有简易上传类和跨域的演示类(cross-domain upload demo),但是作者貌似不太自信,写了这个:

20190226165730.png

ThinkCMF自己有上传,我们就用ThinkCMF的吧。

直接修改主js。我们直接进入editormd.jseditormd.min.js(须格式化),找到image的调用。

image : function()

默认是:this.executePlugin("imageDialog", "image-dialog/image-dialog");
是为了弹出图片上传框。直接注释,让其弹出openUploadDialog,具体的可以查看手册

/**
 * 打开文件上传对话框
 * @param dialog_title 对话框标题
 * @param callback 回调方法,参数有(当前dialog对象,选择的文件数组,你设置的extra_params)
 * @param extra_params 额外参数,object
 * @param multi 是否可以多选
 * @param filetype 文件类型,image,video,audio,file
 * @param app  应用名,CMF的应用名
 */
function openUploadDialog(dialog_title, callback, extra_params, multi, filetype, app)

我就暴力修改了:

        var that = this;
        openUploadDialog('图片上传', function (dialog, files) {
          
            $('#test-editormd').attr('src', files[0].preview_url);                
            var link = files[0].filepath;
            var url  = files[0].filepath;
            var alt  = '';
            var altAttr = '';
            if (link === "" || link === "http://")
            {
                that.cm.replaceSelection("![" + alt + "](" + url + altAttr + ")");
            }
            else
            {
                that.cm.replaceSelection("[![" + alt + "](" + url + altAttr + ")](" + link + altAttr + ")");
            }

        }, '', 0, 'image', 'app');

里面有replaceSelection('XX');,这是Editor.md的自带方法,替换当前光标选中的文本或在当前光标处插入新字符。

20190226171213.png

至于上面的link,url,alt,altAttr参数根据你自己需求进行修改了。

然后就正常了。

20190226171504.png

4.saveHTMLToTextarea?到底是一个textarea还是两个?

这里网上的文章有点含糊不清,有说加textarea一个的,有两个的,其实就是看你想不想要直接保存html到数据库,
看下内部代码:

20190226172117.png

基本上就是,前面的class="editormd-markdown-textarea"是读取到前台的数据,通过{$post.post_content}读取到前台,后面的class="editormd-html-textarea"是解析为HTML的数据,这里的name很重要,是name="post[post_content]",这是最终提交到后台的数据,我们这里的意思就是直接提交html到数据库。

5.打开额外的插件功能

如果你没有启用部分插件,在控制台会遇到问题。

20190226172947.png

plugins目录中的插件上传上去即可。

6.MarkDown的前端解析

如果你在前端没有启用saveHTMLToTextarea,默认为false,并且你在后端接收的时候没有接收第二个textarea中的HTML,那么,你在数据库中的post_content字段为markdown的数据。前端在解析的时候也会是makrdown,显然这并不友好。
你可以有两种选择,就是数据库中储存markdown的数据,前端对markdown的数据进行解析。还有就是数据库中同时储存markdown的数据和html的数据(不推荐,但是我是这样做的,原因是我需要Editor.md和UEditor同时使用,不是同一页面)。
如果你是第一种的话,看起来就是下面这样,仅作演示,实际使用的时候务必注意调用路径!

<script src="__TMPL__/public/assets/editormd/lib/marked.min.js"></script>
<script src="__TMPL__/public/assets/editormd/lib/prettify.min.js"></script>
<script src="__TMPL__/public/assets/editormd/lib/raphael.min.js"></script>
<script src="__TMPL__/public/assets/editormd/lib/underscore.min.js"></script>
<script src="__TMPL__/public/assets/editormd/lib/sequence-diagram.min.js"></script>
<script src="__TMPL__/public/assets/editormd/lib/flowchart.min.js"></script>
<script src="__TMPL__/public/assets/editormd/lib/jquery.flowchart.min.js"></script>
<script src="__TMPL__/public/assets/editormd/editormd.min.js"></script>
<script type="text/javascript">
    $(document).ready(function() {
        var testEditor = editormd.markdownToHTML("test-editormd-view", {
            htmlDecode      : "style,script,iframe"      
        });

    })
</script>

关于第二种,我就不太想说了,你可以通过新建一个数据库字段来完成这个事,有个问题是markdown转html没有任何问题,但是html转markdown就有些问题了。

7.分页

默认的分页是用的UEditor的_ueditor_page_break_tag_,你知道_XXX_是在markdown中会转义的,酌情修改即可。

20190226180012.png

发表留言

人生在世,错别字在所难免,无需纠正。