diff --git a/TODO b/TODO new file mode 100644 index 00000000..99e7e542 --- /dev/null +++ b/TODO @@ -0,0 +1,10 @@ +1、把 log 提取出 dbgout 之类的方法;已改作 beego.Info() 调用; +2、把源代码里的 TODO 完成; +3、Export 的两个 URL 应该可以合并,用是否有 :id 来区分;这样 0 号文档输出整个 book 更顺;已完成; +4、统一代码风格,空格、命名之类的; +5、美化 PDF 的输出格式; +6、自动登录新开标签页而且并不能跳转至起始请求页的问题; +7、用户分组管理; +8、[自动]展示历史版本;增强历史版本对比显示的能力; +9、[自动]展示作者信息;也许可以和上一需求合并考虑; +10、查看了解评论功能; diff --git a/controllers/document.go b/controllers/document.go index 48023380..172e3c12 100644 --- a/controllers/document.go +++ b/controllers/document.go @@ -29,12 +29,12 @@ import ( "github.com/russross/blackfriday" ) -//DocumentController struct. +// DocumentController struct type DocumentController struct { BaseController } -//判断用户是否可以阅读文档. +// 判断用户是否可以阅读文档 func isReadable(identify, token string, c *DocumentController) *models.BookResult { book, err := models.NewBook().FindByFieldFirst("identify", identify) @@ -43,9 +43,8 @@ func isReadable(identify, token string, c *DocumentController) *models.BookResul c.Abort("500") } - //如果文档是私有的 + // 如果文档是私有的 if book.PrivatelyOwned == 1 && !c.Member.IsAdministrator() { - is_ok := false if c.Member != nil { @@ -54,25 +53,24 @@ func isReadable(identify, token string, c *DocumentController) *models.BookResul is_ok = true } } + if book.PrivateToken != "" && !is_ok { - //如果有访问的Token,并且该项目设置了访问Token,并且和用户提供的相匹配,则记录到Session中. - //如果用户未提供Token且用户登录了,则判断用户是否参与了该项目. - //如果用户未登录,则从Session中读取Token. + // 如果有访问的 Token,并且该项目设置了访问 Token,并且和用户提供的相匹配,则记录到 Session 中。 + // 如果用户未提供 Token 且用户登录了,则判断用户是否参与了该项目。 + // 如果用户未登录,则从 Session 中读取 Token。 if token != "" && strings.EqualFold(token, book.PrivateToken) { c.SetSession(identify, token) - } else if token, ok := c.GetSession(identify).(string); !ok || !strings.EqualFold(token, book.PrivateToken) { c.Abort("403") } } else if !is_ok { c.Abort("403") } - } + bookResult := book.ToBookResult() if c.Member != nil { - rel, err := models.NewRelationship().FindByBookIdAndMemberId(bookResult.BookId, c.Member.MemberId) if err == nil { @@ -80,9 +78,9 @@ func isReadable(identify, token string, c *DocumentController) *models.BookResul bookResult.RoleId = rel.RoleId bookResult.RelationshipId = rel.RelationshipId } - } - //判断是否需要显示评论框 + + // 判断是否需要显示评论框 if bookResult.CommentStatus == "closed" { bookResult.IsDisplayComment = false } else if bookResult.CommentStatus == "open" { @@ -96,20 +94,35 @@ func isReadable(identify, token string, c *DocumentController) *models.BookResul return bookResult } -//文档首页. +func isUserLoggedIn(c *DocumentController) bool { + return c.Member != nil && c.Member.MemberId > 0 +} + +func promptUserToLogIn(c *DocumentController) { + if c.IsAjax() { + c.JsonResult(6000, "需要[重]登录。") + } else { + c.Redirect(beego.URLFor("AccountController.Login"), 302) + } +} + +// 文档首页 func (c *DocumentController) Index() { c.Prepare() + identify := c.Ctx.Input.Param(":key") token := c.GetString("token") if identify == "" { c.Abort("404") } - //如果没有开启你们访问则跳转到登录 - if !c.EnableAnonymous && c.Member == nil { - c.Redirect(beego.URLFor("AccountController.Login"), 302) + + // 如果没有开启匿名访问则跳转到登录 + if !c.EnableAnonymous && !isUserLoggedIn(c) { + promptUserToLogIn(c) return } + bookResult := isReadable(identify, token, c) c.TplName = "document/" + bookResult.Theme + "_read.tpl" @@ -124,23 +137,31 @@ func (c *DocumentController) Index() { c.Data["Model"] = bookResult c.Data["Result"] = template.HTML(tree) 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["Info"] = "" + + c.Data["DocumentId"] = "0" // added by dandycheung, 2017-12-08, for exporting + beego.Info("DocumentController.Index(): c.Data[\"DocumentId\"] = ", 0) } -//阅读文档. +// 阅读文档 func (c *DocumentController) Read() { c.Prepare() + identify := c.Ctx.Input.Param(":key") token := c.GetString("token") id := c.GetString(":id") + c.Data["DocumentId"] = id // added by dandycheung, 2017-12-08, for exporting + beego.Info("DocumentController.Read(): c.Data[\"DocumentId\"] = ", id, ", IsAjax = ", c.IsAjax()) + if identify == "" || id == "" { c.Abort("404") } - //如果没有开启你们访问则跳转到登录 - if !c.EnableAnonymous && c.Member == nil { - c.Redirect(beego.URLFor("AccountController.Login"), 302) + // 如果没有开启匿名访问则跳转到登录 + if !c.EnableAnonymous && !isUserLoggedIn(c) { + promptUserToLogIn(c) return } @@ -167,6 +188,7 @@ func (c *DocumentController) Read() { if doc.BookId != bookResult.BookId { c.Abort("403") } + attach, err := models.NewAttachment().FindListByDocumentId(doc.DocumentId) if err == nil { doc.AttachList = attach @@ -183,6 +205,7 @@ func (c *DocumentController) Read() { contentSelection.SetAttr("src", utils.JoinURI(cdnimg, src)) } }) + html, err := query.Html() if err != nil { beego.Error(err) @@ -190,17 +213,34 @@ func (c *DocumentController) Read() { doc.Release = html } } - } + + // assemble doc info, added by dandycheung, 2017-12-20 + docInfo := "" + docCreator, err := models.NewMember().Find(doc.MemberId) + if err == nil { + docInfo += docCreator.Account + } + + docInfo += " 创建于 " + docInfo += doc.CreateTime.Format("2006-01-02 15:04") + + if doc.ModifyTime != doc.CreateTime { + docInfo += ";更新于 " + docInfo += doc.ModifyTime.Format("2006-01-02 15:04") + } + if c.IsAjax() { var data struct { DocTitle string `json:"doc_title"` Body string `json:"body"` Title string `json:"title"` + DocInfo string `json:"doc_info"` } data.DocTitle = doc.DocumentName data.Body = doc.Release data.Title = doc.DocumentName + " - Powered by MinDoc" + data.DocInfo = docInfo c.JsonResult(0, "ok", data) } @@ -215,10 +255,11 @@ func (c *DocumentController) Read() { c.Data["Model"] = bookResult c.Data["Result"] = template.HTML(tree) c.Data["Title"] = doc.DocumentName + c.Data["Info"] = docInfo c.Data["Content"] = template.HTML(doc.Release) } -//编辑文档. +// 编辑文档 func (c *DocumentController) Edit() { c.Prepare() @@ -228,30 +269,30 @@ func (c *DocumentController) Edit() { } bookResult := models.NewBookResult() + var err error - //如果是超级管理者,则不判断权限 + // 如果是超级管理者,则不判断权限 if c.Member.IsAdministrator() { book, err := models.NewBook().FindByFieldFirst("identify", identify) if err != nil { c.JsonResult(6002, "项目不存在或权限不足") } - bookResult = book.ToBookResult() + bookResult = book.ToBookResult() } else { bookResult, err = models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) if err != nil { beego.Error("DocumentController.Edit => ", err) - c.Abort("403") } - if bookResult.RoleId == conf.BookObserver { + if bookResult.RoleId == conf.BookObserver { c.JsonResult(6002, "项目不存在或权限不足") } } - //根据不同编辑器类型加载编辑器 + // 根据不同编辑器类型加载编辑器 if bookResult.Editor == "markdown" { c.TplName = "document/markdown_edit_template.tpl" } else if bookResult.Editor == "html" { @@ -281,11 +322,11 @@ func (c *DocumentController) Edit() { c.Data["Result"] = template.JS("[]") } } - c.Data["BaiDuMapKey"] = beego.AppConfig.DefaultString("baidumapkey", "") + c.Data["BaiDuMapKey"] = beego.AppConfig.DefaultString("baidumapkey", "") } -//创建一个文档. +// 创建一个文档 func (c *DocumentController) Create() { identify := c.GetString("identify") doc_identify := c.GetString("doc_identify") @@ -296,27 +337,32 @@ func (c *DocumentController) Create() { if identify == "" { c.JsonResult(6001, "参数错误") } + if doc_name == "" { c.JsonResult(6004, "文档名称不能为空") } + if doc_identify != "" { if ok, err := regexp.MatchString(`^[a-z]+[a-zA-Z0-9_\-]*$`, doc_identify); !ok || err != nil { - c.JsonResult(6003, "文档标识只能包含小写字母、数字,以及“-”和“_”符号,并且只能小写字母开头") } + d, _ := models.NewDocument().FindByFieldFirst("identify", doc_identify) if d.DocumentId > 0 && d.DocumentId != doc_id { c.JsonResult(6006, "文档标识已被使用") } } + book_id := 0 - //如果是超级管理员则不判断权限 + + // 如果是超级管理员则不判断权限 if c.Member.IsAdministrator() { book, err := models.NewBook().FindByFieldFirst("identify", identify) if err != nil { beego.Error(err) c.JsonResult(6002, "项目不存在或权限不足") } + book_id = book.BookId } else { bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) @@ -325,8 +371,10 @@ func (c *DocumentController) Create() { beego.Error("FindByIdentify => ", err) c.JsonResult(6002, "项目不存在或权限不足") } + book_id = bookResult.BookId } + if parent_id > 0 { doc, err := models.NewDocument().Find(parent_id) if err != nil || doc.BookId != book_id { @@ -338,9 +386,11 @@ func (c *DocumentController) Create() { document.MemberId = c.Member.MemberId document.BookId = book_id + if doc_identify != "" { document.Identify = doc_identify } + document.Version = time.Now().Unix() document.DocumentName = doc_name document.ParentId = parent_id @@ -349,14 +399,12 @@ func (c *DocumentController) Create() { beego.Error("InsertOrUpdate => ", err) c.JsonResult(6005, "保存失败") } else { - c.JsonResult(0, "ok", document) } } -//上传附件或图片. +// 上传附件或图片 func (c *DocumentController) Upload() { - identify := c.GetString("identify") doc_id, _ := c.GetInt("doc_id") is_attach := true @@ -375,6 +423,7 @@ func (c *DocumentController) Upload() { c.JsonResult(6003, "没有发现需要上传的文件") } } + if err != nil { c.JsonResult(6002, err.Error()) } @@ -390,16 +439,18 @@ func (c *DocumentController) Upload() { if !conf.IsAllowUploadFileExt(ext) { c.JsonResult(6004, "不允许的文件类型") } + book_id := 0 - //如果是超级管理员,则不判断权限 + + // 如果是超级管理员,则不判断权限 if c.Member.IsAdministrator() { book, err := models.NewBook().FindByFieldFirst("identify", identify) if err != nil { c.JsonResult(6006, "文档不存在或权限不足") } - book_id = book.BookId + book_id = book.BookId } else { book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) @@ -408,12 +459,15 @@ func (c *DocumentController) Upload() { if err == orm.ErrNoRows { c.JsonResult(6006, "权限不足") } + c.JsonResult(6001, err.Error()) } - //如果没有编辑权限 + + // 如果没有编辑权限 if book.RoleId != conf.BookEditor && book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder { c.JsonResult(6006, "权限不足") } + book_id = book.BookId } @@ -422,15 +476,14 @@ func (c *DocumentController) Upload() { if err != nil { c.JsonResult(6007, "文档不存在") } + if doc.BookId != book_id { c.JsonResult(6008, "文档不属于指定的项目") } } fileName := "attach_" + strconv.FormatInt(time.Now().UnixNano(), 16) - filePath := filepath.Join(commands.WorkingDirectory, "uploads", time.Now().Format("200601"), fileName+ext) - path := filepath.Dir(filePath) os.MkdirAll(path, os.ModePerm) @@ -441,6 +494,7 @@ func (c *DocumentController) Upload() { beego.Error("SaveToFile => ", err) c.JsonResult(6005, "保存文件失败") } + attachment := models.NewAttachment() attachment.BookId = book_id attachment.FileName = moreFile.Filename @@ -452,16 +506,17 @@ func (c *DocumentController) Upload() { if fileInfo, err := os.Stat(filePath); err == nil { attachment.FileSize = float64(fileInfo.Size()) } + if doc_id > 0 { attachment.DocumentId = doc_id } if strings.EqualFold(ext, ".jpg") || strings.EqualFold(ext, ".jpeg") || strings.EqualFold(ext, ".png") || strings.EqualFold(ext, ".gif") { - attachment.HttpPath = "/" + strings.Replace(strings.TrimPrefix(filePath, commands.WorkingDirectory), "\\", "/", -1) if strings.HasPrefix(attachment.HttpPath, "//") { attachment.HttpPath = string(attachment.HttpPath[1:]) } + is_attach = false } @@ -472,6 +527,7 @@ func (c *DocumentController) Upload() { beego.Error("Attachment Insert => ", err) c.JsonResult(6006, "文件保存失败") } + if attachment.HttpPath == "" { attachment.HttpPath = beego.URLFor("DocumentController.DownloadAttachment", ":key", identify, ":attach_id", attachment.AttachmentId) @@ -490,11 +546,12 @@ func (c *DocumentController) Upload() { "is_attach": is_attach, "attach": attachment, } + c.Ctx.Output.JSON(result, true, false) c.StopRun() } -//DownloadAttachment 下载附件. +// 下载附件 func (c *DocumentController) DownloadAttachment() { c.Prepare() @@ -507,20 +564,22 @@ func (c *DocumentController) DownloadAttachment() { if c.Member != nil { member_id = c.Member.MemberId } + book_id := 0 - //判断用户是否参与了项目 + // 判断用户是否参与了项目 bookResult, err := models.NewBookResult().FindByIdentify(identify, member_id) if err != nil { - //判断项目公开状态 + // 判断项目公开状态 book, err := models.NewBook().FindByFieldFirst("identify", identify) if err != nil { c.Abort("404") } - //如果不是超级管理员则判断权限 + + // 如果不是超级管理员则判断权限 if c.Member == nil || c.Member.Role != conf.MemberSuperRole { - //如果项目是私有的,并且token不正确 + // 如果项目是私有的,并且 token 不正确 if (book.PrivatelyOwned == 1 && token == "") || (book.PrivatelyOwned == 1 && book.PrivateToken != token) { c.Abort("403") } @@ -530,7 +589,8 @@ func (c *DocumentController) DownloadAttachment() { } else { book_id = bookResult.BookId } - //查找附件 + + // 查找附件 attachment, err := models.NewAttachment().Find(attach_id) if err != nil { @@ -541,15 +601,16 @@ func (c *DocumentController) DownloadAttachment() { c.Abort("500") } } + if attachment.BookId != book_id { c.Abort("404") } - c.Ctx.Output.Download(filepath.Join(commands.WorkingDirectory, attachment.FilePath), attachment.FileName) + c.Ctx.Output.Download(filepath.Join(commands.WorkingDirectory, attachment.FilePath), attachment.FileName) c.StopRun() } -//删除附件. +// 删除附件 func (c *DocumentController) RemoveAttachment() { c.Prepare() attach_id, _ := c.GetInt("attach_id") @@ -557,40 +618,45 @@ func (c *DocumentController) RemoveAttachment() { if attach_id <= 0 { c.JsonResult(6001, "参数错误") } + attach, err := models.NewAttachment().Find(attach_id) if err != nil { beego.Error(err) c.JsonResult(6002, "附件不存在") } + document, err := models.NewDocument().Find(attach.DocumentId) if err != nil { beego.Error(err) c.JsonResult(6003, "文档不存在") } + if c.Member.Role != conf.MemberSuperRole { rel, err := models.NewRelationship().FindByBookIdAndMemberId(document.BookId, c.Member.MemberId) if err != nil { beego.Error(err) c.JsonResult(6004, "权限不足") } + if rel.RoleId == conf.BookObserver { c.JsonResult(6004, "权限不足") } } - err = attach.Delete() + err = attach.Delete() if err != nil { beego.Error(err) c.JsonResult(6005, "删除失败") } + os.Remove(filepath.Join(commands.WorkingDirectory, attach.FilePath)) c.JsonResult(0, "ok", attach) } -//删除文档. +// 删除文档 func (c *DocumentController) Delete() { c.Prepare() @@ -598,13 +664,15 @@ func (c *DocumentController) Delete() { doc_id, err := c.GetInt("doc_id", 0) book_id := 0 - //如果是超级管理员则忽略权限判断 + + // 如果是超级管理员则忽略权限判断 if c.Member.IsAdministrator() { book, err := models.NewBook().FindByFieldFirst("identify", identify) if err != nil { beego.Error("FindByIdentify => ", err) c.JsonResult(6002, "项目不存在或权限不足") } + book_id = book.BookId } else { bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) @@ -613,6 +681,7 @@ func (c *DocumentController) Delete() { beego.Error("FindByIdentify => ", err) c.JsonResult(6002, "项目不存在或权限不足") } + book_id = bookResult.BookId } @@ -626,21 +695,23 @@ func (c *DocumentController) Delete() { beego.Error("Delete => ", err) c.JsonResult(6003, "删除失败") } - //如果文档所属项目错误 + // 如果文档所属项目错误 if doc.BookId != book_id { c.JsonResult(6004, "参数错误") } - //递归删除项目下的文档以及子文档 + + // 递归删除项目下的文档以及子文档 err = doc.RecursiveDocument(doc.DocumentId) if err != nil { c.JsonResult(6005, "删除失败") } - //重置文档数量统计 + + // 重置文档数量统计 models.NewBook().ResetDocumentNumber(doc.BookId) c.JsonResult(0, "ok") } -//获取文档内容. +// 获取文档内容 func (c *DocumentController) Content() { c.Prepare() @@ -650,13 +721,16 @@ func (c *DocumentController) Content() { if err != nil { doc_id, _ = strconv.Atoi(c.Ctx.Input.Param(":id")) } + book_id := 0 - //如果是超级管理员,则忽略权限 + + // 如果是超级管理员,则忽略权限 if c.Member.IsAdministrator() { book, err := models.NewBook().FindByFieldFirst("identify", identify) if err != nil { c.JsonResult(6002, "项目不存在或权限不足") } + book_id = book.BookId } else { bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) @@ -665,6 +739,7 @@ func (c *DocumentController) Content() { beego.Error("FindByIdentify => ", err) c.JsonResult(6002, "项目不存在或权限不足") } + book_id = bookResult.BookId } @@ -683,13 +758,16 @@ func (c *DocumentController) Content() { if err != nil { c.JsonResult(6003, "读取文档错误") } + if doc.BookId != book_id { c.JsonResult(6004, "保存的文档不属于指定项目") } + if doc.Version != version && !strings.EqualFold(is_cover, "yes") { beego.Info("%d|", version, doc.Version) c.JsonResult(6005, "文档已被修改确定要覆盖吗?") } + history := models.NewDocumentHistory() history.DocumentId = doc_id history.Content = doc.Content @@ -707,13 +785,16 @@ func (c *DocumentController) Content() { } else { doc.Markdown = markdown } + doc.Version = time.Now().Unix() doc.Content = content + if err := doc.InsertOrUpdate(); err != nil { beego.Error("InsertOrUpdate => ", err) c.JsonResult(6006, "保存失败") } - //如果启用了文档历史,则添加历史文档 + + // 如果启用了文档历史,则添加历史文档 if c.EnableDocumentHistory { _, err = history.InsertOrUpdate() if err != nil { @@ -723,37 +804,64 @@ func (c *DocumentController) Content() { c.JsonResult(0, "ok", doc) } - doc, err := models.NewDocument().Find(doc_id) + doc, err := models.NewDocument().Find(doc_id) if err != nil { c.JsonResult(6003, "文档不存在") } + attach, err := models.NewAttachment().FindListByDocumentId(doc.DocumentId) if err == nil { doc.AttachList = attach } + c.JsonResult(0, "ok", doc) } -//导出文件 -func (c *DocumentController) Export() { +func (c *DocumentController) ExportDoc() { + 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.TplName = "document/export.tpl" identify := c.Ctx.Input.Param(":key") - - output := c.GetString("output") - - token := c.GetString("token") - if identify == "" { c.Abort("404") } - //如果没有开启你们访问则跳转到登录 - if !c.EnableAnonymous && c.Member == nil { - c.Redirect(beego.URLFor("AccountController.Login"), 302) + + output := c.GetString("output") + token := c.GetString("token") + + // 如果没有开启匿名访问则跳转到登录 + if !c.EnableAnonymous && !isUserLoggedIn(c) { + promptUserToLogIn(c) return } + bookResult := models.NewBookResult() if c.Member != nil && c.Member.IsAdministrator() { book, err := models.NewBook().FindByIdentify(identify) @@ -761,59 +869,70 @@ func (c *DocumentController) Export() { beego.Error(err) c.Abort("500") } + bookResult = book.ToBookResult() } else { bookResult = isReadable(identify, token, c) } - if bookResult.PrivatelyOwned == 0 { - //TODO 私有项目禁止导出 - } - docs, err := models.NewDocument().FindListByBookId(bookResult.BookId) + if bookResult.PrivatelyOwned == 0 { + // TODO: 私有项目禁止导出 + } + + docs, err := models.NewDocument().FindListByBookId(bookResult.BookId) if err != nil { beego.Error(err) c.Abort("500") } if output == "pdf" { - exe := beego.AppConfig.String("wkhtmltopdf") - if exe == "" { c.TplName = "errors/error.tpl" c.Data["ErrorMessage"] = "没有配置PDF导出程序" c.Data["ErrorCode"] = 50010 return } - dpath := "cache/" + bookResult.Identify + dpath := "cache/" + bookResult.Identify os.MkdirAll(dpath, 0766) 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) + // TODO: check if the pathList is empty + os.MkdirAll("./cache", 0766) pdfpath := filepath.Join("cache", identify+"_"+c.CruSession.SessionID()+".pdf") if _, err := os.Stat(pdfpath); os.IsNotExist(err) { - wkhtmltopdf.SetPath(beego.AppConfig.String("wkhtmltopdf")) - pdfg, err := wkhtmltopdf.NewPDFGenerator() - pdfg.MarginBottom.Set(35) + pdfg, err := wkhtmltopdf.NewPDFGenerator() if err != nil { beego.Error(err) c.Abort("500") } + pdfg.MarginBottom.Set(35) + for e := pathList.Front(); e != nil; e = e.Next() { if page, ok := e.Value.(string); ok { pdfg.AddPage(wkhtmltopdf.NewPage(page)) } } + err = pdfg.Create() if err != nil { beego.Error(err) @@ -836,13 +955,13 @@ func (c *DocumentController) Export() { c.Abort("404") } -//生成项目访问的二维码. +// 生成项目访问的二维码 func (c *DocumentController) QrCode() { c.Prepare() + identify := c.GetString(":key") book, err := models.NewBook().FindByIdentify(identify) - if err != nil || book.BookId <= 0 { c.Abort("404") } @@ -853,15 +972,16 @@ func (c *DocumentController) QrCode() { beego.Error(err) c.Abort("500") } - code, err = barcode.Scale(code, 150, 150) + code, err = barcode.Scale(code, 150, 150) if err != nil { beego.Error(err) c.Abort("500") } + c.Ctx.ResponseWriter.Header().Set("Content-Type", "image/png") - //imgpath := filepath.Join("cache","qrcode",identify + ".png") + // imgpath := filepath.Join("cache","qrcode",identify + ".png") err = png.Encode(c.Ctx.ResponseWriter, code) if err != nil { @@ -870,7 +990,7 @@ func (c *DocumentController) QrCode() { } } -//项目内搜索. +// 项目内搜索 func (c *DocumentController) Search() { c.Prepare() @@ -881,21 +1001,24 @@ func (c *DocumentController) Search() { if identify == "" { c.JsonResult(6001, "参数错误") } - if !c.EnableAnonymous && c.Member == nil { - c.Redirect(beego.URLFor("AccountController.Login"), 302) + + if !c.EnableAnonymous && !isUserLoggedIn(c) { + promptUserToLogIn(c) return } + bookResult := isReadable(identify, token, c) docs, err := models.NewDocumentSearchResult().SearchDocument(keyword, bookResult.BookId) - if err != nil { beego.Error(err) c.JsonResult(6002, "搜索结果错误") } + if len(docs) < 0 { c.JsonResult(404, "没有数据库") } + for _, doc := range docs { doc.BookId = bookResult.BookId doc.BookName = bookResult.BookName @@ -906,9 +1029,10 @@ func (c *DocumentController) Search() { c.JsonResult(0, "ok", docs) } -//文档历史列表. +// 文档历史列表 func (c *DocumentController) History() { c.Prepare() + c.TplName = "document/history.tpl" identify := c.GetString("identify") @@ -916,7 +1040,8 @@ func (c *DocumentController) History() { pageIndex, _ := c.GetInt("page", 1) book_id := 0 - //如果是超级管理员则忽略权限判断 + + // 如果是超级管理员则忽略权限判断 if c.Member.IsAdministrator() { book, err := models.NewBook().FindByFieldFirst("identify", identify) if err != nil { @@ -924,16 +1049,17 @@ func (c *DocumentController) History() { c.Data["ErrorMessage"] = "项目不存在或权限不足" return } + book_id = book.BookId c.Data["Model"] = book } else { bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) - if err != nil || bookResult.RoleId == conf.BookObserver { beego.Error("FindByIdentify => ", err) c.Data["ErrorMessage"] = "项目不存在或权限不足" return } + book_id = bookResult.BookId c.Data["Model"] = bookResult } @@ -944,20 +1070,19 @@ func (c *DocumentController) History() { } doc, err := models.NewDocument().Find(doc_id) - if err != nil { beego.Error("Delete => ", err) c.Data["ErrorMessage"] = "获取历史失败" return } - //如果文档所属项目错误 + + // 如果文档所属项目错误 if doc.BookId != book_id { c.Data["ErrorMessage"] = "参数错误" return } historis, totalCount, err := models.NewDocumentHistory().FindToPager(doc_id, pageIndex, conf.PageSize) - if err != nil { beego.Error("FindToPager => ", err) c.Data["ErrorMessage"] = "获取历史失败" @@ -970,13 +1095,13 @@ func (c *DocumentController) History() { if totalCount > 0 { html := utils.GetPagerHtml(c.Ctx.Request.RequestURI, pageIndex, conf.PageSize, totalCount) - c.Data["PageHtml"] = html } } func (c *DocumentController) DeleteHistory() { c.Prepare() + c.TplName = "document/history.tpl" identify := c.GetString("identify") @@ -986,22 +1111,25 @@ func (c *DocumentController) DeleteHistory() { if history_id <= 0 { c.JsonResult(6001, "参数错误") } + book_id := 0 - //如果是超级管理员则忽略权限判断 + + // 如果是超级管理员则忽略权限判断 if c.Member.IsAdministrator() { book, err := models.NewBook().FindByFieldFirst("identify", identify) if err != nil { beego.Error("FindByIdentify => ", err) c.JsonResult(6002, "项目不存在或权限不足") } + book_id = book.BookId } else { bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) - if err != nil || bookResult.RoleId == conf.BookObserver { beego.Error("FindByIdentify => ", err) c.JsonResult(6002, "项目不存在或权限不足") } + book_id = bookResult.BookId } @@ -1010,25 +1138,28 @@ func (c *DocumentController) DeleteHistory() { } doc, err := models.NewDocument().Find(doc_id) - if err != nil { beego.Error("Delete => ", err) c.JsonResult(6001, "获取历史失败") } - //如果文档所属项目错误 + + // 如果文档所属项目错误 if doc.BookId != book_id { c.JsonResult(6001, "参数错误") } + err = models.NewDocumentHistory().Delete(history_id, doc_id) if err != nil { beego.Error(err) c.JsonResult(6002, "删除失败") } + c.JsonResult(0, "ok") } func (c *DocumentController) RestoreHistory() { c.Prepare() + c.TplName = "document/history.tpl" identify := c.GetString("identify") @@ -1038,22 +1169,24 @@ func (c *DocumentController) RestoreHistory() { if history_id <= 0 { c.JsonResult(6001, "参数错误") } + book_id := 0 - //如果是超级管理员则忽略权限判断 + // 如果是超级管理员则忽略权限判断 if c.Member.IsAdministrator() { book, err := models.NewBook().FindByFieldFirst("identify", identify) if err != nil { beego.Error("FindByIdentify => ", err) c.JsonResult(6002, "项目不存在或权限不足") } + book_id = book.BookId } else { bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) - if err != nil || bookResult.RoleId == conf.BookObserver { beego.Error("FindByIdentify => ", err) c.JsonResult(6002, "项目不存在或权限不足") } + book_id = bookResult.BookId } @@ -1062,33 +1195,37 @@ func (c *DocumentController) RestoreHistory() { } doc, err := models.NewDocument().Find(doc_id) - if err != nil { beego.Error("Delete => ", err) c.JsonResult(6001, "获取历史失败") } - //如果文档所属项目错误 + + // 如果文档所属项目错误 if doc.BookId != book_id { c.JsonResult(6001, "参数错误") } + err = models.NewDocumentHistory().Restore(history_id, doc_id, c.Member.MemberId) if err != nil { beego.Error(err) c.JsonResult(6002, "删除失败") } + c.JsonResult(0, "ok", doc) } -func (c *DocumentController) Compare() { +func (c *DocumentController) Compare() { c.Prepare() + c.TplName = "document/compare.tpl" - history_id ,_ := strconv.Atoi(c.Ctx.Input.Param(":id")) + + history_id, _ := strconv.Atoi(c.Ctx.Input.Param(":id")) identify := c.Ctx.Input.Param(":key") book_id := 0 editor := "markdown" - //如果是超级管理员则忽略权限判断 + // 如果是超级管理员则忽略权限判断 if c.Member.IsAdministrator() { book, err := models.NewBook().FindByFieldFirst("identify", identify) if err != nil { @@ -1096,95 +1233,102 @@ func (c *DocumentController) Compare() { c.Abort("403") return } + book_id = book.BookId c.Data["Model"] = book editor = book.Editor } else { bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId) - if err != nil || bookResult.RoleId == conf.BookObserver { beego.Error("FindByIdentify => ", err) c.Abort("403") return } + book_id = bookResult.BookId c.Data["Model"] = bookResult editor = bookResult.Editor } if history_id <= 0 { - c.ShowErrorPage(60002,"参数错误") + c.ShowErrorPage(60002, "参数错误") } - history,err := models.NewDocumentHistory().Find(history_id) + history, err := models.NewDocumentHistory().Find(history_id) if err != nil { - beego.Error("DocumentController.Compare => ",err) - c.ShowErrorPage(60003,err.Error()) + beego.Error("DocumentController.Compare => ", err) + c.ShowErrorPage(60003, err.Error()) } - doc,err := models.NewDocument().Find(history.DocumentId) + doc, err := models.NewDocument().Find(history.DocumentId) if doc.BookId != book_id { - c.ShowErrorPage(60002,"参数错误") + c.ShowErrorPage(60002, "参数错误") } + c.Data["HistoryId"] = history_id c.Data["DocumentId"] = doc.DocumentId if editor == "markdown" { c.Data["HistoryContent"] = history.Markdown c.Data["Content"] = doc.Markdown - }else{ + } else { c.Data["HistoryContent"] = template.HTML(history.Content) c.Data["Content"] = template.HTML(doc.Content) } } -//递归生成文档序列数组. +// 递归生成文档序列数组 func RecursiveFun(parent_id int, prefix, dpath string, c *DocumentController, book *models.BookResult, docs []*models.Document, paths *list.List) { for _, item := range docs { if item.ParentId == parent_id { - 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,"tbody>tr:hover{ } .manual-article .article-head { position: relative; - zoom:1;padding: 10px 20px + zoom: 1; + padding: 10px 20px } .manual-reader .book-title{ color: #333333; @@ -430,7 +431,7 @@ table>tbody>tr:hover{ .manual-article .article-head h1 { margin: 0; font-size: 20px; - font-weight: 200; + font-weight: 300; text-align: center; line-height: 30px; overflow: hidden; @@ -438,6 +439,17 @@ table>tbody>tr:hover{ white-space: nowrap; color: #444 } +.manual-article .article-head h3 { + margin: 0; + font-size: 12px; + font-weight: 200; + text-align: center; + line-height: 18px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: #444 +} .manual-article .article-content{ min-width: 980px; max-width: 98%; diff --git a/static/js/kancloud.js b/static/js/kancloud.js index a333e5ff..5915ba91 100644 --- a/static/js/kancloud.js +++ b/static/js/kancloud.js @@ -4,51 +4,58 @@ * @param $id * @param $callback */ -function loadDocument($url,$id,$callback) { +function loadDocument($url, $id, $callback) { $.ajax({ url : $url, type : "GET", - beforeSend :function (xhr) { + beforeSend : function (xhr) { var body = events.data('body_' + $id); var title = events.data('title_' + $id); var doc_title = events.data('doc_title_' + $id); + var doc_info = events.data('doc_info_' + $id); - if(body && title && doc_title){ - + if (body && title && doc_title) { if (typeof $callback === "function") { body = $callback(body); } + $("#page-content").html(body); $("title").text(title); $("#article-title").text(doc_title); + $("#article-info").text(doc_info); - events.trigger('article.open',{ $url : $url, $init : false , $id : $id }); + events.trigger('article.open', { $url : $url, $init : false, $id : $id }); return false; } NProgress.start(); }, success : function (res) { - if(res.errcode === 0){ + if (res.errcode === 0) { var body = res.data.body; var doc_title = res.data.doc_title; var title = res.data.title; + var doc_info = res.data.doc_info; $body = body; - if (typeof $callback === "function" ){ + if (typeof $callback === "function" ) { $body = $callback(body); } + $("#page-content").html($body); $("title").text(title); $("#article-title").text(doc_title); + $("#article-info").text(doc_info); - events.data('body_' + $id,body); - events.data('title_' + $id,title); - events.data('doc_title_' + $id,doc_title); + events.data('body_' + $id, body); + events.data('title_' + $id, title); + events.data('doc_title_' + $id, doc_title); + events.data('doc_info_' + $id, doc_info); - events.trigger('article.open',{ $url : $url, $init : true, $id : $id }); - - }else{ + events.trigger('article.open', { $url : $url, $init : true, $id : $id }); + } else if (res.errcode === 6000) { + window.location.href = "/"; + } else { layer.msg("加载失败"); } }, @@ -74,9 +81,9 @@ $(function () { }); $(".manual-right").scroll(function () { var top = $(".manual-right").scrollTop(); - if(top > 100){ + if (top > 100) { $(".view-backtop").addClass("active"); - }else{ + } else { $(".view-backtop").removeClass("active"); } }); @@ -85,7 +92,7 @@ $(function () { initHighlighting(); window.jsTree = $("#sidebar").jstree({ - 'plugins':["wholerow","types"], + 'plugins' : ["wholerow", "types"], "types": { "default" : { "icon" : false // 删除默认图标 @@ -93,31 +100,31 @@ $(function () { }, 'core' : { 'check_callback' : true, - "multiple" : false , + "multiple" : false, 'animation' : 0 } - }).on('select_node.jstree',function (node,selected,event) { + }).on('select_node.jstree', function (node, selected, event) { $(".m-manual").removeClass('manual-mobile-show-left'); var url = selected.node.a_attr.href; - if(url === window.location.href){ + if (url === window.location.href) { return false; } - loadDocument(url,selected.node.id); + loadDocument(url, selected.node.id); }); - $("#slidebar").on("click",function () { + $("#slidebar").on("click", function () { $(".m-manual").addClass('manual-mobile-show-left'); }); - $(".manual-mask").on("click",function () { + $(".manual-mask").on("click", function () { $(".m-manual").removeClass('manual-mobile-show-left'); }); /** * 关闭侧边栏 */ - $(".manual-fullscreen-switch").on("click",function () { + $(".manual-fullscreen-switch").on("click", function () { isFullScreen = !isFullScreen; if (isFullScreen) { $(".m-manual").addClass('manual-fullscreen-active'); @@ -126,24 +133,23 @@ $(function () { } }); - //处理打开事件 + // 处理打开事件 events.on('article.open', function (event, $param) { - if ('pushState' in history) { if ($param.$init === false) { - window.history.replaceState($param , $param.$id , $param.$url); + window.history.replaceState($param, $param.$id, $param.$url); } else { - window.history.pushState($param, $param.$id , $param.$url); + window.history.pushState($param, $param.$id, $param.$url); } - } else { window.location.hash = $param.$url; } + initHighlighting(); $(".manual-right").scrollTop(0); }); - $(".navg-item[data-mode]").on("click",function () { + $(".navg-item[data-mode]").on("click", function () { var mode = $(this).data('mode'); $(this).siblings().removeClass('active').end().addClass('active'); $(".m-manual").removeClass("manual-mode-view manual-mode-collect manual-mode-search").addClass("manual-mode-" + mode); @@ -155,25 +161,25 @@ $(function () { $("#searchForm").ajaxForm({ beforeSubmit : function () { var keyword = $.trim($("#searchForm").find("input[name='keyword']").val()); - if(keyword === ""){ + if (keyword === "") { $(".search-empty").show(); $("#searchList").html(""); return false; } - $("#btnSearch").attr("disabled","disabled").find("i").removeClass("fa-search").addClass("loading"); + $("#btnSearch").attr("disabled", "disabled").find("i").removeClass("fa-search").addClass("loading"); window.keyword = keyword; }, - success :function (res) { + success : function (res) { var html = ""; - if(res.errcode === 0){ - for(var i in res.data){ + if (res.errcode === 0) { + for(var i in res.data) { var item = res.data[i]; - html += '
  • '+ item.doc_name +'
  • '; + html += '
  • ' + item.doc_name + '
  • '; } } - if(html !== ""){ + if (html !== "") { $(".search-empty").hide(); - }else{ + } else { $(".search-empty").show(); } $("#searchList").html(html); @@ -184,7 +190,6 @@ $(function () { }); window.onpopstate = function (e) { - var $param = e.state; console.log($param); if($param.hasOwnProperty("$url")) { @@ -192,18 +197,18 @@ $(function () { window.jsTree.jstree().select_node({ id : $param.$id }); $param.$init = false; - //events.trigger('article.open', $param ); - }else{ + // events.trigger('article.open', $param); + } else { console.log($param); } }; try { var $node = window.jsTree.jstree().get_selected(); if (typeof $node === "object") { - $node = window.jsTree.jstree().get_node({id: $node[0]}); - events.trigger('article.open', {$url: $node.a_attr.href, $init: true, $id: $node.a_attr.id}); + $node = window.jsTree.jstree().get_node({ id: $node[0] }); + events.trigger('article.open', { $url: $node.a_attr.href, $init: true, $id: $node.a_attr.id }); } - }catch (e){ + } catch (e) { console.log(e); } }); \ No newline at end of file diff --git a/views/document/default_read.tpl b/views/document/default_read.tpl index 4dfc1912..c886ea72 100644 --- a/views/document/default_read.tpl +++ b/views/document/default_read.tpl @@ -58,7 +58,8 @@ {{if eq .Model.PrivatelyOwned 0}}
  • 项目分享
  • -
  • 项目导出PDF
  • +
  • 文档导出为 PDF
  • +
  • 项目导出为 PDF
  • {{end}}
  • 返回首页
  • @@ -127,6 +128,7 @@

    {{.Title}}

    +

    {{.Info}}

    @@ -234,6 +236,13 @@ \ No newline at end of file