diff --git a/.gitignore b/.gitignore index 5d6365ad..0226ab6b 100644 --- a/.gitignore +++ b/.gitignore @@ -24,7 +24,7 @@ _testmain.go mindoc mindoc_linux_amd64 mindoc_linux_musl_amd64 -database +database/mindoc.db *.test *.prof .idea diff --git a/README.md b/README.md index 97f01280..286a2b0d 100644 --- a/README.md +++ b/README.md @@ -268,3 +268,59 @@ docker run -p 8181:8181 --name mindoc -e DB_ADAPTER=mysql -e MYSQL_PORT_3306_TCP 一个不纯粹的PHPer,一个不自由的 gopher 。 + +# 部署补充 +- 若内网部署,draw.io无法使用外网,则需要用tomcat运行war包,见(https://github.com/jgraph/drawio) 从release下载,之后修改markdown.js的TODO行对应的链接即可 +- 为了护眼,简单增加了编辑界面的主题切换,见editormd.js和markdown_edit_template.tpl +- (需重新编译项)为了对已删除文档/文档引用图片删除文字后,对悬空无引用的图片/附件进行清理,增加了清理接口,需重新编译 + - 编译后除二进制文件外还需更新三个文件: conf/lang/en-us.ini,zh-cn.ini; attach_list.tpl + - 若不想重新编译,也可通过database/clean.py,手动执行对无引用图片/附件的文件清理和数据库记录双向清理。 +- 若采用nginx二级部署,以yourpath/为例,需修改 + - conf/app.conf修改:`baseurl="/yourpath"` + - static/js/kancloud.js文件中`url: "/comment/xxxxx` => `url: "/yourpath" + "/comment/xxxxx`, 共两处 + + - nginx端口代理示例: + ``` + 增加 + location /yourpath/ { + rewrite ^/yourpath/(.*) /$1 break; + proxy_pass http://127.0.0.1:8181; + } + ``` + 注意使用的是127.0.0.1,根据自身选择替换,如果nginx是docker部署,则还需要在docker中托管运行mindoc,具体参考如下配置: + - docker-compose代理示例(docker-nginx代理运行mindoc) + ``` + version: '3' + services: + mynginx: + image: nginx:latest + ports: + - "8880:80" + command: + - bash + - -c + - | + service nginx start + cd /src/mindoc/ && ./mindoc + volumes: + - ..:/src + - ./nginx:/etc/nginx/conf.d + ``` + + 目录结构 + ``` + onefolder + | + - docker + | + - docker-compose.yml + - nginx + | + - mynginx.conf + + - mindoc + | + - database/ + - conf/ + - ... + ``` \ No newline at end of file diff --git a/conf/lang/en-us.ini b/conf/lang/en-us.ini index 40ea2147..ad12463e 100644 --- a/conf/lang/en-us.ini +++ b/conf/lang/en-us.ini @@ -54,6 +54,7 @@ yes = yes no = no read = Read generate = Generate +clean = Clean [init] default_proj_name = MinDoc Demo Project diff --git a/conf/lang/zh-cn.ini b/conf/lang/zh-cn.ini index 912e7419..bd566ccc 100644 --- a/conf/lang/zh-cn.ini +++ b/conf/lang/zh-cn.ini @@ -54,6 +54,7 @@ yes = 是 no = 否 read = 阅读 generate = 生成 +clean = 清理 [init] default_proj_name = MinDoc演示项目 diff --git a/controllers/ManagerController.go b/controllers/ManagerController.go index dea3e507..8a065775 100644 --- a/controllers/ManagerController.go +++ b/controllers/ManagerController.go @@ -634,6 +634,41 @@ func (c *ManagerController) AttachList() { c.Data["Lists"] = attachList } +//附件清理. +func (c *ManagerController) AttachClean() { + c.Prepare() + + attachList, _, err := models.NewAttachment().FindToPager(0, 0) + + if err != nil { + c.Abort("500") + } + + for _, item := range attachList { + + p := filepath.Join(conf.WorkingDirectory, item.FilePath) + + item.IsExist = filetil.FileExists(p) + if item.IsExist { + // 判断 + searchList, err := models.NewDocumentSearchResult().SearchAllDocument(item.HttpPath) + if err != nil { + c.Abort("500") + } else if len(searchList) == 0 { + logs.Info("delete file:", item.FilePath) + item.FilePath = p + if err := item.Delete(); err != nil { + logs.Error("AttachDelete => ", err) + c.JsonResult(6002, err.Error()) + break + } + } + } + } + + c.JsonResult(0, "ok") +} + //附件详情. func (c *ManagerController) AttachDetailed() { c.Prepare() diff --git a/database/clean.py b/database/clean.py new file mode 100644 index 00000000..85783273 --- /dev/null +++ b/database/clean.py @@ -0,0 +1,34 @@ +import sqlite3 +import os, glob + +conn = sqlite3.connect("mindoc.db") +cur = conn.cursor() #通过建立数据库游标对象,准备读写操作 + + +cmd = """ +SELECT + att.http_path +FROM + md_attachment AS att +WHERE (att.document_id != 0 OR (NOT EXISTS( SELECT 1 FROM md_documents WHERE markdown LIKE ("%" || att.http_path || "%")))) +AND (att.document_id = 0 OR (NOT EXISTS( SELECT 1 FROM md_documents WHERE att.document_id = document_id ))) +""" +cur.execute(cmd) +file_list = cur.fetchall() +for file_item in file_list: + item_path = file_item[0] + # 1. 删除os文件 + if os.path.exists(os.path.join("..", item_path[1:])): + os.remove(os.path.join("..", item_path[1:])) + + # 2. 查询os是否删除成功,成功则删除附件记录 + if not os.path.exists(os.path.join("..", item_path[1:])): + cmd = """ + delete + from md_attachment + WHERE http_path = '{}' + """.format(item_path) + cur.execute(cmd) +conn.commit() #保存提交,确保数据保存成功 + +conn.close() #关闭与数据库的连接 \ No newline at end of file diff --git a/models/AttachmentModel.go b/models/AttachmentModel.go index 8e2d45ce..5de9fb31 100644 --- a/models/AttachmentModel.go +++ b/models/AttachmentModel.go @@ -102,11 +102,15 @@ func (m *Attachment) FindToPager(pageIndex, pageSize int) (attachList []*Attachm return nil, 0, err } totalCount = int(total) - offset := (pageIndex - 1) * pageSize var list []*Attachment - _, err = o.QueryTable(m.TableNameWithPrefix()).OrderBy("-attachment_id").Offset(offset).Limit(pageSize).All(&list) + offset := (pageIndex - 1) * pageSize + if pageSize == 0 { + _, err = o.QueryTable(m.TableNameWithPrefix()).OrderBy("-attachment_id").Offset(offset).Limit(pageSize).All(&list) + } else { + _, err = o.QueryTable(m.TableNameWithPrefix()).OrderBy("-attachment_id").All(&list) + } if err != nil { if err == orm.ErrNoRows { diff --git a/models/DocumentSearchResult.go b/models/DocumentSearchResult.go index 499bba55..8e5097f0 100644 --- a/models/DocumentSearchResult.go +++ b/models/DocumentSearchResult.go @@ -309,3 +309,23 @@ func (m *DocumentSearchResult) SearchDocument(keyword string, bookId int) (docs return } + +// 所有项目搜索. +func (m *DocumentSearchResult) SearchAllDocument(keyword string) (docs []*DocumentSearchResult, err error) { + o := orm.NewOrm() + + sql := "SELECT * FROM md_documents WHERE (document_name LIKE ? OR `release` LIKE ?) " + keyword = "%" + keyword + "%" + + _need_escape := need_escape(keyword) + escape_sql := func(sql string) string { + if _need_escape { + return escape_re.ReplaceAllString(sql, escape_replace) + } + return sql + } + + _, err = o.Raw(escape_sql(sql), keyword, keyword).QueryRows(&docs) + + return +} diff --git a/routers/router.go b/routers/router.go index a6996180..8e4b773b 100644 --- a/routers/router.go +++ b/routers/router.go @@ -154,6 +154,7 @@ func init() { web.Router("/manager/books/open", &controllers.ManagerController{}, "post:PrivatelyOwned") web.Router("/manager/attach/list", &controllers.ManagerController{}, "*:AttachList") + web.Router("/manager/attach/clean", &controllers.ManagerController{}, "post:AttachClean") web.Router("/manager/attach/detailed/:id", &controllers.ManagerController{}, "*:AttachDetailed") web.Router("/manager/attach/delete", &controllers.ManagerController{}, "post:AttachDelete") web.Router("/manager/label/list", &controllers.ManagerController{}, "get:LabelList") diff --git a/static/editor.md/editormd.js b/static/editor.md/editormd.js index 70013ce0..980f7cac 100755 --- a/static/editor.md/editormd.js +++ b/static/editor.md/editormd.js @@ -71,7 +71,7 @@ "list-ul", "list-ol", "hr", "|", "link", "reference-link", "image", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|", "goto-line", "watch", "preview", "fullscreen", "clear", "search", "|", - "help", "info" + "help", "changetheme", "info" ], simple : [ "undo", "redo", "|", @@ -79,12 +79,12 @@ "h1", "h2", "h3", "h4", "h5", "h6", "|", "list-ul", "list-ol", "hr", "|", "watch", "preview", "fullscreen", "|", - "help", "info" + "help", "changetheme", "info" ], mini : [ "undo", "redo", "|", "watch", "preview", "|", - "help", "info" + "help", "changetheme", "info" ] }; @@ -94,8 +94,8 @@ name : "", // Form element name value : "", // value for CodeMirror, if mode not gfm/markdown theme : "", // Editor.md self themes, before v1.5.0 is CodeMirror theme, default empty - editorTheme : "default", // Editor area, this is CodeMirror theme at v1.5.0 - previewTheme : "", // Preview area theme, default empty + editorTheme : "pastel-on-dark", //"default", // Editor area, this is CodeMirror theme at v1.5.0 + previewTheme : "dark", //"", // Preview area theme, default empty markdown : "", // Markdown source code appendMarkdown : "", // if in init textarea value not empty, append markdown to textarea width : "100%", @@ -225,6 +225,7 @@ fullscreen : "fa-arrows-alt", clear : "fa-eraser", help : "fa-question-circle", + changetheme : "fa-info-circle", info : "fa-info-circle" }, toolbarIconTexts : {}, @@ -271,6 +272,7 @@ clear : "清空", search : "搜索", help : "使用帮助", + changetheme : "切换编辑主题", info : "关于" + editormd.title }, buttons : { @@ -322,7 +324,10 @@ }, help : { title : "使用帮助" - } + }, + changetheme : { + title : "切换编辑主题" + }, } } }; @@ -3385,6 +3390,11 @@ this.executePlugin("helpDialog", "help-dialog/help-dialog"); }, + changetheme : function() { + this.setEditorTheme((this.settings.editorTheme=="default")?"pastel-on-dark":"default"); + this.setPreviewTheme((this.settings.previewTheme=="")?"dark":""); + }, + info : function() { this.showInfoDialog(); } diff --git a/static/js/markdown.js b/static/js/markdown.js index 5f2fff4f..c450e3ce 100644 --- a/static/js/markdown.js +++ b/static/js/markdown.js @@ -72,7 +72,7 @@ $(function () { drawio.show = function () { - const drawUrl = 'https://embed.diagrams.net/?embed=1&libraries=1&proto=json&spin=1&saveAndExit=1&noSaveBtn=1&noExitBtn=0'; + const drawUrl = 'https://embed.diagrams.net/?embed=1&libraries=1&proto=json&spin=1&saveAndExit=1&noSaveBtn=1&noExitBtn=0'; // TODO: with Tomcat & https://github.com/jgraph/drawio this.div = document.createElement('div'); this.div.id = 'diagram'; this.gXml = ''; diff --git a/views/document/markdown_edit_template.tpl b/views/document/markdown_edit_template.tpl index e22e2327..5a141376 100644 --- a/views/document/markdown_edit_template.tpl +++ b/views/document/markdown_edit_template.tpl @@ -80,12 +80,6 @@
H1 H2 - H3 - H4 - H5 - H6 -
-
@@ -111,10 +105,12 @@ +
- + +
diff --git a/views/manager/attach_list.tpl b/views/manager/attach_list.tpl index e8598e4a..a2aaaa79 100644 --- a/views/manager/attach_list.tpl +++ b/views/manager/attach_list.tpl @@ -27,8 +27,9 @@ {{template "manager/widgets.tpl" .}}
-
+
{{i18n .Lang "mgr.attachment_mgr"}} +
@@ -104,6 +105,29 @@ } }); }); + + $("#attachAll").on("click","button[data-method='clean']",function () { + var $this = $(this); + $(this).button("loading"); + $.ajax({ + url : "{{urlfor "ManagerController.AttachClean"}}", + type : "post", + dataType : "json", + success : function (res) { + if(res.errcode === 0){ + alert("done"); + }else { + layer.msg(res.message); + } + }, + error : function () { + layer.msg({{i18n .Lang "message.system_error"}}); + }, + complete : function () { + $this.button("reset"); + } + }); + }); });