2017-04-21 18:20:35 +08:00
package models
2017-04-22 17:24:17 +08:00
import (
2018-03-25 14:59:42 +08:00
"crypto/md5"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
2017-04-22 17:24:17 +08:00
"time"
2017-05-02 10:00:21 +08:00
"github.com/astaxie/beego"
2017-07-07 16:20:55 +08:00
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/orm"
"github.com/lifei6671/mindoc/conf"
2018-03-24 17:24:02 +08:00
"github.com/lifei6671/mindoc/utils/cryptil"
2018-03-25 14:59:42 +08:00
"github.com/lifei6671/mindoc/utils/filetil"
2018-03-24 17:24:02 +08:00
"github.com/lifei6671/mindoc/utils/requests"
2018-03-25 14:59:42 +08:00
"github.com/lifei6671/mindoc/utils/ziptil"
2018-03-24 17:24:02 +08:00
"gopkg.in/russross/blackfriday.v2"
2018-08-13 19:05:49 +08:00
"encoding/json"
2017-04-22 17:24:17 +08:00
)
2017-04-21 18:20:35 +08:00
// Book struct .
type Book struct {
2017-07-07 16:20:55 +08:00
BookId int ` orm:"pk;auto;unique;column(book_id)" json:"book_id" `
2017-04-21 18:20:35 +08:00
// BookName 项目名称.
2017-07-07 16:20:55 +08:00
BookName string ` orm:"column(book_name);size(500)" json:"book_name" `
2017-04-21 18:20:35 +08:00
// Identify 项目唯一标识.
2018-01-26 17:17:38 +08:00
Identify string ` orm:"column(identify);size(100);unique" json:"identify" `
2018-01-19 20:10:54 +08:00
//是否是自动发布 0 否/1 是
AutoRelease int ` orm:"column(auto_release);type(int);default(0)" json:"auto_release" `
2018-02-28 15:47:00 +08:00
//是否开启下载功能 0 是/1 否
IsDownload int ` orm:"column(is_download);type(int);default(0)" json:"is_download" `
2018-03-25 14:59:42 +08:00
OrderIndex int ` orm:"column(order_index);type(int);default(0)" json:"order_index" `
2017-04-21 18:20:35 +08:00
// Description 项目描述.
2017-07-07 16:20:55 +08:00
Description string ` orm:"column(description);size(2000)" json:"description" `
2018-01-25 19:18:59 +08:00
//发行公司
2018-01-26 17:17:38 +08:00
Publisher string ` orm:"column(publisher);size(500)" json:"publisher" `
2018-03-25 14:59:42 +08:00
Label string ` orm:"column(label);size(500)" json:"label" `
2017-04-21 18:20:35 +08:00
// PrivatelyOwned 项目私有: 0 公开/ 1 私有
2017-07-07 16:20:55 +08:00
PrivatelyOwned int ` orm:"column(privately_owned);type(int);default(0)" json:"privately_owned" `
2017-04-21 18:20:35 +08:00
// 当项目是私有时的访问Token.
2017-07-07 16:20:55 +08:00
PrivateToken string ` orm:"column(private_token);size(500);null" json:"private_token" `
2017-05-01 12:15:55 +08:00
//状态: 0 正常/1 已删除
2017-07-07 16:20:55 +08:00
Status int ` orm:"column(status);type(int);default(0)" json:"status" `
2017-04-26 18:17:38 +08:00
//默认的编辑器.
2017-07-07 16:20:55 +08:00
Editor string ` orm:"column(editor);size(50)" json:"editor" `
2017-04-21 18:20:35 +08:00
// DocCount 包含文档数量.
2017-07-07 16:20:55 +08:00
DocCount int ` orm:"column(doc_count);type(int)" json:"doc_count" `
2017-04-22 17:24:17 +08:00
// CommentStatus 评论设置的状态:open 为允许所有人评论, closed 为不允许评论, group_only 仅允许参与者评论 ,registered_only 仅允许注册者评论.
2017-07-07 16:20:55 +08:00
CommentStatus string ` orm:"column(comment_status);size(20);default(open)" json:"comment_status" `
CommentCount int ` orm:"column(comment_count);type(int)" json:"comment_count" `
2017-04-30 22:13:12 +08:00
//封面地址
2017-07-07 16:20:55 +08:00
Cover string ` orm:"column(cover);size(1000)" json:"cover" `
2017-04-30 22:13:12 +08:00
//主题风格
2017-07-07 16:20:55 +08:00
Theme string ` orm:"column(theme);size(255);default(default)" json:"theme" `
2017-04-21 18:20:35 +08:00
// CreateTime 创建时间 .
2017-07-07 16:20:55 +08:00
CreateTime time . Time ` orm:"type(datetime);column(create_time);auto_now_add" json:"create_time" `
2018-02-02 23:12:29 +08:00
//每个文档保存的历史记录数量, 0 为不限制
2018-03-25 14:59:42 +08:00
HistoryCount int ` orm:"column(history_count);type(int);default(0)" json:"history_count" `
2018-03-13 19:20:50 +08:00
//是否启用分享, 0启用/1不启用
2018-03-25 14:59:42 +08:00
IsEnableShare int ` orm:"column(is_enable_share);type(int);default(0)" json:"is_enable_share" `
MemberId int ` orm:"column(member_id);size(100)" json:"member_id" `
ModifyTime time . Time ` orm:"type(datetime);column(modify_time);null;auto_now" json:"modify_time" `
Version int64 ` orm:"type(bigint);column(version)" json:"version" `
2018-03-23 10:00:36 +08:00
//是否使用第一篇文章项目为默认首页,0 否/1 是
IsUseFirstDocument int ` orm:"column(is_use_first_document);type(int);default(0)" json:"is_use_first_document" `
2018-08-17 17:33:45 +08:00
//是否开启自动保存: 0 否/1 是
AutoSave int ` orm:"column(auto_save);type(tinyint);default(0)" json:"auto_save" `
2017-04-21 18:20:35 +08:00
}
2018-08-14 15:57:52 +08:00
func ( book * Book ) String ( ) string {
ret , err := json . Marshal ( * book )
2018-08-13 19:05:49 +08:00
if err != nil {
return ""
}
return string ( ret )
}
2017-04-21 18:20:35 +08:00
// TableName 获取对应数据库表名.
2018-03-24 22:36:35 +08:00
func ( book * Book ) TableName ( ) string {
2017-04-21 18:20:35 +08:00
return "books"
}
2017-07-07 16:20:55 +08:00
2017-04-21 18:20:35 +08:00
// TableEngine 获取数据使用的引擎.
2018-03-24 22:36:35 +08:00
func ( book * Book ) TableEngine ( ) string {
2017-04-21 18:20:35 +08:00
return "INNODB"
}
2018-03-24 22:36:35 +08:00
func ( book * Book ) TableNameWithPrefix ( ) string {
return conf . GetDatabasePrefix ( ) + book . TableName ( )
2017-04-22 17:24:17 +08:00
}
func NewBook ( ) * Book {
return & Book { }
}
2018-03-24 17:24:02 +08:00
//添加一个项目
2018-03-24 22:36:35 +08:00
func ( book * Book ) Insert ( ) error {
2017-04-22 17:24:17 +08:00
o := orm . NewOrm ( )
2017-07-07 16:20:55 +08:00
// o.Begin()
2017-04-29 21:28:09 +08:00
2018-03-24 22:36:35 +08:00
_ , err := o . Insert ( book )
2017-04-22 17:24:17 +08:00
2017-04-24 18:25:17 +08:00
if err == nil {
2018-03-24 22:36:35 +08:00
if book . Label != "" {
NewLabel ( ) . InsertOrUpdateMulti ( book . Label )
2017-07-07 16:20:55 +08:00
}
2017-04-24 18:25:17 +08:00
relationship := NewRelationship ( )
2018-03-24 22:36:35 +08:00
relationship . BookId = book . BookId
2017-04-24 18:25:17 +08:00
relationship . RoleId = 0
2018-03-24 22:36:35 +08:00
relationship . MemberId = book . MemberId
2017-04-24 18:25:17 +08:00
err = relationship . Insert ( )
2017-04-29 21:28:09 +08:00
if err != nil {
2017-07-07 16:20:55 +08:00
logs . Error ( "插入项目与用户关联 => " , err )
2017-05-05 11:09:17 +08:00
//o.Rollback()
2017-04-29 21:28:09 +08:00
return err
}
document := NewDocument ( )
2018-03-24 22:36:35 +08:00
document . BookId = book . BookId
2017-04-29 21:28:09 +08:00
document . DocumentName = "空白文档"
2018-03-24 22:36:35 +08:00
document . MemberId = book . MemberId
2017-04-29 21:28:09 +08:00
err = document . InsertOrUpdate ( )
2017-07-07 16:20:55 +08:00
if err != nil {
2017-05-05 11:09:17 +08:00
//o.Rollback()
2017-04-29 21:28:09 +08:00
return err
}
2017-05-05 11:09:17 +08:00
//o.Commit()
2017-05-04 10:35:56 +08:00
return nil
2017-04-24 18:25:17 +08:00
}
2017-05-05 11:09:17 +08:00
//o.Rollback()
2017-04-22 17:24:17 +08:00
return err
}
2018-08-14 15:57:52 +08:00
func ( book * Book ) Find ( id int , cols ... string ) ( * Book , error ) {
2017-04-23 20:33:21 +08:00
if id <= 0 {
2018-03-24 22:36:35 +08:00
return book , ErrInvalidParameter
2017-04-23 20:33:21 +08:00
}
o := orm . NewOrm ( )
2018-08-14 15:57:52 +08:00
err := o . QueryTable ( book . TableNameWithPrefix ( ) ) . Filter ( "book_id" , id ) . One ( book , cols ... )
2017-04-25 20:05:59 +08:00
2018-03-24 22:36:35 +08:00
return book , err
2017-04-23 20:33:21 +08:00
}
2018-03-25 14:59:42 +08:00
2018-03-24 17:24:02 +08:00
//更新一个项目
2018-03-24 22:36:35 +08:00
func ( book * Book ) Update ( cols ... string ) error {
2017-04-22 17:24:17 +08:00
o := orm . NewOrm ( )
2017-07-07 16:20:55 +08:00
temp := NewBook ( )
2018-03-24 22:36:35 +08:00
temp . BookId = book . BookId
2017-07-07 16:20:55 +08:00
2018-01-26 17:17:38 +08:00
if err := o . Read ( temp ) ; err != nil {
2017-07-07 16:20:55 +08:00
return err
}
2018-03-25 14:59:42 +08:00
if book . Label != "" || temp . Label != "" {
2017-07-07 16:20:55 +08:00
2018-03-24 22:36:35 +08:00
go NewLabel ( ) . InsertOrUpdateMulti ( book . Label + "," + temp . Label )
2017-07-07 16:20:55 +08:00
}
2018-03-24 22:36:35 +08:00
_ , err := o . Update ( book , cols ... )
2017-04-22 17:24:17 +08:00
return err
}
2018-07-10 18:53:41 +08:00
//复制项目
func ( book * Book ) Copy ( identify string ) error {
o := orm . NewOrm ( )
err := o . QueryTable ( book . TableNameWithPrefix ( ) ) . Filter ( "identify" , identify ) . One ( book )
if err != nil {
beego . Error ( "查询项目时出错 -> " , err )
return err
}
if err := o . Begin ( ) ; err != nil {
beego . Error ( "开启事物时出错 -> " , err )
return err
}
bookId := book . BookId
book . BookId = 0
book . Identify = book . Identify + fmt . Sprintf ( "%s-%s" , identify , strconv . FormatInt ( time . Now ( ) . UnixNano ( ) , 32 ) )
book . BookName = book . BookName + "[副本]"
book . CreateTime = time . Now ( )
book . CommentCount = 0
book . HistoryCount = 0
if _ , err := o . Insert ( book ) ; err != nil {
beego . Error ( "复制项目时出错 -> " , err )
o . Rollback ( )
return err
}
var rels [ ] * Relationship
if _ , err := o . QueryTable ( NewRelationship ( ) . TableNameWithPrefix ( ) ) . Filter ( "book_id" , bookId ) . All ( & rels ) ; err != nil {
beego . Error ( "复制项目关系时出错 -> " , err )
o . Rollback ( )
return err
}
for _ , rel := range rels {
rel . BookId = book . BookId
rel . RelationshipId = 0
if _ , err := o . Insert ( rel ) ; err != nil {
beego . Error ( "复制项目关系时出错 -> " , err )
o . Rollback ( )
return err
}
}
var docs [ ] * Document
if _ , err := o . QueryTable ( NewDocument ( ) . TableNameWithPrefix ( ) ) . Filter ( "book_id" , bookId ) . Filter ( "parent_id" , 0 ) . All ( & docs ) ; err != nil && err != orm . ErrNoRows {
beego . Error ( "读取项目文档时出错 -> " , err )
o . Rollback ( )
return err
}
if len ( docs ) > 0 {
2018-08-14 15:57:52 +08:00
if err := recursiveInsertDocument ( docs , o , book . BookId , 0 ) ; err != nil {
2018-07-10 18:53:41 +08:00
beego . Error ( "复制项目时出错 -> " , err )
o . Rollback ( )
return err
}
}
return o . Commit ( )
}
//递归的复制文档
func recursiveInsertDocument ( docs [ ] * Document , o orm . Ormer , bookId int , parentId int ) error {
for _ , doc := range docs {
docId := doc . DocumentId
doc . DocumentId = 0
doc . ParentId = parentId
doc . BookId = bookId
doc . Version = time . Now ( ) . Unix ( )
if _ , err := o . Insert ( doc ) ; err != nil {
beego . Error ( "插入项目时出错 -> " , err )
return err
}
var attachList [ ] * Attachment
//读取所有附件列表
if _ , err := o . QueryTable ( NewAttachment ( ) . TableNameWithPrefix ( ) ) . Filter ( "document_id" , docId ) . All ( & attachList ) ; err == nil {
for _ , attach := range attachList {
attach . BookId = bookId
attach . DocumentId = doc . DocumentId
attach . AttachmentId = 0
if _ , err := o . Insert ( attach ) ; err != nil {
return err
}
}
}
var subDocs [ ] * Document
if _ , err := o . QueryTable ( NewDocument ( ) . TableNameWithPrefix ( ) ) . Filter ( "parent_id" , docId ) . All ( & subDocs ) ; err != nil && err != orm . ErrNoRows {
beego . Error ( "读取文档时出错 -> " , err )
return err
}
if len ( subDocs ) > 0 {
if err := recursiveInsertDocument ( subDocs , o , bookId , doc . DocumentId ) ; err != nil {
return err
}
}
}
return nil
}
2017-04-30 22:13:12 +08:00
//根据指定字段查询结果集.
2018-03-25 14:59:42 +08:00
func ( book * Book ) FindByField ( field string , value interface { } , cols ... string ) ( [ ] * Book , error ) {
2017-04-22 17:24:17 +08:00
o := orm . NewOrm ( )
2017-04-30 22:13:12 +08:00
var books [ ] * Book
2018-03-25 14:59:42 +08:00
_ , err := o . QueryTable ( book . TableNameWithPrefix ( ) ) . Filter ( field , value ) . All ( & books , cols ... )
2017-04-22 17:24:17 +08:00
2017-07-07 16:20:55 +08:00
return books , err
2017-04-22 17:24:17 +08:00
}
2017-04-30 22:13:12 +08:00
//根据指定字段查询一个结果.
2018-03-24 22:36:35 +08:00
func ( book * Book ) FindByFieldFirst ( field string , value interface { } ) ( * Book , error ) {
2017-04-24 18:25:17 +08:00
o := orm . NewOrm ( )
2018-03-24 22:36:35 +08:00
err := o . QueryTable ( book . TableNameWithPrefix ( ) ) . Filter ( field , value ) . One ( book )
2017-04-24 18:25:17 +08:00
2018-03-24 22:36:35 +08:00
return book , err
2017-04-24 18:25:17 +08:00
}
2017-04-30 22:13:12 +08:00
2018-03-24 17:24:02 +08:00
//根据项目标识查询项目
2018-08-13 19:05:49 +08:00
func ( book * Book ) FindByIdentify ( identify string , cols ... string ) ( * Book , error ) {
2017-05-12 10:45:40 +08:00
o := orm . NewOrm ( )
2018-08-13 19:05:49 +08:00
err := o . QueryTable ( book . TableNameWithPrefix ( ) ) . Filter ( "identify" , identify ) . One ( book , cols ... )
2017-05-12 10:45:40 +08:00
2018-03-24 22:36:35 +08:00
return book , err
2017-05-12 10:45:40 +08:00
}
2017-04-30 22:13:12 +08:00
//分页查询指定用户的项目
2018-03-24 22:36:35 +08:00
func ( book * Book ) FindToPager ( pageIndex , pageSize , memberId int ) ( books [ ] * BookResult , totalCount int , err error ) {
2017-04-22 17:24:17 +08:00
relationship := NewRelationship ( )
o := orm . NewOrm ( )
2018-03-24 22:36:35 +08:00
sql1 := "SELECT COUNT(book.book_id) AS total_count FROM " + book . TableNameWithPrefix ( ) + " AS book LEFT JOIN " +
2017-07-07 16:20:55 +08:00
relationship . TableNameWithPrefix ( ) + " AS rel ON book.book_id=rel.book_id AND rel.member_id = ? WHERE rel.relationship_id > 0 "
2017-04-22 17:24:17 +08:00
2017-07-07 16:20:55 +08:00
err = o . Raw ( sql1 , memberId ) . QueryRow ( & totalCount )
2017-04-22 17:24:17 +08:00
if err != nil {
return
}
offset := ( pageIndex - 1 ) * pageSize
2017-07-07 16:20:55 +08:00
2018-03-24 22:36:35 +08:00
sql2 := "SELECT book.*,rel.member_id,rel.role_id,m.account as create_name FROM " + book . TableNameWithPrefix ( ) + " AS book" +
2017-07-07 16:20:55 +08:00
" LEFT JOIN " + relationship . TableNameWithPrefix ( ) + " AS rel ON book.book_id=rel.book_id AND rel.member_id = ?" +
" LEFT JOIN " + relationship . TableNameWithPrefix ( ) + " AS rel1 ON book.book_id=rel1.book_id AND rel1.role_id=0" +
" LEFT JOIN " + NewMember ( ) . TableNameWithPrefix ( ) + " AS m ON rel1.member_id=m.member_id " +
2018-01-26 17:17:38 +08:00
" WHERE rel.relationship_id > 0 ORDER BY book.order_index DESC,book.book_id DESC LIMIT " + fmt . Sprintf ( "%d,%d" , offset , pageSize )
2017-07-07 16:20:55 +08:00
_ , err = o . Raw ( sql2 , memberId ) . QueryRows ( & books )
2017-04-24 18:25:17 +08:00
if err != nil {
2017-07-07 16:20:55 +08:00
logs . Error ( "分页查询项目列表 => " , err )
2017-04-24 18:25:17 +08:00
return
}
2017-04-22 17:24:17 +08:00
sql := "SELECT m.account,doc.modify_time FROM md_documents AS doc LEFT JOIN md_members AS m ON doc.modify_at=m.member_id WHERE book_id = ? LIMIT 1 ORDER BY doc.modify_time DESC"
2017-07-07 16:20:55 +08:00
if err == nil && len ( books ) > 0 {
for index , book := range books {
var text struct {
Account string
2017-04-22 17:24:17 +08:00
ModifyTime time . Time
}
2017-07-07 16:20:55 +08:00
err1 := o . Raw ( sql , book . BookId ) . QueryRow ( & text )
2017-04-22 17:24:17 +08:00
if err1 == nil {
books [ index ] . LastModifyText = text . Account + " 于 " + text . ModifyTime . Format ( "2006-01-02 15:04:05" )
}
2017-07-07 16:20:55 +08:00
if book . RoleId == 0 {
2017-04-22 17:24:17 +08:00
book . RoleName = "创始人"
2017-07-07 16:20:55 +08:00
} else if book . RoleId == 1 {
2017-04-22 17:24:17 +08:00
book . RoleName = "管理员"
2017-07-07 16:20:55 +08:00
} else if book . RoleId == 2 {
2017-04-22 17:24:17 +08:00
book . RoleName = "编辑者"
2017-07-07 16:20:55 +08:00
} else if book . RoleId == 3 {
2017-04-22 17:24:17 +08:00
book . RoleName = "观察者"
}
}
}
return
2017-04-23 12:48:46 +08:00
}
2017-04-23 20:33:21 +08:00
// 彻底删除项目.
2018-03-24 22:36:35 +08:00
func ( book * Book ) ThoroughDeleteBook ( id int ) error {
2017-07-07 16:20:55 +08:00
if id <= 0 {
2017-04-23 20:33:21 +08:00
return ErrInvalidParameter
}
o := orm . NewOrm ( )
2018-03-25 14:59:42 +08:00
book , err := book . Find ( id )
2018-03-08 16:36:13 +08:00
if err != nil {
2017-04-23 20:33:21 +08:00
return err
}
o . Begin ( )
2017-07-07 16:20:55 +08:00
2018-08-14 15:57:52 +08:00
//删除附件,这里没有删除实际物理文件
2018-08-15 16:00:04 +08:00
_ , err = o . Raw ( "DELETE FROM " + NewAttachment ( ) . TableNameWithPrefix ( ) + " WHERE book_id=?" , book . BookId ) . Exec ( )
2017-04-23 20:33:21 +08:00
if err != nil {
o . Rollback ( )
return err
}
2018-08-14 15:57:52 +08:00
//删除文档
_ , err = o . Raw ( "DELETE FROM " + NewDocument ( ) . TableNameWithPrefix ( ) + " WHERE book_id = ?" , book . BookId ) . Exec ( )
2017-04-23 20:33:21 +08:00
if err != nil {
o . Rollback ( )
return err
}
2018-08-14 15:57:52 +08:00
//删除项目
_ , err = o . Raw ( "DELETE FROM " + book . TableNameWithPrefix ( ) + " WHERE book_id = ?" , book . BookId ) . Exec ( )
2017-04-23 20:33:21 +08:00
if err != nil {
o . Rollback ( )
2018-08-14 15:57:52 +08:00
return err
}
2018-04-02 15:52:36 +08:00
2018-08-14 15:57:52 +08:00
//删除关系
_ , err = o . Raw ( "DELETE FROM " + NewRelationship ( ) . TableNameWithPrefix ( ) + " WHERE book_id = ?" , book . BookId ) . Exec ( )
if err != nil {
o . Rollback ( )
return err
}
//删除模板
_ , err = o . Raw ( "DELETE FROM " + NewTemplate ( ) . TableNameWithPrefix ( ) + " WHERE book_id = ?" , book . BookId ) . Exec ( )
if err != nil {
o . Rollback ( )
2017-04-23 20:33:21 +08:00
return err
}
2018-08-14 15:57:52 +08:00
2018-03-24 22:36:35 +08:00
if book . Label != "" {
NewLabel ( ) . InsertOrUpdateMulti ( book . Label )
2017-07-07 16:20:55 +08:00
}
2018-08-20 15:11:32 +08:00
//删除导出缓存
2018-08-15 16:00:04 +08:00
if err := os . RemoveAll ( filepath . Join ( conf . GetExportOutputPath ( ) , strconv . Itoa ( id ) ) ) ; err != nil {
beego . Error ( "删除项目缓存失败 ->" , err )
}
2018-08-20 15:11:32 +08:00
//删除附件和图片
if err := os . RemoveAll ( filepath . Join ( conf . WorkingDirectory , "uploads" , book . Identify ) ) ; err != nil {
beego . Error ( "删除项目附件和图片失败 ->" , err )
}
2018-01-26 17:17:38 +08:00
2017-04-23 20:33:21 +08:00
return o . Commit ( )
}
2017-05-13 12:12:37 +08:00
//分页查找系统首页数据.
2018-03-24 22:36:35 +08:00
func ( book * Book ) FindForHomeToPager ( pageIndex , pageSize , member_id int ) ( books [ ] * BookResult , totalCount int , err error ) {
2017-04-30 22:13:12 +08:00
o := orm . NewOrm ( )
offset := ( pageIndex - 1 ) * pageSize
//如果是登录用户
if member_id > 0 {
sql1 := "SELECT COUNT(*) FROM md_books AS book LEFT JOIN md_relationship AS rel ON rel.book_id = book.book_id AND rel.member_id = ? WHERE relationship_id > 0 OR book.privately_owned = 0"
2017-07-07 16:20:55 +08:00
err = o . Raw ( sql1 , member_id ) . QueryRow ( & totalCount )
2017-04-30 22:13:12 +08:00
if err != nil {
return
}
2018-02-02 23:12:29 +08:00
sql2 := ` SELECT book . * , rel1 . * , member . account AS create_name , member . real_name FROM md_books AS book
2017-04-30 22:13:12 +08:00
LEFT JOIN md_relationship AS rel ON rel . book_id = book . book_id AND rel . member_id = ?
LEFT JOIN md_relationship AS rel1 ON rel1 . book_id = book . book_id AND rel1 . role_id = 0
LEFT JOIN md_members AS member ON rel1 . member_id = member . member_id
WHERE rel . relationship_id > 0 OR book . privately_owned = 0 ORDER BY order_index DESC , book . book_id DESC LIMIT ? , ? `
2017-04-23 20:33:21 +08:00
2017-07-07 16:20:55 +08:00
_ , err = o . Raw ( sql2 , member_id , offset , pageSize ) . QueryRows ( & books )
2017-04-23 20:33:21 +08:00
2017-07-07 16:20:55 +08:00
} else {
2018-03-24 22:36:35 +08:00
count , err1 := o . QueryTable ( book . TableNameWithPrefix ( ) ) . Filter ( "privately_owned" , 0 ) . Count ( )
2017-04-23 20:33:21 +08:00
2017-04-30 22:13:12 +08:00
if err1 != nil {
err = err1
return
}
totalCount = int ( count )
2017-04-23 20:33:21 +08:00
2018-02-02 23:12:29 +08:00
sql := ` SELECT book . * , rel . * , member . account AS create_name , member . real_name FROM md_books AS book
2017-04-30 22:13:12 +08:00
LEFT JOIN md_relationship AS rel ON rel . book_id = book . book_id AND rel . role_id = 0
LEFT JOIN md_members AS member ON rel . member_id = member . member_id
WHERE book . privately_owned = 0 ORDER BY order_index DESC , book . book_id DESC LIMIT ? , ? `
2017-07-07 16:20:55 +08:00
_ , err = o . Raw ( sql , offset , pageSize ) . QueryRows ( & books )
2017-04-30 22:13:12 +08:00
}
2018-02-02 23:12:29 +08:00
return
2017-04-30 22:13:12 +08:00
}
2017-07-07 16:20:55 +08:00
//分页全局搜索.
2018-03-24 22:36:35 +08:00
func ( book * Book ) FindForLabelToPager ( keyword string , pageIndex , pageSize , memberId int ) ( books [ ] * BookResult , totalCount int , err error ) {
2017-07-07 16:20:55 +08:00
o := orm . NewOrm ( )
keyword = "%" + keyword + "%"
offset := ( pageIndex - 1 ) * pageSize
//如果是登录用户
2018-02-28 15:47:00 +08:00
if memberId > 0 {
2017-07-07 16:20:55 +08:00
sql1 := "SELECT COUNT(*) FROM md_books AS book LEFT JOIN md_relationship AS rel ON rel.book_id = book.book_id AND rel.member_id = ? WHERE (relationship_id > 0 OR book.privately_owned = 0) AND book.label LIKE ?"
2018-02-28 15:47:00 +08:00
err = o . Raw ( sql1 , memberId , keyword ) . QueryRow ( & totalCount )
2017-07-07 16:20:55 +08:00
if err != nil {
return
}
sql2 := ` SELECT book . * , rel1 . * , member . account AS create_name FROM md_books AS book
LEFT JOIN md_relationship AS rel ON rel . book_id = book . book_id AND rel . member_id = ?
LEFT JOIN md_relationship AS rel1 ON rel1 . book_id = book . book_id AND rel1 . role_id = 0
LEFT JOIN md_members AS member ON rel1 . member_id = member . member_id
WHERE ( rel . relationship_id > 0 OR book . privately_owned = 0 ) AND book . label LIKE ? ORDER BY order_index DESC , book . book_id DESC LIMIT ? , ? `
2018-02-28 15:47:00 +08:00
_ , err = o . Raw ( sql2 , memberId , keyword , offset , pageSize ) . QueryRows ( & books )
2017-07-07 16:20:55 +08:00
return
} else {
2018-01-26 17:17:38 +08:00
count , err1 := o . QueryTable ( NewBook ( ) . TableNameWithPrefix ( ) ) . Filter ( "privately_owned" , 0 ) . Filter ( "label__icontains" , keyword ) . Count ( )
2017-07-07 16:20:55 +08:00
if err1 != nil {
err = err1
return
}
totalCount = int ( count )
sql := ` SELECT book . * , rel . * , member . account AS create_name FROM md_books AS book
LEFT JOIN md_relationship AS rel ON rel . book_id = book . book_id AND rel . role_id = 0
LEFT JOIN md_members AS member ON rel . member_id = member . member_id
WHERE book . privately_owned = 0 AND book . label LIKE ? ORDER BY order_index DESC , book . book_id DESC LIMIT ? , ? `
2018-01-26 17:17:38 +08:00
_ , err = o . Raw ( sql , keyword , offset , pageSize ) . QueryRows ( & books )
2017-07-07 16:20:55 +08:00
return
}
}
2018-03-25 14:59:42 +08:00
//发布文档
func ( book * Book ) ReleaseContent ( bookId int ) {
o := orm . NewOrm ( )
var docs [ ] * Document
_ , err := o . QueryTable ( NewDocument ( ) . TableNameWithPrefix ( ) ) . Filter ( "book_id" , bookId ) . All ( & docs , "document_id" , "identify" , "content" )
if err != nil {
2018-04-02 15:52:36 +08:00
beego . Error ( "发布失败 =>" , bookId , err )
2018-03-25 14:59:42 +08:00
return
}
for _ , item := range docs {
2018-08-13 19:05:49 +08:00
item . BookId = bookId
item . ReleaseContent ( )
2018-03-25 14:59:42 +08:00
}
}
2017-05-02 10:00:21 +08:00
//重置文档数量
2018-03-24 22:36:35 +08:00
func ( book * Book ) ResetDocumentNumber ( bookId int ) {
2017-05-02 10:00:21 +08:00
o := orm . NewOrm ( )
2017-04-23 20:33:21 +08:00
2018-02-28 15:47:00 +08:00
totalCount , err := o . QueryTable ( NewDocument ( ) . TableNameWithPrefix ( ) ) . Filter ( "book_id" , bookId ) . Count ( )
2017-05-02 10:00:21 +08:00
if err == nil {
2018-04-02 15:52:36 +08:00
_ , err = o . Raw ( "UPDATE md_books SET doc_count = ? WHERE book_id = ?" , int ( totalCount ) , bookId ) . Exec ( )
if err != nil {
beego . Error ( "重置文档数量失败 =>" , bookId , err )
}
2017-07-07 16:20:55 +08:00
} else {
2018-04-02 15:52:36 +08:00
beego . Error ( "获取文档数量失败 =>" , bookId , err )
2017-05-02 10:00:21 +08:00
}
}
2018-03-24 17:24:02 +08:00
2018-07-10 18:53:41 +08:00
//导入项目
2018-03-25 14:59:42 +08:00
func ( book * Book ) ImportBook ( zipPath string ) error {
2018-03-24 17:24:02 +08:00
if ! filetil . FileExists ( zipPath ) {
2018-03-25 14:59:42 +08:00
return errors . New ( "文件不存在 => " + zipPath )
2018-03-24 17:24:02 +08:00
}
w := md5 . New ( )
2018-03-25 14:59:42 +08:00
io . WriteString ( w , zipPath ) //将str写入到w中
2018-03-24 17:24:02 +08:00
io . WriteString ( w , time . Now ( ) . String ( ) )
2018-03-24 22:36:35 +08:00
io . WriteString ( w , book . BookName )
2018-03-24 17:24:02 +08:00
md5str := fmt . Sprintf ( "%x" , w . Sum ( nil ) ) //w.Sum(nil)将w的hash转成[]byte格式
2018-03-25 14:59:42 +08:00
tempPath := filepath . Join ( os . TempDir ( ) , md5str )
2018-03-24 17:24:02 +08:00
2018-04-02 15:52:36 +08:00
if err := os . MkdirAll ( tempPath , 0766 ) ; err != nil {
beego . Error ( "创建导入目录出错 => " , err )
}
2018-03-24 17:24:02 +08:00
//如果加压缩失败
2018-03-25 14:59:42 +08:00
if err := ziptil . Unzip ( zipPath , tempPath ) ; err != nil {
2018-03-24 17:24:02 +08:00
return err
}
2018-03-25 14:59:42 +08:00
//当导入结束后,删除临时文件
2018-07-23 09:54:00 +08:00
//defer os.RemoveAll(tempPath)
2018-03-25 14:59:42 +08:00
for {
//如果当前目录下只有一个目录,则重置根目录
if entries , err := ioutil . ReadDir ( tempPath ) ; err == nil && len ( entries ) == 1 {
dir := entries [ 0 ]
if dir . IsDir ( ) && dir . Name ( ) != "." && dir . Name ( ) != ".." {
tempPath = filepath . Join ( tempPath , dir . Name ( ) )
break
}
} else {
break
}
}
2018-03-24 17:24:02 +08:00
2018-03-25 14:59:42 +08:00
tempPath = strings . Replace ( tempPath , "\\" , "/" , - 1 )
2018-03-24 22:36:35 +08:00
2018-03-25 14:59:42 +08:00
docMap := make ( map [ string ] int , 0 )
o := orm . NewOrm ( )
o . Insert ( book )
relationship := NewRelationship ( )
relationship . BookId = book . BookId
relationship . RoleId = 0
relationship . MemberId = book . MemberId
relationship . Insert ( )
2018-03-24 17:24:02 +08:00
err := filepath . Walk ( tempPath , func ( path string , info os . FileInfo , err error ) error {
2018-03-25 14:59:42 +08:00
path = strings . Replace ( path , "\\" , "/" , - 1 )
2018-03-24 17:24:02 +08:00
if path == tempPath {
return nil
}
if ! info . IsDir ( ) {
ext := filepath . Ext ( info . Name ( ) )
2018-03-24 22:36:35 +08:00
//如果是Markdown文件
2018-03-25 14:59:42 +08:00
if strings . EqualFold ( ext , ".md" ) || strings . EqualFold ( ext , ".markdown" ) {
beego . Info ( "正在处理 =>" , path , info . Name ( ) )
2018-03-24 17:24:02 +08:00
doc := NewDocument ( )
doc . BookId = book . BookId
2018-03-24 22:36:35 +08:00
doc . MemberId = book . MemberId
2018-03-24 17:24:02 +08:00
docIdentify := strings . Replace ( strings . TrimPrefix ( path , tempPath + "/" ) , "/" , "-" , - 1 )
if ok , err := regexp . MatchString ( ` [a-z]+[a-zA-Z0-9_.\-]*$ ` , docIdentify ) ; ! ok || err != nil {
docIdentify = "import-" + docIdentify
}
doc . Identify = docIdentify
2018-03-24 22:36:35 +08:00
//匹配图片,如果图片语法是在代码块中,这里同样会处理
2018-03-24 17:24:02 +08:00
re := regexp . MustCompile ( ` !\[(.*?)\]\((.*?)\) ` )
2018-03-26 19:26:01 +08:00
markdown , err := filetil . ReadFileAndIgnoreUTF8BOM ( path )
2018-03-24 17:24:02 +08:00
if err != nil {
return err
}
2018-03-26 19:26:01 +08:00
2018-03-25 10:56:52 +08:00
//处理图片
2018-03-24 17:24:02 +08:00
doc . Markdown = re . ReplaceAllStringFunc ( string ( markdown ) , func ( image string ) string {
2018-03-25 14:59:42 +08:00
images := re . FindAllSubmatch ( [ ] byte ( image ) , - 1 )
2018-03-24 17:24:02 +08:00
if len ( images ) <= 0 || len ( images [ 0 ] ) < 3 {
return image
}
2018-03-24 22:36:35 +08:00
originalImageUrl := string ( images [ 0 ] [ 2 ] )
imageUrl := strings . Replace ( string ( originalImageUrl ) , "\\" , "/" , - 1 )
2018-03-24 17:24:02 +08:00
//如果是本地路径,则需要将图片复制到项目目录
2018-07-23 09:54:00 +08:00
if ! strings . HasPrefix ( imageUrl , "http://" ) &&
! strings . HasPrefix ( imageUrl , "https://" ) &&
! strings . HasPrefix ( imageUrl , "ftp://" ) {
2018-03-26 22:39:16 +08:00
//如果路径中存在参数
if l := strings . Index ( imageUrl , "?" ) ; l > 0 {
imageUrl = imageUrl [ : l ]
}
2018-03-24 17:24:02 +08:00
if strings . HasPrefix ( imageUrl , "/" ) {
imageUrl = filepath . Join ( tempPath , imageUrl )
} else if strings . HasPrefix ( imageUrl , "./" ) {
imageUrl = filepath . Join ( filepath . Dir ( path ) , strings . TrimPrefix ( imageUrl , "./" ) )
} else if strings . HasPrefix ( imageUrl , "../" ) {
imageUrl = filepath . Join ( filepath . Dir ( path ) , imageUrl )
} else {
imageUrl = filepath . Join ( filepath . Dir ( path ) , imageUrl )
}
2018-03-24 22:36:35 +08:00
imageUrl = strings . Replace ( imageUrl , "\\" , "/" , - 1 )
dstFile := filepath . Join ( conf . WorkingDirectory , "uploads" , time . Now ( ) . Format ( "200601" ) , strings . TrimPrefix ( imageUrl , tempPath ) )
2018-03-24 17:24:02 +08:00
if filetil . FileExists ( imageUrl ) {
2018-03-24 22:36:35 +08:00
filetil . CopyFile ( imageUrl , dstFile )
2018-03-24 17:24:02 +08:00
2018-03-26 22:39:16 +08:00
imageUrl = strings . TrimPrefix ( strings . Replace ( dstFile , "\\" , "/" , - 1 ) , strings . Replace ( conf . WorkingDirectory , "\\" , "/" , - 1 ) )
2018-03-24 17:24:02 +08:00
2018-03-24 22:36:35 +08:00
if ! strings . HasPrefix ( imageUrl , "/" ) && ! strings . HasPrefix ( imageUrl , "\\" ) {
2018-03-24 17:24:02 +08:00
imageUrl = "/" + imageUrl
}
}
2018-03-24 22:36:35 +08:00
} else {
2018-03-24 17:24:02 +08:00
imageExt := cryptil . Md5Crypt ( imageUrl ) + filepath . Ext ( imageUrl )
2018-03-24 22:36:35 +08:00
dstFile := filepath . Join ( conf . WorkingDirectory , "uploads" , time . Now ( ) . Format ( "200601" ) , imageExt )
2018-03-24 17:24:02 +08:00
2018-03-24 22:36:35 +08:00
if err := requests . DownloadAndSaveFile ( imageUrl , dstFile ) ; err == nil {
imageUrl = strings . TrimPrefix ( strings . Replace ( dstFile , "\\" , "/" , - 1 ) , strings . Replace ( conf . WorkingDirectory , "\\" , "/" , - 1 ) )
if ! strings . HasPrefix ( imageUrl , "/" ) && ! strings . HasPrefix ( imageUrl , "\\" ) {
2018-03-24 17:24:02 +08:00
imageUrl = "/" + imageUrl
}
}
}
2018-07-11 11:06:21 +08:00
imageUrl = strings . Replace ( strings . TrimSuffix ( image , originalImageUrl + ")" ) + conf . URLForWithCdnImage ( imageUrl ) + ")" , "\\" , "/" , - 1 )
2018-03-24 17:24:02 +08:00
return imageUrl
} )
2018-03-25 10:56:52 +08:00
linkRegexp := regexp . MustCompile ( ` \[(.*?)\]\((.*?)\) ` )
2018-07-23 09:54:00 +08:00
//处理链接
2018-03-25 10:56:52 +08:00
doc . Markdown = linkRegexp . ReplaceAllStringFunc ( doc . Markdown , func ( link string ) string {
2018-03-25 14:59:42 +08:00
links := linkRegexp . FindAllStringSubmatch ( link , - 1 )
originalLink := links [ 0 ] [ 2 ]
2018-07-23 09:54:00 +08:00
var linkPath string
var err error
2018-07-23 14:38:33 +08:00
if strings . HasPrefix ( originalLink , "<" ) {
originalLink = strings . TrimPrefix ( originalLink , "<" )
}
if strings . HasSuffix ( originalLink , ">" ) {
originalLink = strings . TrimSuffix ( originalLink , ">" )
}
2018-07-23 09:54:00 +08:00
//如果是从根目录开始,
2018-07-23 14:38:33 +08:00
if strings . HasPrefix ( originalLink , "/" ) {
linkPath , err = filepath . Abs ( filepath . Join ( tempPath , originalLink ) )
} else if strings . HasPrefix ( originalLink , "./" ) {
2018-07-23 09:54:00 +08:00
linkPath , err = filepath . Abs ( filepath . Join ( filepath . Dir ( path ) , originalLink [ 1 : ] ) )
2018-07-23 14:38:33 +08:00
} else {
2018-07-23 09:54:00 +08:00
linkPath , err = filepath . Abs ( filepath . Join ( filepath . Dir ( path ) , originalLink ) )
}
2018-03-25 10:56:52 +08:00
if err == nil {
//如果本地存在该链接
if filetil . FileExists ( linkPath ) {
ext := filepath . Ext ( linkPath )
2018-07-23 11:02:03 +08:00
//beego.Info("当前后缀 -> ",ext)
2018-03-25 10:56:52 +08:00
//如果链接是Markdown文件, 则生成文档标识,否则,将目标文件复制到项目目录
2018-03-25 14:59:42 +08:00
if strings . EqualFold ( ext , ".md" ) || strings . EqualFold ( ext , ".markdown" ) {
docIdentify := strings . Replace ( strings . TrimPrefix ( strings . Replace ( linkPath , "\\" , "/" , - 1 ) , tempPath + "/" ) , "/" , "-" , - 1 )
//beego.Info(originalLink, "|", linkPath, "|", docIdentify)
2018-03-25 10:56:52 +08:00
if ok , err := regexp . MatchString ( ` [a-z]+[a-zA-Z0-9_.\-]*$ ` , docIdentify ) ; ! ok || err != nil {
docIdentify = "import-" + docIdentify
}
2018-03-25 14:59:42 +08:00
docIdentify = strings . TrimSuffix ( docIdentify , "-README.md" )
link = strings . TrimSuffix ( link , originalLink + ")" ) + conf . URLFor ( "DocumentController.Read" , ":key" , book . Identify , ":id" , docIdentify ) + ")"
2018-07-23 09:54:00 +08:00
2018-03-25 14:59:42 +08:00
} else {
dstPath := filepath . Join ( conf . WorkingDirectory , "uploads" , time . Now ( ) . Format ( "200601" ) , originalLink )
filetil . CopyFile ( linkPath , dstPath )
2018-03-27 00:55:11 +08:00
tempLink := conf . BaseUrl + strings . TrimPrefix ( strings . Replace ( dstPath , "\\" , "/" , - 1 ) , strings . Replace ( conf . WorkingDirectory , "\\" , "/" , - 1 ) )
2018-03-25 14:59:42 +08:00
2018-03-27 00:55:11 +08:00
link = strings . TrimSuffix ( link , originalLink + ")" ) + tempLink + ")"
2018-03-25 14:59:42 +08:00
2018-03-25 10:56:52 +08:00
}
2018-07-23 09:54:00 +08:00
} else {
beego . Info ( "文件不存在 ->" , linkPath )
2018-03-25 10:56:52 +08:00
}
}
return link
} )
2018-03-26 13:32:02 +08:00
//codeRe := regexp.MustCompile("```\\w+")
//doc.Markdown = codeRe.ReplaceAllStringFunc(doc.Markdown, func(s string) string {
// //beego.Info(s)
// return strings.Replace(s,"```","``` ",-1)
//})
doc . Content = string ( blackfriday . Run ( [ ] byte ( doc . Markdown ) ) )
2018-03-25 14:59:42 +08:00
2018-03-24 22:36:35 +08:00
doc . Version = time . Now ( ) . Unix ( )
//解析文档名称, 默认使用第一个h标签为标题
docName := strings . TrimSuffix ( info . Name ( ) , ext )
for _ , line := range strings . Split ( doc . Markdown , "\n" ) {
if strings . HasPrefix ( line , "#" ) {
docName = strings . TrimLeft ( line , "#" )
break
}
}
2018-03-26 22:39:16 +08:00
doc . DocumentName = strings . TrimSpace ( docName )
2018-03-24 22:36:35 +08:00
parentId := 0
2018-03-25 14:59:42 +08:00
parentIdentify := strings . Replace ( strings . Trim ( strings . TrimSuffix ( strings . TrimPrefix ( path , tempPath ) , info . Name ( ) ) , "/" ) , "/" , "-" , - 1 )
2018-03-24 22:36:35 +08:00
if parentIdentify != "" {
2018-03-24 23:17:50 +08:00
2018-03-24 22:36:35 +08:00
if ok , err := regexp . MatchString ( ` [a-z]+[a-zA-Z0-9_.\-]*$ ` , parentIdentify ) ; ! ok || err != nil {
parentIdentify = "import-" + parentIdentify
}
if id , ok := docMap [ parentIdentify ] ; ok {
parentId = id
}
}
2018-03-24 23:17:50 +08:00
if strings . EqualFold ( info . Name ( ) , "README.md" ) {
2018-03-25 14:59:42 +08:00
beego . Info ( path , "|" , info . Name ( ) , "|" , parentIdentify , "|" , parentId )
2018-03-24 23:17:50 +08:00
}
2018-03-24 22:36:35 +08:00
isInsert := false
//如果当前文件是README.md, 则将内容更新到父级
2018-03-24 23:17:50 +08:00
if strings . EqualFold ( info . Name ( ) , "README.md" ) && parentId != 0 {
2018-03-24 22:36:35 +08:00
doc . DocumentId = parentId
2018-03-24 23:17:50 +08:00
//beego.Info(path,"|",parentId)
2018-03-24 22:36:35 +08:00
} else {
2018-03-24 23:17:50 +08:00
//beego.Info(path,"|",parentIdentify)
2018-03-24 22:36:35 +08:00
doc . ParentId = parentId
isInsert = true
}
2018-03-25 14:59:42 +08:00
if err := doc . InsertOrUpdate ( "document_name" , "markdown" , "content" ) ; err != nil {
beego . Error ( doc . DocumentId , err )
2018-03-24 22:36:35 +08:00
}
if isInsert {
docMap [ docIdentify ] = doc . DocumentId
}
}
2018-03-25 14:59:42 +08:00
} else {
2018-03-24 22:36:35 +08:00
//如果当前目录下存在Markdown文件, 则需要创建此节点
2018-03-25 14:59:42 +08:00
if filetil . HasFileOfExt ( path , [ ] string { ".md" , ".markdown" } ) {
beego . Info ( "正在处理 =>" , path , info . Name ( ) )
identify := strings . Replace ( strings . Trim ( strings . TrimPrefix ( path , tempPath ) , "/" ) , "/" , "-" , - 1 )
2018-03-24 22:36:35 +08:00
if ok , err := regexp . MatchString ( ` [a-z]+[a-zA-Z0-9_.\-]*$ ` , identify ) ; ! ok || err != nil {
identify = "import-" + identify
}
2018-03-24 23:17:50 +08:00
2018-03-24 22:36:35 +08:00
parentDoc := NewDocument ( )
parentDoc . MemberId = book . MemberId
parentDoc . BookId = book . BookId
parentDoc . Identify = identify
parentDoc . Version = time . Now ( ) . Unix ( )
parentDoc . DocumentName = "空白文档"
parentId := 0
2018-03-24 23:17:50 +08:00
2018-03-25 14:59:42 +08:00
parentIdentify := strings . TrimSuffix ( identify , "-" + info . Name ( ) )
2018-03-24 22:36:35 +08:00
2018-03-25 14:59:42 +08:00
if id , ok := docMap [ parentIdentify ] ; ok {
2018-03-24 22:36:35 +08:00
parentId = id
}
parentDoc . ParentId = parentId
2018-03-25 14:59:42 +08:00
if err := parentDoc . InsertOrUpdate ( ) ; err != nil {
2018-03-24 22:36:35 +08:00
beego . Error ( err )
}
2018-03-24 17:24:02 +08:00
2018-03-24 22:36:35 +08:00
docMap [ identify ] = parentDoc . DocumentId
2018-03-25 14:59:42 +08:00
//beego.Info(path,"|",parentDoc.DocumentId,"|",identify,"|",info.Name(),"|",parentIdentify)
2018-03-24 17:24:02 +08:00
}
}
return nil
} )
2018-03-25 14:59:42 +08:00
if err != nil {
beego . Error ( "导入项目异常 => " , err )
book . Description = "【项目导入存在错误:" + err . Error ( ) + "】"
}
2018-08-14 15:57:52 +08:00
beego . Info ( "项目导入完毕 => " , book . BookName )
2018-03-25 14:59:42 +08:00
book . ReleaseContent ( book . BookId )
2018-03-24 17:24:02 +08:00
return err
2018-03-25 14:59:42 +08:00
}