DocumentController.go 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342
  1. package controllers
  2. import (
  3. "container/list"
  4. "encoding/json"
  5. "html/template"
  6. "net/http"
  7. "os"
  8. "path/filepath"
  9. "regexp"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "net/url"
  14. "image/png"
  15. "bytes"
  16. "github.com/PuerkitoBio/goquery"
  17. "github.com/astaxie/beego"
  18. "github.com/astaxie/beego/orm"
  19. "github.com/boombuler/barcode"
  20. "github.com/boombuler/barcode/qr"
  21. "github.com/lifei6671/mindoc/conf"
  22. "github.com/lifei6671/mindoc/models"
  23. "github.com/lifei6671/mindoc/utils"
  24. "github.com/lifei6671/mindoc/utils/pagination"
  25. "gopkg.in/russross/blackfriday.v2"
  26. "github.com/lifei6671/mindoc/utils/cryptil"
  27. "fmt"
  28. )
  29. // DocumentController struct
  30. type DocumentController struct {
  31. BaseController
  32. }
  33. // 文档首页
  34. func (c *DocumentController) Index() {
  35. c.Prepare()
  36. identify := c.Ctx.Input.Param(":key")
  37. token := c.GetString("token")
  38. if identify == "" {
  39. c.Abort("404")
  40. }
  41. // 如果没有开启匿名访问则跳转到登录
  42. if !c.EnableAnonymous && !isUserLoggedIn(c) {
  43. promptUserToLogIn(c)
  44. return
  45. }
  46. bookResult := isReadable(identify, token, c)
  47. c.TplName = "document/" + bookResult.Theme + "_read.tpl"
  48. selected := 0
  49. if bookResult.IsUseFirstDocument {
  50. doc,err := bookResult.FindFirstDocumentByBookId(bookResult.BookId)
  51. if err == nil {
  52. if strings.TrimSpace(doc.Release) != "" {
  53. doc.Release += "<div class=\"wiki-bottom\">文档更新时间: " + doc.ModifyTime.Local().Format("2006-01-02 15:04") + "</div>";
  54. }
  55. selected = doc.DocumentId
  56. c.Data["Title"] = doc.DocumentName
  57. c.Data["Content"] = template.HTML(doc.Release)
  58. }
  59. }else {
  60. c.Data["Title"] = "概要"
  61. c.Data["Content"] = template.HTML(blackfriday.Run([]byte(bookResult.Description)))
  62. }
  63. tree, err := models.NewDocument().CreateDocumentTreeForHtml(bookResult.BookId, selected)
  64. if err != nil {
  65. beego.Error(err)
  66. c.Abort("500")
  67. }
  68. c.Data["Model"] = bookResult
  69. c.Data["Result"] = template.HTML(tree)
  70. }
  71. // 阅读文档
  72. func (c *DocumentController) Read() {
  73. c.Prepare()
  74. identify := c.Ctx.Input.Param(":key")
  75. token := c.GetString("token")
  76. id := c.GetString(":id")
  77. c.Data["DocumentId"] = id
  78. if identify == "" || id == "" {
  79. c.Abort("404")
  80. }
  81. // 如果没有开启匿名访问则跳转到登录
  82. if !c.EnableAnonymous && !isUserLoggedIn(c) {
  83. promptUserToLogIn(c)
  84. return
  85. }
  86. bookResult := isReadable(identify, token, c)
  87. c.TplName = fmt.Sprintf("document/%s_read.tpl",bookResult.Theme)
  88. doc := models.NewDocument()
  89. if docId, err := strconv.Atoi(id); err == nil {
  90. doc, err = doc.FromCacheById(docId)
  91. if err != nil {
  92. beego.Error(err)
  93. c.Abort("500")
  94. }
  95. } else {
  96. doc, err = doc.FromCacheByIdentify(id,bookResult.BookId)
  97. if err != nil {
  98. beego.Error(err)
  99. c.Abort("500")
  100. }
  101. }
  102. if doc.BookId != bookResult.BookId {
  103. c.Abort("403")
  104. }
  105. attach, err := models.NewAttachment().FindListByDocumentId(doc.DocumentId)
  106. if err == nil {
  107. doc.AttachList = attach
  108. }
  109. cdnimg := beego.AppConfig.String("cdnimg")
  110. if doc.Release != "" && cdnimg != "" {
  111. query, err := goquery.NewDocumentFromReader(bytes.NewBufferString(doc.Release))
  112. if err != nil {
  113. beego.Error(err)
  114. } else {
  115. query.Find("img").Each(func(i int, contentSelection *goquery.Selection) {
  116. if src, ok := contentSelection.Attr("src"); ok && strings.HasPrefix(src, "/uploads/") {
  117. contentSelection.SetAttr("src", utils.JoinURI(cdnimg, src))
  118. }
  119. })
  120. html, err := query.Html()
  121. if err != nil {
  122. beego.Error(err)
  123. } else {
  124. doc.Release = html
  125. }
  126. }
  127. }
  128. // assemble doc info, added by dandycheung, 2017-12-20
  129. docInfo := ""
  130. docCreator, err := models.NewMember().Find(doc.MemberId)
  131. if err == nil {
  132. docInfo += docCreator.Account
  133. }
  134. docInfo += " 创建于 "
  135. docInfo += doc.CreateTime.Local().Format("2006-01-02 15:04")
  136. if doc.ModifyTime != doc.CreateTime {
  137. docInfo += ";更新于 "
  138. docInfo += doc.ModifyTime.Local().Format("2006-01-02 15:04")
  139. if strings.TrimSpace(doc.Release) != "" {
  140. doc.Release += "<div class=\"wiki-bottom\">文档更新时间: " + doc.ModifyTime.Local().Format("2006-01-02 15:04") + "</div>";
  141. }
  142. }
  143. if c.IsAjax() {
  144. var data struct {
  145. DocTitle string `json:"doc_title"`
  146. Body string `json:"body"`
  147. Title string `json:"title"`
  148. DocInfo string `json:"doc_info"`
  149. }
  150. data.DocTitle = doc.DocumentName
  151. data.Body = doc.Release
  152. data.Title = doc.DocumentName + " - Powered by MinDoc"
  153. data.DocInfo = docInfo
  154. c.JsonResult(0, "ok", data)
  155. }
  156. tree, err := models.NewDocument().CreateDocumentTreeForHtml(bookResult.BookId, doc.DocumentId)
  157. if err != nil {
  158. beego.Error(err)
  159. c.Abort("500")
  160. }
  161. c.Data["Model"] = bookResult
  162. c.Data["Result"] = template.HTML(tree)
  163. c.Data["Title"] = doc.DocumentName
  164. c.Data["Info"] = docInfo
  165. c.Data["Content"] = template.HTML(doc.Release)
  166. }
  167. // 编辑文档
  168. func (c *DocumentController) Edit() {
  169. c.Prepare()
  170. identify := c.Ctx.Input.Param(":key")
  171. if identify == "" {
  172. c.Abort("404")
  173. }
  174. bookResult := models.NewBookResult()
  175. var err error
  176. // 如果是超级管理者,则不判断权限
  177. if c.Member.IsAdministrator() {
  178. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  179. if err != nil {
  180. c.JsonResult(6002, "项目不存在或权限不足")
  181. }
  182. bookResult = models.NewBookResult().ToBookResult(*book)
  183. } else {
  184. bookResult, err = models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  185. if err != nil {
  186. beego.Error("DocumentController.Edit => ", err)
  187. c.Abort("403")
  188. }
  189. if bookResult.RoleId == conf.BookObserver {
  190. c.JsonResult(6002, "项目不存在或权限不足")
  191. }
  192. }
  193. // 根据不同编辑器类型加载编辑器
  194. if bookResult.Editor == "markdown" {
  195. c.TplName = "document/markdown_edit_template.tpl"
  196. } else if bookResult.Editor == "html" {
  197. c.TplName = "document/new_html_edit_template.tpl"
  198. } else {
  199. c.TplName = "document/" + bookResult.Editor + "_edit_template.tpl"
  200. }
  201. c.Data["Model"] = bookResult
  202. r, _ := json.Marshal(bookResult)
  203. c.Data["ModelResult"] = template.JS(string(r))
  204. c.Data["Result"] = template.JS("[]")
  205. trees, err := models.NewDocument().FindDocumentTree(bookResult.BookId)
  206. if err != nil {
  207. beego.Error("FindDocumentTree => ", err)
  208. } else {
  209. if len(trees) > 0 {
  210. if jtree, err := json.Marshal(trees); err == nil {
  211. c.Data["Result"] = template.JS(string(jtree))
  212. }
  213. } else {
  214. c.Data["Result"] = template.JS("[]")
  215. }
  216. }
  217. c.Data["BaiDuMapKey"] = beego.AppConfig.DefaultString("baidumapkey", "")
  218. }
  219. // 创建一个文档
  220. func (c *DocumentController) Create() {
  221. identify := c.GetString("identify")
  222. docIdentify := c.GetString("doc_identify")
  223. docName := c.GetString("doc_name")
  224. parentId, _ := c.GetInt("parent_id", 0)
  225. docId, _ := c.GetInt("doc_id", 0)
  226. if identify == "" {
  227. c.JsonResult(6001, "参数错误")
  228. }
  229. if docName == "" {
  230. c.JsonResult(6004, "文档名称不能为空")
  231. }
  232. bookId := 0
  233. // 如果是超级管理员则不判断权限
  234. if c.Member.IsAdministrator() {
  235. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  236. if err != nil {
  237. beego.Error(err)
  238. c.JsonResult(6002, "项目不存在或权限不足")
  239. }
  240. bookId = book.BookId
  241. } else {
  242. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  243. if err != nil || bookResult.RoleId == conf.BookObserver {
  244. beego.Error("FindByIdentify => ", err)
  245. c.JsonResult(6002, "项目不存在或权限不足")
  246. }
  247. bookId = bookResult.BookId
  248. }
  249. if docIdentify != "" {
  250. if ok, err := regexp.MatchString(`[a-z]+[a-zA-Z0-9_.\-]*$`, docIdentify); !ok || err != nil {
  251. c.JsonResult(6003, "文档标识只能包含小写字母、数字,以及“-”、“.”和“_”符号")
  252. }
  253. d, _ := models.NewDocument().FindByIdentityFirst(docIdentify,bookId)
  254. if d.DocumentId > 0 && d.DocumentId != docId {
  255. c.JsonResult(6006, "文档标识已被使用")
  256. }
  257. }
  258. if parentId > 0 {
  259. doc, err := models.NewDocument().Find(parentId)
  260. if err != nil || doc.BookId != bookId {
  261. c.JsonResult(6003, "父分类不存在")
  262. }
  263. }
  264. document, _ := models.NewDocument().Find(docId)
  265. document.MemberId = c.Member.MemberId
  266. document.BookId = bookId
  267. document.Identify = docIdentify
  268. document.Version = time.Now().Unix()
  269. document.DocumentName = docName
  270. document.ParentId = parentId
  271. if err := document.InsertOrUpdate(); err != nil {
  272. beego.Error("InsertOrUpdate => ", err)
  273. c.JsonResult(6005, "保存失败")
  274. } else {
  275. c.JsonResult(0, "ok", document)
  276. }
  277. }
  278. // 上传附件或图片
  279. func (c *DocumentController) Upload() {
  280. identify := c.GetString("identify")
  281. doc_id, _ := c.GetInt("doc_id")
  282. is_attach := true
  283. if identify == "" {
  284. c.JsonResult(6001, "参数错误")
  285. }
  286. name := "editormd-file-file"
  287. file, moreFile, err := c.GetFile(name)
  288. if err == http.ErrMissingFile {
  289. name = "editormd-image-file"
  290. file, moreFile, err = c.GetFile(name)
  291. if err == http.ErrMissingFile {
  292. c.JsonResult(6003, "没有发现需要上传的文件")
  293. }
  294. }
  295. if err != nil {
  296. c.JsonResult(6002, err.Error())
  297. }
  298. defer file.Close()
  299. type Size interface {
  300. Size() int64
  301. }
  302. if conf.GetUploadFileSize() > 0 && moreFile.Size > conf.GetUploadFileSize() {
  303. c.JsonResult(6009, "查过文件允许的上传最大值")
  304. }
  305. ext := filepath.Ext(moreFile.Filename)
  306. if ext == "" {
  307. c.JsonResult(6003, "无法解析文件的格式")
  308. }
  309. //如果文件类型设置为 * 标识不限制文件类型
  310. if beego.AppConfig.DefaultString("upload_file_ext", "") != "*" {
  311. if !conf.IsAllowUploadFileExt(ext) {
  312. c.JsonResult(6004, "不允许的文件类型")
  313. }
  314. }
  315. bookId := 0
  316. // 如果是超级管理员,则不判断权限
  317. if c.Member.IsAdministrator() {
  318. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  319. if err != nil {
  320. c.JsonResult(6006, "文档不存在或权限不足")
  321. }
  322. bookId = book.BookId
  323. } else {
  324. book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  325. if err != nil {
  326. beego.Error("DocumentController.Edit => ", err)
  327. if err == orm.ErrNoRows {
  328. c.JsonResult(6006, "权限不足")
  329. }
  330. c.JsonResult(6001, err.Error())
  331. }
  332. // 如果没有编辑权限
  333. if book.RoleId != conf.BookEditor && book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder {
  334. c.JsonResult(6006, "权限不足")
  335. }
  336. bookId = book.BookId
  337. }
  338. if doc_id > 0 {
  339. doc, err := models.NewDocument().Find(doc_id)
  340. if err != nil {
  341. c.JsonResult(6007, "文档不存在")
  342. }
  343. if doc.BookId != bookId {
  344. c.JsonResult(6008, "文档不属于指定的项目")
  345. }
  346. }
  347. fileName := "attach_" + strconv.FormatInt(time.Now().UnixNano(), 16)
  348. filePath := filepath.Join(conf.WorkingDirectory, "uploads", time.Now().Format("200601"),identify, fileName+ext)
  349. path := filepath.Dir(filePath)
  350. os.MkdirAll(path, os.ModePerm)
  351. err = c.SaveToFile(name, filePath)
  352. if err != nil {
  353. beego.Error("SaveToFile => ", err)
  354. c.JsonResult(6005, "保存文件失败")
  355. }
  356. attachment := models.NewAttachment()
  357. attachment.BookId = bookId
  358. attachment.FileName = moreFile.Filename
  359. attachment.CreateAt = c.Member.MemberId
  360. attachment.FileExt = ext
  361. attachment.FilePath = strings.TrimPrefix(filePath, conf.WorkingDirectory)
  362. attachment.DocumentId = doc_id
  363. if fileInfo, err := os.Stat(filePath); err == nil {
  364. attachment.FileSize = float64(fileInfo.Size())
  365. }
  366. if doc_id > 0 {
  367. attachment.DocumentId = doc_id
  368. }
  369. if strings.EqualFold(ext, ".jpg") || strings.EqualFold(ext, ".jpeg") || strings.EqualFold(ext, ".png") || strings.EqualFold(ext, ".gif") {
  370. attachment.HttpPath = "/" + strings.Replace(strings.TrimPrefix(filePath, conf.WorkingDirectory), "\\", "/", -1)
  371. if strings.HasPrefix(attachment.HttpPath, "//") {
  372. attachment.HttpPath = conf.URLForWithCdnImage(string(attachment.HttpPath[1:]))
  373. }
  374. is_attach = false
  375. }
  376. err = attachment.Insert()
  377. if err != nil {
  378. os.Remove(filePath)
  379. beego.Error("Attachment Insert => ", err)
  380. c.JsonResult(6006, "文件保存失败")
  381. }
  382. if attachment.HttpPath == "" {
  383. attachment.HttpPath = conf.URLFor("DocumentController.DownloadAttachment", ":key", identify, ":attach_id", attachment.AttachmentId)
  384. if err := attachment.Update(); err != nil {
  385. beego.Error("SaveToFile => ", err)
  386. c.JsonResult(6005, "保存文件失败")
  387. }
  388. }
  389. result := map[string]interface{}{
  390. "errcode": 0,
  391. "success": 1,
  392. "message": "ok",
  393. "url": attachment.HttpPath,
  394. "alt": attachment.FileName,
  395. "is_attach": is_attach,
  396. "attach": attachment,
  397. }
  398. c.Ctx.Output.JSON(result, true, false)
  399. c.StopRun()
  400. }
  401. // 下载附件
  402. func (c *DocumentController) DownloadAttachment() {
  403. c.Prepare()
  404. identify := c.Ctx.Input.Param(":key")
  405. attachId, _ := strconv.Atoi(c.Ctx.Input.Param(":attach_id"))
  406. token := c.GetString("token")
  407. memberId := 0
  408. if c.Member != nil {
  409. memberId = c.Member.MemberId
  410. }
  411. bookId := 0
  412. // 判断用户是否参与了项目
  413. bookResult, err := models.NewBookResult().FindByIdentify(identify, memberId)
  414. if err != nil {
  415. // 判断项目公开状态
  416. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  417. if err != nil {
  418. c.Abort("404")
  419. }
  420. // 如果不是超级管理员则判断权限
  421. if c.Member == nil || c.Member.Role != conf.MemberSuperRole {
  422. // 如果项目是私有的,并且 token 不正确
  423. if (book.PrivatelyOwned == 1 && token == "") || (book.PrivatelyOwned == 1 && book.PrivateToken != token) {
  424. c.Abort("403")
  425. }
  426. }
  427. bookId = book.BookId
  428. } else {
  429. bookId = bookResult.BookId
  430. }
  431. // 查找附件
  432. attachment, err := models.NewAttachment().Find(attachId)
  433. if err != nil {
  434. beego.Error("DownloadAttachment => ", err)
  435. if err == orm.ErrNoRows {
  436. c.Abort("404")
  437. } else {
  438. c.Abort("500")
  439. }
  440. }
  441. if attachment.BookId != bookId {
  442. c.Abort("404")
  443. }
  444. c.Ctx.Output.Download(filepath.Join(conf.WorkingDirectory, attachment.FilePath), attachment.FileName)
  445. c.StopRun()
  446. }
  447. // 删除附件
  448. func (c *DocumentController) RemoveAttachment() {
  449. c.Prepare()
  450. attach_id, _ := c.GetInt("attach_id")
  451. if attach_id <= 0 {
  452. c.JsonResult(6001, "参数错误")
  453. }
  454. attach, err := models.NewAttachment().Find(attach_id)
  455. if err != nil {
  456. beego.Error(err)
  457. c.JsonResult(6002, "附件不存在")
  458. }
  459. document, err := models.NewDocument().Find(attach.DocumentId)
  460. if err != nil {
  461. beego.Error(err)
  462. c.JsonResult(6003, "文档不存在")
  463. }
  464. if c.Member.Role != conf.MemberSuperRole {
  465. rel, err := models.NewRelationship().FindByBookIdAndMemberId(document.BookId, c.Member.MemberId)
  466. if err != nil {
  467. beego.Error(err)
  468. c.JsonResult(6004, "权限不足")
  469. }
  470. if rel.RoleId == conf.BookObserver {
  471. c.JsonResult(6004, "权限不足")
  472. }
  473. }
  474. err = attach.Delete()
  475. if err != nil {
  476. beego.Error(err)
  477. c.JsonResult(6005, "删除失败")
  478. }
  479. os.Remove(filepath.Join(conf.WorkingDirectory, attach.FilePath))
  480. c.JsonResult(0, "ok", attach)
  481. }
  482. // 删除文档
  483. func (c *DocumentController) Delete() {
  484. c.Prepare()
  485. identify := c.GetString("identify")
  486. doc_id, err := c.GetInt("doc_id", 0)
  487. book_id := 0
  488. // 如果是超级管理员则忽略权限判断
  489. if c.Member.IsAdministrator() {
  490. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  491. if err != nil {
  492. beego.Error("FindByIdentify => ", err)
  493. c.JsonResult(6002, "项目不存在或权限不足")
  494. }
  495. book_id = book.BookId
  496. } else {
  497. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  498. if err != nil || bookResult.RoleId == conf.BookObserver {
  499. beego.Error("FindByIdentify => ", err)
  500. c.JsonResult(6002, "项目不存在或权限不足")
  501. }
  502. book_id = bookResult.BookId
  503. }
  504. if doc_id <= 0 {
  505. c.JsonResult(6001, "参数错误")
  506. }
  507. doc, err := models.NewDocument().Find(doc_id)
  508. if err != nil {
  509. beego.Error("Delete => ", err)
  510. c.JsonResult(6003, "删除失败")
  511. }
  512. // 如果文档所属项目错误
  513. if doc.BookId != book_id {
  514. c.JsonResult(6004, "参数错误")
  515. }
  516. // 递归删除项目下的文档以及子文档
  517. err = doc.RecursiveDocument(doc.DocumentId)
  518. if err != nil {
  519. c.JsonResult(6005, "删除失败")
  520. }
  521. // 重置文档数量统计
  522. models.NewBook().ResetDocumentNumber(doc.BookId)
  523. c.JsonResult(0, "ok")
  524. }
  525. // 获取文档内容
  526. func (c *DocumentController) Content() {
  527. c.Prepare()
  528. identify := c.Ctx.Input.Param(":key")
  529. docId, err := c.GetInt("doc_id")
  530. if err != nil {
  531. docId, _ = strconv.Atoi(c.Ctx.Input.Param(":id"))
  532. }
  533. bookId := 0
  534. autoRelease := false
  535. // 如果是超级管理员,则忽略权限
  536. if c.Member.IsAdministrator() {
  537. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  538. if err != nil {
  539. c.JsonResult(6002, "项目不存在或权限不足")
  540. }
  541. bookId = book.BookId
  542. autoRelease = book.AutoRelease == 1
  543. } else {
  544. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  545. if err != nil || bookResult.RoleId == conf.BookObserver {
  546. beego.Error("FindByIdentify => ", err)
  547. c.JsonResult(6002, "项目不存在或权限不足")
  548. }
  549. bookId = bookResult.BookId
  550. autoRelease = bookResult.AutoRelease
  551. }
  552. if docId <= 0 {
  553. c.JsonResult(6001, "参数错误")
  554. }
  555. if c.Ctx.Input.IsPost() {
  556. markdown := strings.TrimSpace(c.GetString("markdown", ""))
  557. content := c.GetString("html")
  558. version, _ := c.GetInt64("version", 0)
  559. isCover := c.GetString("cover")
  560. doc, err := models.NewDocument().Find(docId)
  561. if err != nil {
  562. c.JsonResult(6003, "读取文档错误")
  563. }
  564. if doc.BookId != bookId {
  565. c.JsonResult(6004, "保存的文档不属于指定项目")
  566. }
  567. if doc.Version != version && !strings.EqualFold(isCover, "yes") {
  568. beego.Info("%d|", version, doc.Version)
  569. c.JsonResult(6005, "文档已被修改确定要覆盖吗?")
  570. }
  571. history := models.NewDocumentHistory()
  572. history.DocumentId = docId
  573. history.Content = doc.Content
  574. history.Markdown = doc.Markdown
  575. history.DocumentName = doc.DocumentName
  576. history.ModifyAt = c.Member.MemberId
  577. history.MemberId = doc.MemberId
  578. history.ParentId = doc.ParentId
  579. history.Version = time.Now().Unix()
  580. history.Action = "modify"
  581. history.ActionName = "修改文档"
  582. if markdown == "" && content != "" {
  583. doc.Markdown = content
  584. } else {
  585. doc.Markdown = markdown
  586. }
  587. doc.Version = time.Now().Unix()
  588. doc.Content = content
  589. if err := doc.InsertOrUpdate(); err != nil {
  590. beego.Error("InsertOrUpdate => ", err)
  591. c.JsonResult(6006, "保存失败")
  592. }
  593. // 如果启用了文档历史,则添加历史文档
  594. ///如果两次保存的MD5值不同则保存为历史,否则忽略
  595. go func(history *models.DocumentHistory) {
  596. if c.EnableDocumentHistory && cryptil.Md5Crypt(history.Markdown) != cryptil.Md5Crypt(doc.Markdown) {
  597. _, err = history.InsertOrUpdate()
  598. if err != nil {
  599. beego.Error("DocumentHistory InsertOrUpdate => ", err)
  600. }
  601. }
  602. }(history)
  603. //如果启用了自动发布
  604. if autoRelease {
  605. go func(identify string) {
  606. models.NewDocument().ReleaseContent(bookId)
  607. }(identify)
  608. }
  609. c.JsonResult(0, "ok", doc)
  610. }
  611. doc, err := models.NewDocument().Find(docId)
  612. if err != nil {
  613. c.JsonResult(6003, "文档不存在")
  614. }
  615. attach, err := models.NewAttachment().FindListByDocumentId(doc.DocumentId)
  616. if err == nil {
  617. doc.AttachList = attach
  618. }
  619. c.JsonResult(0, "ok", doc)
  620. }
  621. //
  622. //func (c *DocumentController) GetDocumentById(id string) (doc *models.Document, err error) {
  623. // doc = models.NewDocument()
  624. // if doc_id, err := strconv.Atoi(id); err == nil {
  625. // doc, err = doc.Find(doc_id)
  626. // if err != nil {
  627. // return nil, err
  628. // }
  629. // } else {
  630. // doc, err = doc.FindByFieldFirst("identify", id)
  631. // if err != nil {
  632. // return nil, err
  633. // }
  634. // }
  635. //
  636. // return doc, nil
  637. //}
  638. // 导出
  639. func (c *DocumentController) Export() {
  640. c.Prepare()
  641. identify := c.Ctx.Input.Param(":key")
  642. if identify == "" {
  643. c.Abort("404")
  644. }
  645. output := c.GetString("output")
  646. token := c.GetString("token")
  647. // 如果没有开启匿名访问则跳转到登录
  648. if !c.EnableAnonymous && !isUserLoggedIn(c) {
  649. promptUserToLogIn(c)
  650. return
  651. }
  652. bookResult := models.NewBookResult()
  653. if c.Member != nil && c.Member.IsAdministrator() {
  654. book, err := models.NewBook().FindByIdentify(identify)
  655. if err != nil {
  656. beego.Error(err)
  657. c.Abort("500")
  658. }
  659. bookResult = models.NewBookResult().ToBookResult(*book)
  660. } else {
  661. bookResult = isReadable(identify, token, c)
  662. }
  663. if !bookResult.IsDownload {
  664. c.ShowErrorPage(200,"当前项目没有开启导出功能")
  665. }
  666. if !strings.HasPrefix(bookResult.Cover, "http:://") && !strings.HasPrefix(bookResult.Cover, "https:://") {
  667. bookResult.Cover = conf.URLForWithCdnImage(bookResult.Cover)
  668. }
  669. if output == "markdown" {
  670. if bookResult.Editor != "markdown"{
  671. c.ShowErrorPage(500,"当前项目不支持Markdown编辑器")
  672. }
  673. p,err := bookResult.ExportMarkdown(c.CruSession.SessionID())
  674. if err != nil {
  675. c.ShowErrorPage(500,"导出文档失败")
  676. }
  677. c.Ctx.Output.Download(p, bookResult.BookName+".zip")
  678. c.StopRun()
  679. return
  680. }
  681. eBookResult, err := bookResult.Converter(c.CruSession.SessionID())
  682. if err != nil {
  683. beego.Error("转换文档失败:" + bookResult.BookName + " -> " + err.Error())
  684. c.Abort("500")
  685. }
  686. if output == "pdf" {
  687. c.Ctx.Output.Download(eBookResult.PDFPath, bookResult.BookName+".pdf")
  688. c.Abort("200")
  689. } else if output == "epub" {
  690. c.Ctx.Output.Download(eBookResult.EpubPath, bookResult.BookName+".epub")
  691. c.Abort("200")
  692. } else if output == "mobi" {
  693. c.Ctx.Output.Download(eBookResult.MobiPath, bookResult.BookName+".mobi")
  694. c.Abort("200")
  695. } else if output == "docx" {
  696. c.Ctx.Output.Download(eBookResult.WordPath, bookResult.BookName+".docx")
  697. c.Abort("200")
  698. }else{
  699. c.ShowErrorPage(200,"不支持的文件格式")
  700. }
  701. c.Abort("404")
  702. }
  703. // 生成项目访问的二维码
  704. func (c *DocumentController) QrCode() {
  705. c.Prepare()
  706. identify := c.GetString(":key")
  707. book, err := models.NewBook().FindByIdentify(identify)
  708. if err != nil || book.BookId <= 0 {
  709. c.Abort("404")
  710. }
  711. uri := conf.URLFor("DocumentController.Index", ":key", identify)
  712. code, err := qr.Encode(uri, qr.L, qr.Unicode)
  713. if err != nil {
  714. beego.Error(err)
  715. c.Abort("500")
  716. }
  717. code, err = barcode.Scale(code, 150, 150)
  718. if err != nil {
  719. beego.Error(err)
  720. c.Abort("500")
  721. }
  722. c.Ctx.ResponseWriter.Header().Set("Content-Type", "image/png")
  723. // imgpath := filepath.Join("cache","qrcode",identify + ".png")
  724. err = png.Encode(c.Ctx.ResponseWriter, code)
  725. if err != nil {
  726. beego.Error(err)
  727. c.Abort("500")
  728. }
  729. }
  730. // 项目内搜索
  731. func (c *DocumentController) Search() {
  732. c.Prepare()
  733. identify := c.Ctx.Input.Param(":key")
  734. token := c.GetString("token")
  735. keyword := strings.TrimSpace(c.GetString("keyword"))
  736. if identify == "" {
  737. c.JsonResult(6001, "参数错误")
  738. }
  739. if !c.EnableAnonymous && !isUserLoggedIn(c) {
  740. promptUserToLogIn(c)
  741. return
  742. }
  743. bookResult := isReadable(identify, token, c)
  744. docs, err := models.NewDocumentSearchResult().SearchDocument(keyword, bookResult.BookId)
  745. if err != nil {
  746. beego.Error(err)
  747. c.JsonResult(6002, "搜索结果错误")
  748. }
  749. if len(docs) < 0 {
  750. c.JsonResult(404, "没有数据库")
  751. }
  752. for _, doc := range docs {
  753. doc.BookId = bookResult.BookId
  754. doc.BookName = bookResult.BookName
  755. doc.Description = bookResult.Description
  756. doc.BookIdentify = bookResult.Identify
  757. }
  758. c.JsonResult(0, "ok", docs)
  759. }
  760. // 文档历史列表
  761. func (c *DocumentController) History() {
  762. c.Prepare()
  763. c.TplName = "document/history.tpl"
  764. identify := c.GetString("identify")
  765. docId, err := c.GetInt("doc_id", 0)
  766. pageIndex, _ := c.GetInt("page", 1)
  767. book_id := 0
  768. // 如果是超级管理员则忽略权限判断
  769. if c.Member.IsAdministrator() {
  770. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  771. if err != nil {
  772. beego.Error("FindByIdentify => ", err)
  773. c.Data["ErrorMessage"] = "项目不存在或权限不足"
  774. return
  775. }
  776. book_id = book.BookId
  777. c.Data["Model"] = book
  778. } else {
  779. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  780. if err != nil || bookResult.RoleId == conf.BookObserver {
  781. beego.Error("FindByIdentify => ", err)
  782. c.Data["ErrorMessage"] = "项目不存在或权限不足"
  783. return
  784. }
  785. book_id = bookResult.BookId
  786. c.Data["Model"] = bookResult
  787. }
  788. if docId <= 0 {
  789. c.Data["ErrorMessage"] = "参数错误"
  790. return
  791. }
  792. doc, err := models.NewDocument().Find(docId)
  793. if err != nil {
  794. beego.Error("Delete => ", err)
  795. c.Data["ErrorMessage"] = "获取历史失败"
  796. return
  797. }
  798. // 如果文档所属项目错误
  799. if doc.BookId != book_id {
  800. c.Data["ErrorMessage"] = "参数错误"
  801. return
  802. }
  803. historis, totalCount, err := models.NewDocumentHistory().FindToPager(docId, pageIndex, conf.PageSize)
  804. if err != nil {
  805. beego.Error("FindToPager => ", err)
  806. c.Data["ErrorMessage"] = "获取历史失败"
  807. return
  808. }
  809. c.Data["List"] = historis
  810. c.Data["PageHtml"] = ""
  811. c.Data["Document"] = doc
  812. if totalCount > 0 {
  813. pager := pagination.NewPagination(c.Ctx.Request, totalCount, conf.PageSize, c.BaseUrl())
  814. c.Data["PageHtml"] = pager.HtmlPages()
  815. }
  816. }
  817. func (c *DocumentController) DeleteHistory() {
  818. c.Prepare()
  819. c.TplName = "document/history.tpl"
  820. identify := c.GetString("identify")
  821. docId, err := c.GetInt("doc_id", 0)
  822. historyId, _ := c.GetInt("history_id", 0)
  823. if historyId <= 0 {
  824. c.JsonResult(6001, "参数错误")
  825. }
  826. bookId := 0
  827. // 如果是超级管理员则忽略权限判断
  828. if c.Member.IsAdministrator() {
  829. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  830. if err != nil {
  831. beego.Error("FindByIdentify => ", err)
  832. c.JsonResult(6002, "项目不存在或权限不足")
  833. }
  834. bookId = book.BookId
  835. } else {
  836. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  837. if err != nil || bookResult.RoleId == conf.BookObserver {
  838. beego.Error("FindByIdentify => ", err)
  839. c.JsonResult(6002, "项目不存在或权限不足")
  840. }
  841. bookId = bookResult.BookId
  842. }
  843. if docId <= 0 {
  844. c.JsonResult(6001, "参数错误")
  845. }
  846. doc, err := models.NewDocument().Find(docId)
  847. if err != nil {
  848. beego.Error("Delete => ", err)
  849. c.JsonResult(6001, "获取历史失败")
  850. }
  851. // 如果文档所属项目错误
  852. if doc.BookId != bookId {
  853. c.JsonResult(6001, "参数错误")
  854. }
  855. err = models.NewDocumentHistory().Delete(historyId, docId)
  856. if err != nil {
  857. beego.Error(err)
  858. c.JsonResult(6002, "删除失败")
  859. }
  860. c.JsonResult(0, "ok")
  861. }
  862. func (c *DocumentController) RestoreHistory() {
  863. c.Prepare()
  864. c.TplName = "document/history.tpl"
  865. identify := c.GetString("identify")
  866. docId, err := c.GetInt("doc_id", 0)
  867. historyId, _ := c.GetInt("history_id", 0)
  868. if historyId <= 0 {
  869. c.JsonResult(6001, "参数错误")
  870. }
  871. bookId := 0
  872. // 如果是超级管理员则忽略权限判断
  873. if c.Member.IsAdministrator() {
  874. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  875. if err != nil {
  876. beego.Error("FindByIdentify => ", err)
  877. c.JsonResult(6002, "项目不存在或权限不足")
  878. }
  879. bookId = book.BookId
  880. } else {
  881. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  882. if err != nil || bookResult.RoleId == conf.BookObserver {
  883. beego.Error("FindByIdentify => ", err)
  884. c.JsonResult(6002, "项目不存在或权限不足")
  885. }
  886. bookId = bookResult.BookId
  887. }
  888. if docId <= 0 {
  889. c.JsonResult(6001, "参数错误")
  890. }
  891. doc, err := models.NewDocument().Find(docId)
  892. if err != nil {
  893. beego.Error("Delete => ", err)
  894. c.JsonResult(6001, "获取历史失败")
  895. }
  896. // 如果文档所属项目错误
  897. if doc.BookId != bookId {
  898. c.JsonResult(6001, "参数错误")
  899. }
  900. err = models.NewDocumentHistory().Restore(historyId, docId, c.Member.MemberId)
  901. if err != nil {
  902. beego.Error(err)
  903. c.JsonResult(6002, "删除失败")
  904. }
  905. c.JsonResult(0, "ok", doc)
  906. }
  907. func (c *DocumentController) Compare() {
  908. c.Prepare()
  909. c.TplName = "document/compare.tpl"
  910. historyId, _ := strconv.Atoi(c.Ctx.Input.Param(":id"))
  911. identify := c.Ctx.Input.Param(":key")
  912. bookId := 0
  913. editor := "markdown"
  914. // 如果是超级管理员则忽略权限判断
  915. if c.Member.IsAdministrator() {
  916. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  917. if err != nil {
  918. beego.Error("DocumentController.Compare => ", err)
  919. c.Abort("403")
  920. return
  921. }
  922. bookId = book.BookId
  923. c.Data["Model"] = book
  924. editor = book.Editor
  925. } else {
  926. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  927. if err != nil || bookResult.RoleId == conf.BookObserver {
  928. beego.Error("FindByIdentify => ", err)
  929. c.Abort("403")
  930. return
  931. }
  932. bookId = bookResult.BookId
  933. c.Data["Model"] = bookResult
  934. editor = bookResult.Editor
  935. }
  936. if historyId <= 0 {
  937. c.ShowErrorPage(60002, "参数错误")
  938. }
  939. history, err := models.NewDocumentHistory().Find(historyId)
  940. if err != nil {
  941. beego.Error("DocumentController.Compare => ", err)
  942. c.ShowErrorPage(60003, err.Error())
  943. }
  944. doc, err := models.NewDocument().Find(history.DocumentId)
  945. if doc.BookId != bookId {
  946. c.ShowErrorPage(60002, "参数错误")
  947. }
  948. c.Data["HistoryId"] = historyId
  949. c.Data["DocumentId"] = doc.DocumentId
  950. if editor == "markdown" {
  951. c.Data["HistoryContent"] = history.Markdown
  952. c.Data["Content"] = doc.Markdown
  953. } else {
  954. c.Data["HistoryContent"] = template.HTML(history.Content)
  955. c.Data["Content"] = template.HTML(doc.Content)
  956. }
  957. }
  958. // 递归生成文档序列数组
  959. func RecursiveFun(parentId int, prefix, dpath string, c *DocumentController, book *models.BookResult, docs []*models.Document, paths *list.List) {
  960. for _, item := range docs {
  961. if item.ParentId == parentId {
  962. EachFun(prefix, dpath, c, book, item, paths)
  963. for _, sub := range docs {
  964. if sub.ParentId == item.DocumentId {
  965. prefix += strconv.Itoa(item.ParentId) + strconv.Itoa(item.OrderSort) + strconv.Itoa(item.DocumentId)
  966. RecursiveFun(item.DocumentId, prefix, dpath, c, book, docs, paths)
  967. break
  968. }
  969. }
  970. }
  971. }
  972. }
  973. func EachFun(prefix, dpath string, c *DocumentController, book *models.BookResult, item *models.Document, paths *list.List) {
  974. name := prefix + strconv.Itoa(item.ParentId) + strconv.Itoa(item.OrderSort) + strconv.Itoa(item.DocumentId)
  975. fpath := dpath + "/" + name + ".html"
  976. paths.PushBack(fpath)
  977. f, err := os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0777)
  978. if err != nil {
  979. beego.Error(err)
  980. c.Abort("500")
  981. }
  982. html, err := c.ExecuteViewPathTemplate("document/export.tpl", map[string]interface{}{"Model": book, "Lists": item, "BaseUrl": c.BaseUrl()})
  983. if err != nil {
  984. f.Close()
  985. beego.Error(err)
  986. c.Abort("500")
  987. }
  988. buf := bytes.NewReader([]byte(html))
  989. doc, err := goquery.NewDocumentFromReader(buf)
  990. doc.Find("img").Each(func(i int, contentSelection *goquery.Selection) {
  991. if src, ok := contentSelection.Attr("src"); ok && strings.HasPrefix(src, "/uploads/") {
  992. contentSelection.SetAttr("src", c.BaseUrl()+src)
  993. }
  994. })
  995. html, err = doc.Html()
  996. if err != nil {
  997. f.Close()
  998. beego.Error(err)
  999. c.Abort("500")
  1000. }
  1001. // html = strings.Replace(html, "<img src=\"/uploads", "<img src=\"" + c.BaseUrl() + "/uploads", -1)
  1002. f.WriteString(html)
  1003. f.Close()
  1004. }
  1005. // 判断用户是否可以阅读文档
  1006. func isReadable(identify, token string, c *DocumentController) *models.BookResult {
  1007. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  1008. if err != nil {
  1009. beego.Error(err)
  1010. c.Abort("500")
  1011. }
  1012. // 如果文档是私有的
  1013. if book.PrivatelyOwned == 1 && !c.Member.IsAdministrator() {
  1014. is_ok := false
  1015. if c.Member != nil {
  1016. _, err := models.NewRelationship().FindForRoleId(book.BookId, c.Member.MemberId)
  1017. if err == nil {
  1018. is_ok = true
  1019. }
  1020. }
  1021. if book.PrivateToken != "" && !is_ok {
  1022. // 如果有访问的 Token,并且该项目设置了访问 Token,并且和用户提供的相匹配,则记录到 Session 中。
  1023. // 如果用户未提供 Token 且用户登录了,则判断用户是否参与了该项目。
  1024. // 如果用户未登录,则从 Session 中读取 Token。
  1025. if token != "" && strings.EqualFold(token, book.PrivateToken) {
  1026. c.SetSession(identify, token)
  1027. } else if token, ok := c.GetSession(identify).(string); !ok || !strings.EqualFold(token, book.PrivateToken) {
  1028. c.Abort("403")
  1029. }
  1030. } else if !is_ok {
  1031. c.Abort("403")
  1032. }
  1033. }
  1034. bookResult := models.NewBookResult().ToBookResult(*book)
  1035. if c.Member != nil {
  1036. rel, err := models.NewRelationship().FindByBookIdAndMemberId(bookResult.BookId, c.Member.MemberId)
  1037. if err == nil {
  1038. bookResult.MemberId = rel.MemberId
  1039. bookResult.RoleId = rel.RoleId
  1040. bookResult.RelationshipId = rel.RelationshipId
  1041. }
  1042. }
  1043. // 判断是否需要显示评论框
  1044. if bookResult.CommentStatus == "closed" {
  1045. bookResult.IsDisplayComment = false
  1046. } else if bookResult.CommentStatus == "open" {
  1047. bookResult.IsDisplayComment = true
  1048. } else if bookResult.CommentStatus == "group_only" {
  1049. bookResult.IsDisplayComment = bookResult.RelationshipId > 0
  1050. } else if bookResult.CommentStatus == "registered_only" {
  1051. bookResult.IsDisplayComment = true
  1052. }
  1053. return bookResult
  1054. }
  1055. func isUserLoggedIn(c *DocumentController) bool {
  1056. return c.Member != nil && c.Member.MemberId > 0
  1057. }
  1058. func promptUserToLogIn(c *DocumentController) {
  1059. beego.Info("Access " + c.Ctx.Request.URL.RequestURI() + " not permitted.")
  1060. beego.Info(" Access will be redirected to login page(SessionId: " + c.CruSession.SessionID() + ").")
  1061. if c.IsAjax() {
  1062. c.JsonResult(6000, "请重新登录。")
  1063. } else {
  1064. c.Redirect(conf.URLFor("AccountController.Login")+ "?url=" + url.PathEscape(conf.BaseUrl+ c.Ctx.Request.URL.RequestURI()), 302)
  1065. }
  1066. }