-
+ {{range $index,$item := .Model.AttachList}}
+
- {{$item.FileName}} + {{end}} +
diff --git a/controllers/BaseController.go b/controllers/BaseController.go index a19072db..49edeb44 100644 --- a/controllers/BaseController.go +++ b/controllers/BaseController.go @@ -157,6 +157,10 @@ func (c *BaseController) ShowErrorPage(errCode int, errMsg string) { if err := beego.ExecuteViewPathTemplate(&buf, "errors/error.tpl", beego.BConfig.WebConfig.ViewsPath, map[string]interface{}{"ErrorMessage": errMsg, "ErrorCode": errCode, "BaseUrl": conf.BaseUrl}); err != nil { c.Abort("500") } + if errCode >= 200 && errCode <= 510 { + c.CustomAbort(errCode, buf.String()) + }else{ + c.CustomAbort(200, buf.String()) + } - c.CustomAbort(200, buf.String()) } diff --git a/controllers/BlogController.go b/controllers/BlogController.go index 27bcaa82..800278b7 100644 --- a/controllers/BlogController.go +++ b/controllers/BlogController.go @@ -13,25 +13,89 @@ import ( "net/http" "path/filepath" "github.com/astaxie/beego/orm" + "html/template" + "encoding/json" ) type BlogController struct{ BaseController } - +//文章阅读 func (c *BlogController) Index() { c.Prepare() c.TplName = "blog/index.tpl" + blogId,_ := strconv.Atoi(c.Ctx.Input.Param(":id")) + + if blogId <= 0{ + c.ShowErrorPage(404,"页面不存在") + } + blogReadSession := fmt.Sprintf("blog:read:%d",blogId) + + blog,err := models.NewBlog().Find(blogId) + + if err != nil { + c.ShowErrorPage(404,"文章不存在") + } + + if c.Ctx.Input.IsPost() { + password := c.GetString("password"); + + if blog.BlogStatus == "password" && password != blog.Password { + c.JsonResult(6001,"文章密码不正确") + }else if blog.BlogStatus == "password" && password == blog.Password { + //如果密码输入正确,则存入session中 + c.CruSession.Set(blogReadSession,blogId) + c.JsonResult(0,"OK") + } + c.JsonResult(0,"OK") + }else if blog.BlogStatus == "password" && c.CruSession.Get(blogReadSession) == nil { + //如果不存在已输入密码的标记 + c.TplName = "blog/index_password.tpl" + } + //加载文章附件 + blog.LinkAttach(); + c.Data["Model"] = blog + c.Data["Content"] = template.HTML(blog.BlogRelease) + + if nextBlog,err := models.NewBlog().QueryNext(blogId);err == nil { + c.Data["Next"] = nextBlog + } + if preBlog,err := models.NewBlog().QueryPrevious(blogId);err == nil { + c.Data["Previous"] = preBlog + } } +//文章列表 func (c *BlogController) List() { c.Prepare() c.TplName = "blog/list.tpl" + pageIndex, _ := c.GetInt("page", 1) + var blogList []*models.Blog + var totalCount int + var err error + + blogList,totalCount,err = models.NewBlog().FindToPager(pageIndex,conf.PageSize,0,"") + + + if err != nil { + c.ShowErrorPage(500,err.Error()) + } + if totalCount > 0 { + pager := pagination.NewPagination(c.Ctx.Request, totalCount, conf.PageSize, c.BaseUrl()) + c.Data["PageHtml"] = pager.HtmlPages() + for _,blog := range blogList { + blog.Link() + } + } else { + c.Data["PageHtml"] = "" + } + + c.Data["Lists"] = blogList } -//管理后台 +//管理后台文章列表 func (c *BlogController) ManageList() { c.Prepare() c.TplName = "blog/manage_list.tpl" @@ -40,7 +104,6 @@ func (c *BlogController) ManageList() { blogList,totalCount,err := models.NewBlog().FindToPager(pageIndex,conf.PageSize,c.Member.MemberId,"") - beego.Info(totalCount) if err != nil { c.ShowErrorPage(500,err.Error()) } @@ -66,14 +129,17 @@ func (c *BlogController) ManageSetting() { blogIdentify := c.GetString("identify") orderIndex,_ := c.GetInt("order_index",0) blogType,_ := c.GetInt("blog_type",0) - documentId,_ := c.GetInt("document_id",0) blogExcerpt := c.GetString("excerpt","") blogStatus := c.GetString("status","publish") blogPassword := c.GetString("password","") + documentIdentify := strings.TrimSpace(c.GetString("documentIdentify")) + bookIdentify := strings.TrimSpace(c.GetString("bookIdentify")) + documentId := 0 + if blogTitle == "" { c.JsonResult(6001,"文章标题不能为空") } - if strings.Count(blogExcerpt,"") > 100 { + if strings.Count(blogExcerpt,"") > 500 { c.JsonResult(6008,"文章摘要必须小于500字符") } if blogStatus != "public" && blogStatus != "password" && blogStatus != "draft" { @@ -84,14 +150,33 @@ func (c *BlogController) ManageSetting() { } if blogType != 0 && blogType != 1 { c.JsonResult(6005,"未知的文章类型") - }else if documentId <= 0 && blogType == 1 { - c.JsonResult(6006,"请选择链接的文章") - }else if blogType == 1 && documentId > 0 && !models.NewDocument().IsExist(documentId){ - c.JsonResult(6007,"链接的文章不存在") } if strings.Count(blogTitle,"") > 200 { c.JsonResult(6002,"文章标题不能大于200个字符") } + //如果是关联文章,需要同步关联的文档 + if blogType == 1 { + book,err := models.NewBook().FindByIdentify(bookIdentify) + + if err != nil { + c.JsonResult(6011,"关联文档的项目不存在") + } + doc,err := models.NewDocument().FindByIdentityFirst(documentIdentify,book.BookId) + if err != nil { + c.JsonResult(6003,"查询关联项目文档时出错") + } + documentId = doc.DocumentId + + // 如果不是超级管理员,则校验权限 + if !c.Member.IsAdministrator() { + bookResult, err := models.NewBookResult().FindByIdentify(book.Identify, c.Member.MemberId) + + if err != nil || bookResult.RoleId == conf.BookObserver { + c.JsonResult(6002, "关联文档不存在或权限不足") + } + } + } + var blog *models.Blog var err error //如果文章ID存在,则从数据库中查询文章 @@ -157,11 +242,14 @@ func (c *BlogController) ManageSetting() { } blogId,err := strconv.Atoi(c.Ctx.Input.Param(":id")) + c.Data["DocumentIdentify"] = ""; + if err == nil { blog,err := models.NewBlog().FindByIdAndMemberId(blogId,c.Member.MemberId) if err != nil { c.ShowErrorPage(500,err.Error()) } + c.Data["Model"] = blog }else{ c.Data["Model"] = models.NewBlog() @@ -199,12 +287,28 @@ func (c *BlogController) ManageEdit() { if version > 0 && blog.Version != version && cover != "yes"{ c.JsonResult(6005,"文章已被修改") } + //如果是关联文章,需要同步关联的文档 if blog.BlogType == 1 { doc,err := models.NewDocument().Find(blog.DocumentId) if err != nil { beego.Error("查询关联项目文档时出错 ->", err) c.JsonResult(6003,"查询关联项目文档时出错") } + book, err := models.NewBook().Find(doc.BookId) + if err != nil { + c.JsonResult(6002, "项目不存在或权限不足") + } + + // 如果不是超级管理员,则校验权限 + if !c.Member.IsAdministrator() { + bookResult, err := models.NewBookResult().FindByIdentify(book.Identify, c.Member.MemberId) + + if err != nil || bookResult.RoleId == conf.BookObserver { + beego.Error("FindByIdentify => ", err) + c.JsonResult(6002, "关联文档不存在或权限不足") + } + } + doc.Markdown = blogContent doc.Release = blogHtml doc.Content = blogHtml @@ -246,6 +350,19 @@ func (c *BlogController) ManageEdit() { if err != nil { c.ShowErrorPage(404,"文章不存在或已删除") } + blog.LinkAttach() + + if len(blog.AttachList) > 0 { + returnJSON, err := json.Marshal(blog.AttachList) + if err != nil { + beego.Error("序列化文章附件时出错 ->",err) + }else{ + c.Data["AttachList"] = template.JS(string(returnJSON)) + } + }else{ + c.Data["AttachList"] = template.JS("[]") + } + c.Data["Model"] = blog } @@ -286,6 +403,15 @@ func (c *BlogController) Upload() { c.JsonResult(6001, "参数错误") } + blog,err := models.NewBlog().Find(blogId) + + if err != nil { + c.JsonResult(6010,"文章不存在") + } + if !c.Member.IsAdministrator() && blog.MemberId != c.Member.MemberId { + c.JsonResult(6011,"没有文章的访问权限") + } + name := "editormd-file-file" file, moreFile, err := c.GetFile(name) @@ -361,7 +487,7 @@ func (c *BlogController) Upload() { var httpPath string result := make(map[string]interface{}) - + //如果是图片,则当做内置图片处理,否则当做附件处理 if strings.EqualFold(ext, ".jpg") || strings.EqualFold(ext, ".jpeg") || strings.EqualFold(ext, ".png") || strings.EqualFold(ext, ".gif") { httpPath = "/" + strings.Replace(strings.TrimPrefix(filePath, conf.WorkingDirectory), "\\", "/", -1) if strings.HasPrefix(httpPath, "//") { @@ -375,7 +501,11 @@ func (c *BlogController) Upload() { attachment.FileExt = ext attachment.FilePath = strings.TrimPrefix(filePath, conf.WorkingDirectory) attachment.DocumentId = blogId - + //如果是关联文章,则将附件设置为关联文档的文档上 + if blog.BlogType == 1 { + attachment.BookId = blog.BookId + attachment.DocumentId = blog.DocumentId + } if fileInfo, err := os.Stat(filePath); err == nil { attachment.FileSize = float64(fileInfo.Size()) } @@ -413,11 +543,19 @@ func (c *BlogController) Upload() { func (c *BlogController) RemoveAttachment() { c.Prepare() attachId, _ := c.GetInt("attach_id") + blogId, _ := strconv.Atoi(c.Ctx.Input.Param(":id")) if attachId <= 0 { c.JsonResult(6001, "参数错误") } - + blog, err := models.NewBlog().Find(blogId) + if err != nil { + if err == orm.ErrNoRows { + c.ShowErrorPage(500, "文档不存在") + } else { + c.ShowErrorPage(500, "查询文章时异常") + } + } attach, err := models.NewAttachment().Find(attachId) if err != nil { @@ -434,6 +572,11 @@ func (c *BlogController) RemoveAttachment() { } } + if blog.BlogType == 1 && attach.BookId != blog.BookId && attach.DocumentId != blog.DocumentId { + c.ShowErrorPage(404,"附件不存在") + }else if attach.BookId !=0 || attach.DocumentId != blogId { + c.ShowErrorPage(404,"附件不存在") + } if err := attach.Delete();err != nil { beego.Error(err) @@ -461,8 +604,9 @@ func (c *BlogController) Download() { c.ShowErrorPage(500, "查询文章时异常") } } + blogReadSession := fmt.Sprintf("blog:read:%d",blogId) - if (c.Member != nil && !c.Member.IsAdministrator()) || ( blog.BlogStatus == "password" && password != blog.Password) { + if (c.Member != nil && !c.Member.IsAdministrator()) || ( blog.BlogStatus == "password" && password != blog.Password && c.CruSession.Get(blogReadSession) == nil) { c.ShowErrorPage(403, "没有下载权限") } @@ -477,8 +621,9 @@ func (c *BlogController) Download() { c.ShowErrorPage(500,"查询附件时出现异常") } } - - if attachment.BookId !=0 || attachment.DocumentId != blogId { + if blog.BlogType == 1 && attachment.BookId != blog.BookId && attachment.DocumentId != blog.DocumentId { + c.ShowErrorPage(404,"附件不存在") + }else if attachment.BookId !=0 || attachment.DocumentId != blogId { c.ShowErrorPage(404,"附件不存在") } diff --git a/models/Blog.go b/models/Blog.go index bda4d3a3..cee3e417 100644 --- a/models/Blog.go +++ b/models/Blog.go @@ -18,10 +18,18 @@ type Blog struct { OrderIndex int `orm:"column(order_index);type(int);default(0)" json:"order_index"` //所属用户 MemberId int `orm:"column(member_id);type(int);default(0):index" json:"member_id"` + //用户头像 + MemberAvatar string `orm:"-" json:"member_avatar"` //文章类型:0 普通文章/1 链接文章 BlogType int `orm:"column(blog_type);type(int);default(0)" json:"blog_type"` //链接到的项目中的文档ID DocumentId int `orm:"column(document_id);type(int);default(0)" json:"document_id"` + //文章的标识 + DocumentIdentify string `orm:"-" json:"document_identify"` + //关联文档的项目标识 + BookIdentify string `orm:"-" json:"book_identify"` + //关联文档的项目ID + BookId int `orm:"-" json:"book_id"` //文章摘要 BlogExcerpt string `orm:"column(blog_excerpt);size(1500)" json:"blog_excerpt"` //文章内容 @@ -42,6 +50,8 @@ type Blog struct { CreateName string `orm:"-" json:"create_name"` //版本号 Version int64 `orm:"type(bigint);column(version)" json:"version"` + //附件列表 + AttachList []*Attachment `orm:"-" json:"attach_list"` } // 多字段唯一键 @@ -81,7 +91,8 @@ func (b *Blog) Find(blogId int) (*Blog,error) { return nil,err } - return b,nil + + return b.Link() } //查找指定用户的指定文章 func (b *Blog) FindByIdAndMemberId(blogId,memberId int) (*Blog,error) { @@ -93,7 +104,7 @@ func (b *Blog) FindByIdAndMemberId(blogId,memberId int) (*Blog,error) { return nil,err } - return b,nil + return b.Link() } //根据文章标识查询文章 func (b *Blog) FindByIdentify(identify string) (*Blog,error) { @@ -106,18 +117,49 @@ func (b *Blog) FindByIdentify(identify string) (*Blog,error) { } return b,nil } + //获取指定文章的链接内容 func (b *Blog)Link() (*Blog,error) { o := orm.NewOrm() //如果是链接文章,则需要从链接的项目中查找文章内容 - if b.BlogType == 1 && b.DocumentId > 0{ + if b.BlogType == 1 && b.DocumentId > 0 { doc := NewDocument() - if err := o.QueryTable(doc.TableNameWithPrefix()).Filter("document_id",b.DocumentId).One(doc,"release","markdown");err != nil { + if err := o.QueryTable(doc.TableNameWithPrefix()).Filter("document_id",b.DocumentId).One(doc,"release","markdown","identify","book_id");err != nil { beego.Error("查询文章链接对象时出错 -> ",err) }else{ + b.DocumentIdentify = doc.Identify b.BlogRelease = doc.Release //目前仅支持markdown文档进行链接 b.BlogContent = doc.Markdown + book := NewBook() + if err := o.QueryTable(book.TableNameWithPrefix()).Filter("book_id",doc.BookId).One(book,"identify");err != nil { + beego.Error("查询关联文档的项目时出错 ->",err) + }else{ + b.BookIdentify = book.Identify + b.BookId = doc.BookId + } + } + } + + if b.ModifyAt > 0{ + member := NewMember() + if err := o.QueryTable(member.TableNameWithPrefix()).Filter("member_id",b.ModifyAt).One(member,"real_name","account"); err == nil { + if member.RealName != ""{ + b.ModifyRealName = member.RealName + }else{ + b.ModifyRealName = member.Account + } + } + } + if b.MemberId > 0 { + member := NewMember() + if err := o.QueryTable(member.TableNameWithPrefix()).Filter("member_id",b.MemberId).One(member,"real_name","account","avatar"); err == nil { + if member.RealName != ""{ + b.CreateName = member.RealName + }else{ + b.CreateName = member.Account + } + b.MemberAvatar = member.Avatar } } @@ -130,11 +172,12 @@ func (b *Blog) IsExist(identify string) bool { return o.QueryTable(b.TableNameWithPrefix()).Filter("blog_identify",identify).Exist() } + //保存文章 func (b *Blog) Save(cols ...string) error { o := orm.NewOrm() - if b.OrderIndex == 0 { + if b.OrderIndex <= 0 { blog := NewBlog() if err :=o.QueryTable(b.TableNameWithPrefix()).OrderBy("-blog_id").One(blog,"blog_id");err == nil{ b.OrderIndex = b.BlogId + 1; @@ -149,11 +192,13 @@ func (b *Blog) Save(cols ...string) error { b.Modified = time.Now() _,err = o.Update(b,cols...) }else{ + b.Created = time.Now() _,err = o.Insert(b) } return err } + //分页查询文章列表 func (b *Blog) FindToPager(pageIndex, pageSize int,memberId int,status string) (blogList []*Blog, totalCount int, err error) { @@ -171,7 +216,7 @@ func (b *Blog) FindToPager(pageIndex, pageSize int,memberId int,status string) ( } - _,err = query.OrderBy("-order_index").Offset(offset).Limit(pageSize).All(&blogList) + _,err = query.OrderBy("-order_index","-blog_id").Offset(offset).Limit(pageSize).All(&blogList) if err != nil { if err == orm.ErrNoRows { @@ -205,4 +250,63 @@ func (b *Blog) Delete(blogId int) error { beego.Error("删除文章失败 ->",err) } return err +} + +//查询下一篇文章 +func (b *Blog) QueryNext(blogId int) (*Blog,error) { + o := orm.NewOrm() + blog := NewBlog() + + if err := o.QueryTable(b.TableNameWithPrefix()).Filter("blog_id",blogId).One(blog,"order_index"); err != nil { + beego.Error("查询文章时出错 ->",err) + return b,err + } + + err := o.QueryTable(b.TableNameWithPrefix()).Filter("order_index__gte",blog.OrderIndex).Filter("blog_id__gt",blogId).OrderBy("-order_index","-blog_id").One(blog) + + if err != nil { + beego.Error("查询文章时出错 ->",err) + } + return blog,err +} + +//查询下一篇文章 +func (b *Blog) QueryPrevious(blogId int) (*Blog,error) { + o := orm.NewOrm() + blog := NewBlog() + + if err := o.QueryTable(b.TableNameWithPrefix()).Filter("blog_id",blogId).One(blog,"order_index"); err != nil { + beego.Error("查询文章时出错 ->",err) + return b,err + } + + err := o.QueryTable(b.TableNameWithPrefix()).Filter("order_index__lte",blog.OrderIndex).Filter("blog_id__lt",blogId).OrderBy("-order_index","-blog_id").One(blog) + + if err != nil { + beego.Error("查询文章时出错 ->",err) + } + return blog,err +} + +//关联文章附件 +func (b *Blog) LinkAttach() (err error) { + + o := orm.NewOrm() + + var attachList []*Attachment + //当不是关联文章时,用文章ID去查询附件 + if b.BlogType != 1 || b.DocumentId <= 0 { + _, err = o.QueryTable(NewAttachment().TableNameWithPrefix()).Filter("document_id", b.BlogId).Filter("book_id",0).All(&attachList) + if err != nil { + beego.Error("查询文章附件时出错 ->", err) + } + }else { + _, err = o.QueryTable(NewAttachment().TableNameWithPrefix()).Filter("document_id", b.DocumentId).Filter("book_id", b.BookId).All(&attachList) + + if err != nil { + beego.Error("查询文章附件时出错 ->", err) + } + } + b.AttachList = attachList + return } \ No newline at end of file diff --git a/models/DocumentModel.go b/models/DocumentModel.go index 6768c7ff..ecaa4b12 100644 --- a/models/DocumentModel.go +++ b/models/DocumentModel.go @@ -220,6 +220,7 @@ func (m *Document) FindListByBookId(bookId int) (docs []*Document, err error) { return } + //判断文章是否存在 func (m *Document) IsExist(documentId int) bool { o := orm.NewOrm() diff --git a/routers/router.go b/routers/router.go index 6bbb6f84..e88bec8a 100644 --- a/routers/router.go +++ b/routers/router.go @@ -69,6 +69,8 @@ func init() { beego.Router("/blogs/edit/?:id",&controllers.BlogController{}, "*:ManageEdit") beego.Router("/blogs/delete",&controllers.BlogController{}, "post:ManageDelete") beego.Router("/blogs/upload",&controllers.BlogController{}, "post:Upload") + beego.Router("/blogs/attach/:id",&controllers.BlogController{}, "post:RemoveAttachment") + //读文章的路由 beego.Router("/blog", &controllers.BlogController{}, "*:List") diff --git a/static/js/blog.js b/static/js/blog.js index 9205dd63..89252282 100644 --- a/static/js/blog.js +++ b/static/js/blog.js @@ -146,4 +146,19 @@ $(function () { } window.isLoad = false; } + /** + * 打开文档模板 + */ + $("#documentTemplateModal").on("click", ".section>a[data-type]", function () { + var $this = $(this).attr("data-type"); + var body = $("#template-" + $this).html(); + if (body) { + window.isLoad = true; + window.editor.clear(); + window.editor.insertValue(body); + window.editor.setCursor({ line: 0, ch: 0 }); + resetEditorChanged(true); + } + $("#documentTemplateModal").modal('hide'); + }); }); \ No newline at end of file diff --git a/views/blog/index.tpl b/views/blog/index.tpl index 8b137891..a29a6d62 100644 --- a/views/blog/index.tpl +++ b/views/blog/index.tpl @@ -1 +1,144 @@ + + +
+ + + + + + + + +