1、实现富文本编辑器

2、实现文档转换为PDF、MOBI、EPUB格式
This commit is contained in:
Minho
2018-01-25 19:18:59 +08:00
parent dab6f31d01
commit e1ec6bb788
16 changed files with 990 additions and 473 deletions

View File

@@ -3,8 +3,6 @@ package models
import (
"time"
"strings"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
@@ -24,6 +22,10 @@ type Book struct {
OrderIndex int `orm:"column(order_index);type(int);default(0)" json:"order_index"`
// Description 项目描述.
Description string `orm:"column(description);size(2000)" json:"description"`
//发行公司
Publisher string `orm:"column(publisher);size(500)" json:"publisher"`
//是否缓存导出的电子书,如果缓存可能会出现导出的文件不是最新的。 0 为不缓存
IsCacheEBook int `orm:"column(is_cache_ebook);type(int);default(0)" json:"is_cache_ebook"`
Label string `orm:"column(label);size(500)" json:"label"`
// PrivatelyOwned 项目私有: 0 公开/ 1 私有
PrivatelyOwned int `orm:"column(privately_owned);type(int);default(0)" json:"privately_owned"`
@@ -354,38 +356,6 @@ func (m *Book) FindForLabelToPager(keyword string, pageIndex, pageSize, member_i
}
func (book *Book) ToBookResult() *BookResult {
m := NewBookResult()
m.BookId = book.BookId
m.BookName = book.BookName
m.Identify = book.Identify
m.OrderIndex = book.OrderIndex
m.Description = strings.Replace(book.Description, "\r\n", "<br/>", -1)
m.PrivatelyOwned = book.PrivatelyOwned
m.PrivateToken = book.PrivateToken
m.DocCount = book.DocCount
m.CommentStatus = book.CommentStatus
m.CommentCount = book.CommentCount
m.CreateTime = book.CreateTime
m.ModifyTime = book.ModifyTime
m.Cover = book.Cover
m.Label = book.Label
m.Status = book.Status
m.Editor = book.Editor
m.Theme = book.Theme
m.AutoRelease = book.AutoRelease == 1
if book.Theme == "" {
m.Theme = "default"
}
if book.Editor == "" {
m.Editor = "markdown"
}
return m
}
//重置文档数量
func (m *Book) ResetDocumentNumber(book_id int) {
o := orm.NewOrm()

View File

@@ -2,10 +2,20 @@ package models
import (
"time"
"bytes"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/logs"
"github.com/lifei6671/mindoc/conf"
"strings"
"github.com/lifei6671/mindoc/converter"
"strconv"
"github.com/russross/blackfriday"
"path/filepath"
"github.com/astaxie/beego"
"os"
"github.com/PuerkitoBio/goquery"
"github.com/lifei6671/mindoc/utils"
)
type BookResult struct {
@@ -14,17 +24,19 @@ type BookResult struct {
Identify string `json:"identify"`
OrderIndex int `json:"order_index"`
Description string `json:"description"`
Publisher string `json:"publisher"`
IsCacheEBook bool `json:"is_cache_ebook"`
PrivatelyOwned int `json:"privately_owned"`
PrivateToken string `json:"private_token"`
DocCount int `json:"doc_count"`
CommentStatus string `json:"comment_status"`
CommentCount int `json:"comment_count"`
CreateTime time.Time `json:"create_time"`
CreateName string `json:"create_name"`
CreateName string `json:"create_name"`
ModifyTime time.Time `json:"modify_time"`
Cover string `json:"cover"`
Theme string `json:"theme"`
Label string `json:"label"`
Theme string `json:"theme"`
Label string `json:"label"`
MemberId int `json:"member_id"`
Editor string `json:"editor"`
AutoRelease bool `json:"auto_release"`
@@ -79,13 +91,14 @@ func (m *BookResult) FindByIdentify(identify string,member_id int) (*BookResult,
return m, err
}
m = book.ToBookResult()
m = NewBookResult().ToBookResult(*book)
m.CreateName = member.Account
m.MemberId = relationship.MemberId
m.RoleId = relationship.RoleId
m.RelationshipId = relationship.RelationshipId
if m.RoleId == conf.BookFounder {
m.RoleName = "创始人"
} else if m.RoleId == conf.BookAdmin {
@@ -134,6 +147,183 @@ func (m *BookResult) FindToPager(pageIndex, pageSize int) (books []*BookResult,t
return
}
//实体转换
func (m *BookResult) ToBookResult(book Book) *BookResult {
m.BookId = book.BookId
m.BookName = book.BookName
m.Identify = book.Identify
m.OrderIndex = book.OrderIndex
m.Description = strings.Replace(book.Description, "\r\n", "<br/>", -1)
m.PrivatelyOwned = book.PrivatelyOwned
m.PrivateToken = book.PrivateToken
m.DocCount = book.DocCount
m.CommentStatus = book.CommentStatus
m.CommentCount = book.CommentCount
m.CreateTime = book.CreateTime
m.ModifyTime = book.ModifyTime
m.Cover = book.Cover
m.Label = book.Label
m.Status = book.Status
m.Editor = book.Editor
m.Theme = book.Theme
m.AutoRelease = book.AutoRelease == 1
m.Publisher = book.Publisher
m.IsCacheEBook = book.IsCacheEBook == 1
if book.Theme == "" {
m.Theme = "default"
}
if book.Editor == "" {
m.Editor = "markdown"
}
return m
}
func (m *BookResult) Converter(sessionId string) (ConvertBookResult,error) {
convertBookResult := ConvertBookResult{}
outputPath := filepath.Join(beego.AppConfig.DefaultString("book_output_path","cache"),sessionId,strconv.Itoa(m.BookId))
if m.IsCacheEBook {
outputPath = filepath.Join(beego.AppConfig.DefaultString("book_output_path","cache"),strconv.Itoa(m.BookId))
}
if m.IsCacheEBook {
pdfpath := filepath.Join(outputPath,"output","book.pdf")
epubpath := filepath.Join(outputPath,"output","book.epub")
mobipath := filepath.Join(outputPath,"output","book.mobi")
if utils.FileExists(pdfpath) && utils.FileExists(epubpath) && utils.FileExists(mobipath){
convertBookResult.EpubPath = epubpath
convertBookResult.MobiPath = mobipath
convertBookResult.PDFPath = pdfpath
return convertBookResult,nil
}
}
docs, err := NewDocument().FindListByBookId(m.BookId)
if err != nil {
return convertBookResult,err
}
tocList := make([]converter.Toc,0)
for _, item := range docs {
if item.ParentId == 0 {
toc := converter.Toc{
Id: item.DocumentId,
Link: strconv.Itoa(item.DocumentId) + ".html",
Pid: item.ParentId,
Title: item.DocumentName,
}
tocList = append(tocList,toc)
}
}
for _, item := range docs {
if item.ParentId != 0 {
toc := converter.Toc{
Id: item.DocumentId,
Link: strconv.Itoa(item.DocumentId) + ".html",
Pid: item.ParentId,
Title: item.DocumentName,
}
tocList = append(tocList,toc)
}
}
ebookConfig := converter.Config{
Charset : "utf-8",
Cover : m.Cover,
Timestamp : time.Now().Format("2006-01-02 15:04:05"),
Description : string(blackfriday.MarkdownBasic([]byte(m.Description))),
Footer : "<p style='color:#8E8E8E;font-size:12px;'>本文档使用 <a href='https://www.iminho.me' style='text-decoration:none;color:#1abc9c;font-weight:bold;'>MinDoc</a> 构建 <span style='float:right'>- _PAGENUM_ -</span></p>",
Header : "<p style='color:#8E8E8E;font-size:12px;'>_SECTION_</p>",
Identifier : "",
Language : "zh-CN",
Creator : m.CreateName,
Publisher : m.Publisher,
Contributor : m.Publisher,
Title : m.BookName,
Format: []string{"epub", "mobi", "pdf"},
FontSize : "14",
PaperSize : "a4",
MarginLeft : "72",
MarginRight : "72",
MarginTop : "72",
MarginBottom : "72",
Toc : tocList,
More : []string{},
}
os.MkdirAll(outputPath, 0766)
if outputPath, err = filepath.Abs(outputPath); err != nil {
beego.Error("导出目录配置错误:" + err.Error())
return convertBookResult,err
}
viewPath := beego.BConfig.WebConfig.ViewsPath
baseUrl := beego.AppConfig.DefaultString("baseurl","")
for _,item := range docs {
name := strconv.Itoa(item.DocumentId)
fpath := filepath.Join(outputPath,name + ".html")
f, err := os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0777)
if err != nil {
return convertBookResult,err
}
var buf bytes.Buffer
if err := beego.ExecuteViewPathTemplate(&buf,"document/export.tpl",viewPath,map[string]interface{}{"Model": m, "Lists": item, "BaseUrl": baseUrl}); err != nil {
return convertBookResult,err
}
html := buf.String()
if err != nil {
f.Close()
return convertBookResult,err
}
bufio := bytes.NewReader(buf.Bytes())
doc, err := goquery.NewDocumentFromReader(bufio)
doc.Find("img").Each(func(i int, contentSelection *goquery.Selection) {
if src, ok := contentSelection.Attr("src"); ok && strings.HasPrefix(src, "/uploads/") {
contentSelection.SetAttr("src", baseUrl + src)
}
})
html, err = doc.Html()
if err != nil {
f.Close()
return convertBookResult,err
}
// html = strings.Replace(html, "<img src=\"/uploads", "<img src=\"" + c.BaseUrl() + "/uploads", -1)
f.WriteString(html)
f.Close()
}
eBookConverter := &converter.Converter{
BasePath : outputPath,
Config : ebookConfig,
Debug : false,
}
if err := eBookConverter.Convert();err != nil {
beego.Error("转换文件错误:" + m.BookName +" => "+ err.Error())
return convertBookResult,err
}
convertBookResult.MobiPath = filepath.Join(outputPath,"output","book.mobi")
convertBookResult.PDFPath = filepath.Join(outputPath,"output","book.pdf")
convertBookResult.EpubPath = filepath.Join(outputPath,"output","book.epub")
return convertBookResult,nil
}

View File

@@ -0,0 +1,8 @@
package models
// 转换结果
type ConvertBookResult struct {
PDFPath string
EpubPath string
MobiPath string
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
"github.com/lifei6671/mindoc/conf"
"strings"
)
// Document struct.
@@ -137,6 +138,9 @@ func (m *Document) ReleaseContent(book_id int) {
if err == nil && len(attach_list) > 0 {
content := bytes.NewBufferString("<div class=\"attach-list\"><strong>附件</strong><ul>")
for _, attach := range attach_list {
if strings.HasPrefix(attach.HttpPath,"/"){
attach.HttpPath = strings.TrimSuffix(beego.AppConfig.DefaultString("baseurl",""),"/") + attach.HttpPath
}
li := fmt.Sprintf("<li><a href=\"%s\" target=\"_blank\" title=\"%s\">%s</a></li>", attach.HttpPath, attach.FileName, attach.FileName)
content.WriteString(li)