mirror of https://github.com/mindoc-org/mindoc.git
增加了导出单篇文档为 PDF 的功能。
parent
5bb61618fc
commit
789d46c340
|
@ -0,0 +1,11 @@
|
||||||
|
1、把 log 提取出 dbgout 之类的方法;
|
||||||
|
2、把源代码里的 TODO 完成;
|
||||||
|
3、Export 的两个 URL 应该可以合并,用是否有 :id 来区分;这样 0 号文档输出整个 book 更顺;
|
||||||
|
4、统一代码风格,空格、命名之类的;
|
||||||
|
5、美化 PDF 的输出格式;
|
||||||
|
6、自动登录新开标签页而且并不能跳转至起始请求页的问题;
|
||||||
|
7、用户分组管理;
|
||||||
|
8、[自动]展示历史版本;增强历史版本对比显示的能力;
|
||||||
|
9、[自动]展示作者信息;也许可以和上一需求合并考虑;
|
||||||
|
10、查看了解评论功能;
|
||||||
|
|
|
@ -16,6 +16,8 @@ import (
|
||||||
|
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"github.com/astaxie/beego/orm"
|
"github.com/astaxie/beego/orm"
|
||||||
|
@ -125,6 +127,9 @@ func (c *DocumentController) Index() {
|
||||||
c.Data["Result"] = template.HTML(tree)
|
c.Data["Result"] = template.HTML(tree)
|
||||||
c.Data["Title"] = "概要"
|
c.Data["Title"] = "概要"
|
||||||
c.Data["Content"] = template.HTML( blackfriday.MarkdownBasic([]byte(bookResult.Description)))
|
c.Data["Content"] = template.HTML( blackfriday.MarkdownBasic([]byte(bookResult.Description)))
|
||||||
|
|
||||||
|
c.Data["DocumentId"] = "0" // added by dandycheung, 2017-12-08, for exporting
|
||||||
|
log.Println("DocumentController.Index(): c.Data[\"DocumentId\"] = ", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
//阅读文档.
|
//阅读文档.
|
||||||
|
@ -134,6 +139,9 @@ func (c *DocumentController) Read() {
|
||||||
token := c.GetString("token")
|
token := c.GetString("token")
|
||||||
id := c.GetString(":id")
|
id := c.GetString(":id")
|
||||||
|
|
||||||
|
c.Data["DocumentId"] = id // added by dandycheung, 2017-12-08, for exporting
|
||||||
|
log.Println("DocumentController.Read(): c.Data[\"DocumentId\"] = ", id, ", IsAjax = ", c.IsAjax())
|
||||||
|
|
||||||
if identify == "" || id == "" {
|
if identify == "" || id == "" {
|
||||||
c.Abort("404")
|
c.Abort("404")
|
||||||
}
|
}
|
||||||
|
@ -735,8 +743,32 @@ func (c *DocumentController) Content() {
|
||||||
c.JsonResult(0, "ok", doc)
|
c.JsonResult(0, "ok", doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
//导出文件
|
func (c *DocumentController) ExportDoc() {
|
||||||
func (c *DocumentController) Export() {
|
c.Export(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DocumentController) ExportBook() {
|
||||||
|
c.Export(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DocumentController) GetDocumentById(id string) (doc *models.Document, err error) {
|
||||||
|
doc = models.NewDocument()
|
||||||
|
if doc_id, err := strconv.Atoi(id); err == nil {
|
||||||
|
doc, err = doc.Find(doc_id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
doc, err = doc.FindByFieldFirst("identify", id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return doc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
func (c *DocumentController) Export(single_doc bool) {
|
||||||
c.Prepare()
|
c.Prepare()
|
||||||
c.TplName = "document/export.tpl"
|
c.TplName = "document/export.tpl"
|
||||||
|
|
||||||
|
@ -791,10 +823,20 @@ func (c *DocumentController) Export() {
|
||||||
|
|
||||||
pathList := list.New()
|
pathList := list.New()
|
||||||
|
|
||||||
RecursiveFun(0, "", dpath, c, bookResult, docs, pathList)
|
// 增加对单页文档的导出,dandycheung, 2017-12-07
|
||||||
|
if single_doc {
|
||||||
|
id := c.Ctx.Input.Param(":id")
|
||||||
|
if doc, err := c.GetDocumentById(id); err == nil {
|
||||||
|
EachFun("", dpath, c, bookResult, doc, pathList)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RecursiveFun(0, "", dpath, c, bookResult, docs, pathList)
|
||||||
|
}
|
||||||
|
|
||||||
defer os.RemoveAll(dpath)
|
defer os.RemoveAll(dpath)
|
||||||
|
|
||||||
|
// TODO: check if the pathList is empty
|
||||||
|
|
||||||
os.MkdirAll("./cache", 0766)
|
os.MkdirAll("./cache", 0766)
|
||||||
pdfpath := filepath.Join("cache", identify+"_"+c.CruSession.SessionID()+".pdf")
|
pdfpath := filepath.Join("cache", identify+"_"+c.CruSession.SessionID()+".pdf")
|
||||||
|
|
||||||
|
@ -1142,49 +1184,54 @@ func (c *DocumentController) Compare() {
|
||||||
func RecursiveFun(parent_id int, prefix, dpath string, c *DocumentController, book *models.BookResult, docs []*models.Document, paths *list.List) {
|
func RecursiveFun(parent_id int, prefix, dpath string, c *DocumentController, book *models.BookResult, docs []*models.Document, paths *list.List) {
|
||||||
for _, item := range docs {
|
for _, item := range docs {
|
||||||
if item.ParentId == parent_id {
|
if item.ParentId == parent_id {
|
||||||
name := prefix + strconv.Itoa(item.ParentId) + strconv.Itoa(item.OrderSort) + strconv.Itoa(item.DocumentId)
|
EachFun(prefix, dpath, c, book, item, paths)
|
||||||
fpath := dpath + "/" + name + ".html"
|
|
||||||
paths.PushBack(fpath)
|
|
||||||
|
|
||||||
f, err := os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0777)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
beego.Error(err)
|
|
||||||
c.Abort("500")
|
|
||||||
}
|
|
||||||
|
|
||||||
html, err := c.ExecuteViewPathTemplate("document/export.tpl", map[string]interface{}{"Model": book, "Lists": item, "BaseUrl": c.BaseUrl()})
|
|
||||||
if err != nil {
|
|
||||||
f.Close()
|
|
||||||
beego.Error(err)
|
|
||||||
c.Abort("500")
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := bytes.NewReader([]byte(html))
|
|
||||||
doc, err := goquery.NewDocumentFromReader(buf)
|
|
||||||
doc.Find("img").Each(func(i int, contentSelection *goquery.Selection) {
|
|
||||||
if src, ok := contentSelection.Attr("src"); ok && strings.HasPrefix(src, "/uploads/") {
|
|
||||||
contentSelection.SetAttr("src", c.BaseUrl()+src)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
html, err = doc.Html()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
f.Close()
|
|
||||||
beego.Error(err)
|
|
||||||
c.Abort("500")
|
|
||||||
}
|
|
||||||
//html = strings.Replace(html,"<img src=\"/uploads","<img src=\""+ c.BaseUrl() +"/uploads",-1)
|
|
||||||
|
|
||||||
f.WriteString(html)
|
|
||||||
f.Close()
|
|
||||||
|
|
||||||
for _, sub := range docs {
|
for _, sub := range docs {
|
||||||
if sub.ParentId == item.DocumentId {
|
if sub.ParentId == item.DocumentId {
|
||||||
RecursiveFun(item.DocumentId, name, dpath, c, book, docs, paths)
|
prefix += strconv.Itoa(item.ParentId) + strconv.Itoa(item.OrderSort) + strconv.Itoa(item.DocumentId);
|
||||||
|
RecursiveFun(item.DocumentId, prefix, dpath, c, book, docs, paths)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EachFun(prefix, dpath string, c *DocumentController, book *models.BookResult, item *models.Document, paths *list.List) {
|
||||||
|
name := prefix + strconv.Itoa(item.ParentId) + strconv.Itoa(item.OrderSort) + strconv.Itoa(item.DocumentId)
|
||||||
|
fpath := dpath + "/" + name + ".html"
|
||||||
|
paths.PushBack(fpath)
|
||||||
|
|
||||||
|
f, err := os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0777)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
beego.Error(err)
|
||||||
|
c.Abort("500")
|
||||||
|
}
|
||||||
|
|
||||||
|
html, err := c.ExecuteViewPathTemplate("document/export.tpl", map[string]interface{}{"Model": book, "Lists": item, "BaseUrl": c.BaseUrl()})
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
beego.Error(err)
|
||||||
|
c.Abort("500")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewReader([]byte(html))
|
||||||
|
doc, err := goquery.NewDocumentFromReader(buf)
|
||||||
|
doc.Find("img").Each(func(i int, contentSelection *goquery.Selection) {
|
||||||
|
if src, ok := contentSelection.Attr("src"); ok && strings.HasPrefix(src, "/uploads/") {
|
||||||
|
contentSelection.SetAttr("src", c.BaseUrl()+src)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
html, err = doc.Html()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
beego.Error(err)
|
||||||
|
c.Abort("500")
|
||||||
|
}
|
||||||
|
//html = strings.Replace(html,"<img src=\"/uploads","<img src=\""+ c.BaseUrl() +"/uploads",-1)
|
||||||
|
|
||||||
|
f.WriteString(html)
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
|
@ -73,7 +73,8 @@ func init() {
|
||||||
beego.Router("/docs/:key", &controllers.DocumentController{},"*:Index")
|
beego.Router("/docs/:key", &controllers.DocumentController{},"*:Index")
|
||||||
beego.Router("/docs/:key/:id", &controllers.DocumentController{},"*:Read")
|
beego.Router("/docs/:key/:id", &controllers.DocumentController{},"*:Read")
|
||||||
beego.Router("/docs/:key/search", &controllers.DocumentController{},"post:Search")
|
beego.Router("/docs/:key/search", &controllers.DocumentController{},"post:Search")
|
||||||
beego.Router("/export/:key", &controllers.DocumentController{},"*:Export")
|
beego.Router("/export/:key", &controllers.DocumentController{},"*:ExportBook")
|
||||||
|
beego.Router("/export/:key/:id", &controllers.DocumentController{},"*:ExportDoc")
|
||||||
beego.Router("/qrcode/:key.png",&controllers.DocumentController{},"get:QrCode")
|
beego.Router("/qrcode/:key.png",&controllers.DocumentController{},"get:QrCode")
|
||||||
|
|
||||||
beego.Router("/attach_files/:key/:attach_id",&controllers.DocumentController{},"get:DownloadAttachment")
|
beego.Router("/attach_files/:key/:attach_id",&controllers.DocumentController{},"get:DownloadAttachment")
|
||||||
|
|
|
@ -58,7 +58,8 @@
|
||||||
{{if eq .Model.PrivatelyOwned 0}}
|
{{if eq .Model.PrivatelyOwned 0}}
|
||||||
<li><a href="javascript:" data-toggle="modal" data-target="#shareProject">项目分享</a> </li>
|
<li><a href="javascript:" data-toggle="modal" data-target="#shareProject">项目分享</a> </li>
|
||||||
<li role="presentation" class="divider"></li>
|
<li role="presentation" class="divider"></li>
|
||||||
<li><a href="{{urlfor "DocumentController.Export" ":key" .Model.Identify "output" "pdf"}}" target="_blank">项目导出PDF</a> </li>
|
<li><a href="javascript:void(0);" onclick="ExportPdfDoc()">文档导出为 PDF</a> </li>
|
||||||
|
<li><a href="{{urlfor "DocumentController.ExportBook" ":key" .Model.Identify "output" "pdf"}}" target="_blank">项目导出为 PDF</a> </li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<li><a href="{{urlfor "HomeController.Index"}}" title="返回首页">返回首页</a> </li>
|
<li><a href="{{urlfor "HomeController.Index"}}" title="返回首页">返回首页</a> </li>
|
||||||
|
@ -234,6 +235,13 @@
|
||||||
<script type="text/javascript" src="/static/js/jquery.highlight.js"></script>
|
<script type="text/javascript" src="/static/js/jquery.highlight.js"></script>
|
||||||
<script type="text/javascript" src="/static/js/kancloud.js"></script>
|
<script type="text/javascript" src="/static/js/kancloud.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
active_book_id = {{.Model.Identify}};
|
||||||
|
active_doc_id = {{.DocumentId}};
|
||||||
|
$(function () {
|
||||||
|
$("body").on('article.open', function (event, $param) {
|
||||||
|
active_doc_id = $param.$id;
|
||||||
|
});
|
||||||
|
});
|
||||||
$(function () {
|
$(function () {
|
||||||
$("#searchList").on("click","a",function () {
|
$("#searchList").on("click","a",function () {
|
||||||
var id = $(this).attr("data-id");
|
var id = $(this).attr("data-id");
|
||||||
|
@ -245,6 +253,12 @@ $(function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
function ExportPdfDoc() {
|
||||||
|
var id = active_book_id;
|
||||||
|
if(active_doc_id != "0")
|
||||||
|
id += "/" + active_doc_id;
|
||||||
|
window.location.href = "/export/" + id + "?output=pdf";
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Reference in New Issue