DocumentController.go 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478
  1. package controllers
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "html/template"
  7. "image/png"
  8. "io"
  9. "mime/multipart"
  10. "net/http"
  11. "net/url"
  12. "os"
  13. "path/filepath"
  14. "regexp"
  15. "strconv"
  16. "strings"
  17. "time"
  18. "github.com/beego/beego/v2/client/orm"
  19. "github.com/beego/beego/v2/core/logs"
  20. "github.com/beego/beego/v2/server/web"
  21. "github.com/beego/i18n"
  22. "github.com/boombuler/barcode"
  23. "github.com/boombuler/barcode/qr"
  24. "github.com/mindoc-org/mindoc/conf"
  25. "github.com/mindoc-org/mindoc/models"
  26. "github.com/mindoc-org/mindoc/utils"
  27. "github.com/mindoc-org/mindoc/utils/cryptil"
  28. "github.com/mindoc-org/mindoc/utils/filetil"
  29. "github.com/mindoc-org/mindoc/utils/gopool"
  30. "github.com/mindoc-org/mindoc/utils/pagination"
  31. "github.com/russross/blackfriday/v2"
  32. )
  33. // DocumentController struct
  34. type DocumentController struct {
  35. BaseController
  36. }
  37. // Document prev&next
  38. type DocumentTreeFlatten struct {
  39. DocumentId int `json:"id"`
  40. DocumentName string `json:"text"`
  41. // ParentId interface{} `json:"parent"`
  42. Identify string `json:"identify"`
  43. // BookIdentify string `json:"-"`
  44. // Version int64 `json:"version"`
  45. }
  46. // 文档首页
  47. func (c *DocumentController) Index() {
  48. c.Prepare()
  49. identify := c.Ctx.Input.Param(":key")
  50. token := c.GetString("token")
  51. if identify == "" {
  52. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.item_not_exist"))
  53. }
  54. // 如果没有开启匿名访问则跳转到登录
  55. if !c.EnableAnonymous && !c.isUserLoggedIn() {
  56. promptUserToLogIn(c)
  57. return
  58. }
  59. bookResult := c.isReadable(identify, token)
  60. c.TplName = "document/" + bookResult.Theme + "_read.tpl"
  61. selected := 0
  62. if bookResult.IsUseFirstDocument {
  63. doc, err := bookResult.FindFirstDocumentByBookId(bookResult.BookId)
  64. if err == nil {
  65. selected = doc.DocumentId
  66. c.Data["Title"] = doc.DocumentName
  67. c.Data["Content"] = template.HTML(doc.Release)
  68. c.Data["Description"] = utils.AutoSummary(doc.Release, 120)
  69. c.Data["FoldSetting"] = "first"
  70. if bookResult.Editor == EditorCherryMarkdown {
  71. c.Data["MarkdownTheme"] = doc.MarkdownTheme
  72. }
  73. if bookResult.IsDisplayComment {
  74. // 获取评论、分页
  75. comments, count, _ := models.NewComment().QueryCommentByDocumentId(doc.DocumentId, 1, conf.PageSize, c.Member)
  76. page := pagination.PageUtil(int(count), 1, conf.PageSize, comments)
  77. c.Data["Page"] = page
  78. }
  79. }
  80. } else {
  81. c.Data["Title"] = i18n.Tr(c.Lang, "blog.summary")
  82. c.Data["Content"] = template.HTML(blackfriday.Run([]byte(bookResult.Description)))
  83. c.Data["FoldSetting"] = "closed"
  84. }
  85. tree, err := models.NewDocument().CreateDocumentTreeForHtml(bookResult.BookId, selected)
  86. if err != nil {
  87. if err == orm.ErrNoRows {
  88. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.no_doc_in_cur_proj"))
  89. } else {
  90. logs.Error("生成项目文档树时出错 -> ", err)
  91. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.build_doc_tree_error"))
  92. }
  93. }
  94. c.Data["IS_DOCUMENT_INDEX"] = true
  95. c.Data["Model"] = bookResult
  96. c.Data["Result"] = template.HTML(tree)
  97. }
  98. // CheckPassword : Handles password verification for private documents,
  99. // and front-end requests are made through Ajax.
  100. func (c *DocumentController) CheckPassword() {
  101. identify := c.Ctx.Input.Param(":key")
  102. password := c.GetString("bPassword")
  103. if identify == "" || password == "" {
  104. c.JsonResult(http.StatusBadRequest, i18n.Tr(c.Lang, "message.param_error"))
  105. }
  106. // You have not logged in and need to log in again.
  107. if !c.EnableAnonymous && !c.isUserLoggedIn() {
  108. logs.Info("You have not logged in and need to log in again(SessionId: %s).",
  109. c.CruSession.SessionID(context.TODO()))
  110. c.JsonResult(6000, i18n.Tr(c.Lang, "message.need_relogin"))
  111. return
  112. }
  113. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  114. if err != nil {
  115. logs.Error(err)
  116. c.JsonResult(500, i18n.Tr(c.Lang, "message.item_not_exist"))
  117. }
  118. if book.BookPassword != password {
  119. c.JsonResult(5001, i18n.Tr(c.Lang, "message.wrong_password"))
  120. } else {
  121. c.SetSession(identify, password)
  122. c.JsonResult(0, "OK")
  123. }
  124. }
  125. // 阅读文档
  126. func (c *DocumentController) Read() {
  127. identify := c.Ctx.Input.Param(":key")
  128. token := c.GetString("token")
  129. id := c.GetString(":id")
  130. if identify == "" || id == "" {
  131. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.item_not_exist"))
  132. }
  133. // 如果没有开启匿名访问则跳转到登录
  134. if !c.EnableAnonymous && !c.isUserLoggedIn() {
  135. promptUserToLogIn(c)
  136. return
  137. }
  138. bookResult := c.isReadable(identify, token)
  139. c.TplName = fmt.Sprintf("document/%s_read.tpl", bookResult.Theme)
  140. doc := models.NewDocument()
  141. if docId, err := strconv.Atoi(id); err == nil {
  142. doc, err = doc.FromCacheById(docId)
  143. if err != nil || doc == nil {
  144. logs.Error("从缓存中读取文档时失败 ->", err)
  145. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.doc_not_exist"))
  146. return
  147. }
  148. } else {
  149. doc, err = doc.FromCacheByIdentify(id, bookResult.BookId)
  150. if err != nil || doc == nil {
  151. if err == orm.ErrNoRows {
  152. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.doc_not_exist"))
  153. } else {
  154. logs.Error("从数据库查询文档时出错 ->", err)
  155. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.unknown_exception"))
  156. }
  157. return
  158. }
  159. }
  160. if doc.BookId != bookResult.BookId {
  161. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.doc_not_exist"))
  162. }
  163. doc.Lang = c.Lang
  164. doc.Processor()
  165. attach, err := models.NewAttachment().FindListByDocumentId(doc.DocumentId)
  166. if err == nil {
  167. doc.AttachList = attach
  168. }
  169. // prev,next
  170. treeJson, err := models.NewDocument().FindDocumentTree2(bookResult.BookId)
  171. if err != nil {
  172. logs.Error("生成项目文档树时出错 ->", err)
  173. }
  174. res := getTreeRecursive(treeJson, 0)
  175. flat := make([]DocumentTreeFlatten, 0)
  176. Flatten(res, &flat)
  177. var index int
  178. for i, v := range flat {
  179. if v.Identify == id {
  180. index = i
  181. }
  182. }
  183. var PrevName, PrevPath, NextName, NextPath string
  184. if index == 0 {
  185. c.Data["PrevName"] = ""
  186. PrevName = ""
  187. } else {
  188. c.Data["PrevPath"] = identify + "/" + flat[index-1].Identify
  189. c.Data["PrevName"] = flat[index-1].DocumentName
  190. PrevPath = identify + "/" + flat[index-1].Identify
  191. PrevName = flat[index-1].DocumentName
  192. }
  193. if index == len(flat)-1 {
  194. c.Data["NextName"] = ""
  195. NextName = ""
  196. } else {
  197. c.Data["NextPath"] = identify + "/" + flat[index+1].Identify
  198. c.Data["NextName"] = flat[index+1].DocumentName
  199. NextPath = identify + "/" + flat[index+1].Identify
  200. NextName = flat[index+1].DocumentName
  201. }
  202. doc.IncrViewCount(doc.DocumentId)
  203. doc.ViewCount = doc.ViewCount + 1
  204. doc.PutToCache()
  205. if c.IsAjax() {
  206. var data struct {
  207. DocId int `json:"doc_id"`
  208. DocIdentify string `json:"doc_identify"`
  209. DocTitle string `json:"doc_title"`
  210. Body string `json:"body"`
  211. Title string `json:"title"`
  212. Version int64 `json:"version"`
  213. ViewCount int `json:"view_count"`
  214. MarkdownTheme string `json:"markdown_theme"`
  215. IsMarkdown bool `json:"is_markdown"`
  216. }
  217. data.DocId = doc.DocumentId
  218. data.DocIdentify = doc.Identify
  219. data.DocTitle = doc.DocumentName
  220. data.Body = doc.Release + "<div class='wiki-bottom-left'>"+ i18n.Tr(c.Lang, "doc.prev") + ": <a href='/docs/" + PrevPath + "' rel='prev'>" + PrevName + "</a><br />" + i18n.Tr(c.Lang, "doc.next") + ": <a href='/docs/" + NextPath + "' rel='next'>" + NextName + "</a><br /></div>"
  221. data.Title = doc.DocumentName + " - Powered by MinDoc"
  222. data.Version = doc.Version
  223. data.ViewCount = doc.ViewCount
  224. data.MarkdownTheme = doc.MarkdownTheme
  225. if bookResult.Editor == EditorCherryMarkdown {
  226. data.IsMarkdown = true
  227. }
  228. c.JsonResult(0, "ok", data)
  229. } else {
  230. c.Data["DocumentId"] = doc.DocumentId
  231. c.Data["DocIdentify"] = doc.Identify
  232. if bookResult.IsDisplayComment {
  233. // 获取评论、分页
  234. comments, count, _ := models.NewComment().QueryCommentByDocumentId(doc.DocumentId, 1, conf.PageSize, c.Member)
  235. page := pagination.PageUtil(int(count), 1, conf.PageSize, comments)
  236. c.Data["Page"] = page
  237. }
  238. }
  239. tree, err := models.NewDocument().CreateDocumentTreeForHtml(bookResult.BookId, doc.DocumentId)
  240. if err != nil && err != orm.ErrNoRows {
  241. logs.Error("生成项目文档树时出错 ->", err)
  242. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.build_doc_tree_error"))
  243. }
  244. c.Data["Description"] = utils.AutoSummary(doc.Release, 120)
  245. c.Data["Model"] = bookResult
  246. c.Data["Result"] = template.HTML(tree)
  247. c.Data["Title"] = doc.DocumentName
  248. c.Data["Content"] = template.HTML(doc.Release + "<div class='wiki-bottom-left'>"+ i18n.Tr(c.Lang, "doc.prev") + ": <a href='/docs/" + PrevPath + "' rel='prev'>" + PrevName + "</a><br />" + i18n.Tr(c.Lang, "doc.next") + ": <a href='/docs/" + NextPath + "' rel='next'>" + NextName + "</a><br /></div>")
  249. c.Data["ViewCount"] = doc.ViewCount
  250. c.Data["FoldSetting"] = "closed"
  251. if bookResult.Editor == EditorCherryMarkdown {
  252. c.Data["MarkdownTheme"] = doc.MarkdownTheme
  253. }
  254. if doc.IsOpen == 1 {
  255. c.Data["FoldSetting"] = "open"
  256. } else if doc.IsOpen == 2 {
  257. c.Data["FoldSetting"] = "empty"
  258. }
  259. }
  260. // 递归得到树状结构体
  261. func getTreeRecursive(list []*models.DocumentTree, parentId int) (res []*models.DocumentTree) {
  262. for _, v := range list {
  263. if v.ParentId == parentId {
  264. v.Children = getTreeRecursive(list, v.DocumentId)
  265. res = append(res, v)
  266. }
  267. }
  268. return res
  269. }
  270. // 递归将树状结构体转换为扁平结构体数组
  271. // func Flatten(list []*models.DocumentTree, flattened *[]DocumentTreeFlatten) (flatten *[]DocumentTreeFlatten) {
  272. func Flatten(list []*models.DocumentTree, flattened *[]DocumentTreeFlatten) {
  273. // Treeslice := make([]*DocumentTreeFlatten, 0)
  274. for _, v := range list {
  275. tree := make([]DocumentTreeFlatten, 1)
  276. tree[0].DocumentId = v.DocumentId
  277. tree[0].DocumentName = v.DocumentName
  278. tree[0].Identify = v.Identify
  279. *flattened = append(*flattened, tree...)
  280. if len(v.Children) > 0 {
  281. Flatten(v.Children, flattened)
  282. }
  283. }
  284. return
  285. }
  286. // 编辑文档
  287. func (c *DocumentController) Edit() {
  288. c.Prepare()
  289. if c.Member.Role == conf.MemberReaderRole {
  290. c.JsonResult(6001, i18n.Tr(c.Lang, "message.no_permission"))
  291. }
  292. identify := c.Ctx.Input.Param(":key")
  293. if identify == "" {
  294. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.project_id_error"))
  295. }
  296. bookResult := models.NewBookResult()
  297. var err error
  298. // 如果是管理者,则不判断权限
  299. if c.Member.IsAdministrator() {
  300. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  301. if err != nil {
  302. c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
  303. }
  304. bookResult = models.NewBookResult().ToBookResult(*book)
  305. } else {
  306. bookResult, err = models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  307. if err != nil {
  308. if err == orm.ErrNoRows || err == models.ErrPermissionDenied {
  309. c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
  310. } else {
  311. logs.Error("查询项目时出错 -> ", err)
  312. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.system_error"))
  313. }
  314. return
  315. }
  316. if bookResult.RoleId == conf.BookObserver {
  317. c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
  318. }
  319. }
  320. c.TplName = fmt.Sprintf("document/%s_edit_template.tpl", bookResult.Editor)
  321. c.Data["Model"] = bookResult
  322. r, _ := json.Marshal(bookResult)
  323. c.Data["ModelResult"] = template.JS(string(r))
  324. c.Data["Result"] = template.JS("[]")
  325. trees, err := models.NewDocument().FindDocumentTree(bookResult.BookId)
  326. if err != nil {
  327. logs.Error("FindDocumentTree => ", err)
  328. } else {
  329. if len(trees) > 0 {
  330. if jtree, err := json.Marshal(trees); err == nil {
  331. c.Data["Result"] = template.JS(string(jtree))
  332. }
  333. } else {
  334. c.Data["Result"] = template.JS("[]")
  335. }
  336. }
  337. c.Data["BaiDuMapKey"] = web.AppConfig.DefaultString("baidumapkey", "")
  338. if conf.GetUploadFileSize() > 0 {
  339. c.Data["UploadFileSize"] = conf.GetUploadFileSize()
  340. } else {
  341. c.Data["UploadFileSize"] = "undefined"
  342. }
  343. }
  344. // 创建一个文档
  345. func (c *DocumentController) Create() {
  346. identify := c.GetString("identify")
  347. docIdentify := c.GetString("doc_identify")
  348. docName := c.GetString("doc_name")
  349. parentId, _ := c.GetInt("parent_id", 0)
  350. docId, _ := c.GetInt("doc_id", 0)
  351. isOpen, _ := c.GetInt("is_open", 0)
  352. if identify == "" {
  353. c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
  354. }
  355. if docName == "" {
  356. c.JsonResult(6004, i18n.Tr(c.Lang, "message.doc_name_empty"))
  357. }
  358. bookId := 0
  359. // 如果是超级管理员则不判断权限
  360. if c.Member.IsAdministrator() {
  361. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  362. if err != nil {
  363. logs.Error(err)
  364. c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_existed_or_no_permit"))
  365. }
  366. bookId = book.BookId
  367. } else {
  368. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  369. if err != nil || bookResult.RoleId == conf.BookObserver {
  370. logs.Error("FindByIdentify => ", err)
  371. c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_existed_or_no_permit"))
  372. }
  373. bookId = bookResult.BookId
  374. }
  375. if docIdentify != "" {
  376. if ok, err := regexp.MatchString(`[a-z]+[a-zA-Z0-9_.\-]*$`, docIdentify); !ok || err != nil {
  377. c.JsonResult(6003, i18n.Tr(c.Lang, "message.project_id_tips"))
  378. }
  379. d, _ := models.NewDocument().FindByIdentityFirst(docIdentify, bookId)
  380. if d.DocumentId > 0 && d.DocumentId != docId {
  381. c.JsonResult(6006, i18n.Tr(c.Lang, "message.project_id_existed"))
  382. }
  383. }
  384. if parentId > 0 {
  385. doc, err := models.NewDocument().Find(parentId)
  386. if err != nil || doc.BookId != bookId {
  387. c.JsonResult(6003, i18n.Tr(c.Lang, "message.parent_id_not_existed"))
  388. }
  389. }
  390. document, _ := models.NewDocument().Find(docId)
  391. document.MemberId = c.Member.MemberId
  392. document.BookId = bookId
  393. document.Identify = docIdentify
  394. document.Version = time.Now().Unix()
  395. document.DocumentName = docName
  396. document.ParentId = parentId
  397. if isOpen == 1 {
  398. document.IsOpen = 1
  399. } else if isOpen == 2 {
  400. document.IsOpen = 2
  401. } else {
  402. document.IsOpen = 0
  403. }
  404. if err := document.InsertOrUpdate(); err != nil {
  405. logs.Error("添加或更新文档时出错 -> ", err)
  406. c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
  407. } else {
  408. c.JsonResult(0, "ok", document)
  409. }
  410. }
  411. // 上传附件或图片
  412. func (c *DocumentController) Upload() {
  413. identify := c.GetString("identify")
  414. docId, _ := c.GetInt("doc_id")
  415. isAttach := true
  416. if identify == "" {
  417. c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
  418. }
  419. names := []string{"editormd-file-file", "editormd-image-file", "file", "editormd-resource-file"}
  420. var files []*multipart.FileHeader
  421. for _, name := range names {
  422. file, err := c.GetFiles(name)
  423. if err != nil {
  424. continue
  425. }
  426. if len(file) > 0 && err == nil {
  427. files = append(files, file...)
  428. }
  429. }
  430. if len(files) == 0 {
  431. c.JsonResult(6003, i18n.Tr(c.Lang, "message.upload_file_empty"))
  432. return
  433. }
  434. result2 := []map[string]interface{}{}
  435. var result map[string]interface{}
  436. for i, _ := range files {
  437. //for each fileheader, get a handle to the actual file
  438. file, err := files[i].Open()
  439. defer file.Close()
  440. if err != nil {
  441. c.JsonResult(6002, err.Error())
  442. }
  443. // defer file.Close()
  444. type Size interface {
  445. Size() int64
  446. }
  447. // if conf.GetUploadFileSize() > 0 && moreFile.Size > conf.GetUploadFileSize() {
  448. if conf.GetUploadFileSize() > 0 && files[i].Size > conf.GetUploadFileSize() {
  449. c.JsonResult(6009, i18n.Tr(c.Lang, "message.upload_file_size_limit"))
  450. }
  451. // ext := filepath.Ext(moreFile.Filename)
  452. ext := filepath.Ext(files[i].Filename)
  453. //文件必须带有后缀名
  454. if ext == "" {
  455. c.JsonResult(6003, i18n.Tr(c.Lang, "message.upload_file_type_error"))
  456. }
  457. //如果文件类型设置为 * 标识不限制文件类型
  458. if conf.IsAllowUploadFileExt(ext) == false {
  459. c.JsonResult(6004, i18n.Tr(c.Lang, "message.upload_file_type_error"))
  460. }
  461. bookId := 0
  462. // 如果是超级管理员,则不判断权限
  463. if c.Member.IsAdministrator() {
  464. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  465. if err != nil {
  466. c.JsonResult(6006, i18n.Tr(c.Lang, "message.doc_not_exist_or_no_permit"))
  467. }
  468. bookId = book.BookId
  469. } else {
  470. book, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  471. if err != nil {
  472. logs.Error("DocumentController.Edit => ", err)
  473. if err == orm.ErrNoRows {
  474. c.JsonResult(6006, i18n.Tr(c.Lang, "message.no_permission"))
  475. }
  476. c.JsonResult(6001, err.Error())
  477. }
  478. // 如果没有编辑权限
  479. if book.RoleId != conf.BookEditor && book.RoleId != conf.BookAdmin && book.RoleId != conf.BookFounder {
  480. c.JsonResult(6006, i18n.Tr(c.Lang, "message.no_permission"))
  481. }
  482. bookId = book.BookId
  483. }
  484. if docId > 0 {
  485. doc, err := models.NewDocument().Find(docId)
  486. if err != nil {
  487. c.JsonResult(6007, i18n.Tr(c.Lang, "message.doc_not_exist"))
  488. }
  489. if doc.BookId != bookId {
  490. c.JsonResult(6008, i18n.Tr(c.Lang, "message.doc_not_belong_project"))
  491. }
  492. }
  493. fileName := "m_" + cryptil.UniqueId() + "_r"
  494. filePath := filepath.Join(conf.WorkingDirectory, "uploads", identify)
  495. //将图片和文件分开存放
  496. attachment := models.NewAttachment()
  497. var strategy filetil.FileTypeStrategy
  498. if filetil.IsImageExt(files[i].Filename) {
  499. strategy = filetil.ImageStrategy{}
  500. attachment.ResourceType = "image"
  501. } else if filetil.IsVideoExt(files[i].Filename) {
  502. strategy = filetil.VideoStrategy{}
  503. attachment.ResourceType = "video"
  504. } else {
  505. strategy = filetil.DefaultStrategy{}
  506. attachment.ResourceType = "file"
  507. }
  508. filePath = strategy.GetFilePath(filePath, fileName, ext)
  509. path := filepath.Dir(filePath)
  510. _ = os.MkdirAll(path, os.ModePerm)
  511. //copy the uploaded file to the destination file
  512. dst, err := os.Create(filePath)
  513. defer dst.Close()
  514. if _, err := io.Copy(dst, file); err != nil {
  515. logs.Error("保存文件失败 -> ", err)
  516. c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
  517. }
  518. attachment.BookId = bookId
  519. // attachment.FileName = moreFile.Filename
  520. attachment.FileName = files[i].Filename
  521. attachment.CreateAt = c.Member.MemberId
  522. attachment.FileExt = ext
  523. attachment.FilePath = strings.TrimPrefix(filePath, conf.WorkingDirectory)
  524. attachment.DocumentId = docId
  525. if fileInfo, err := os.Stat(filePath); err == nil {
  526. attachment.FileSize = float64(fileInfo.Size())
  527. }
  528. if docId > 0 {
  529. attachment.DocumentId = docId
  530. }
  531. if filetil.IsImageExt(files[i].Filename) || filetil.IsVideoExt(files[i].Filename) {
  532. attachment.HttpPath = "/" + strings.Replace(strings.TrimPrefix(filePath, conf.WorkingDirectory), "\\", "/", -1)
  533. if strings.HasPrefix(attachment.HttpPath, "//") {
  534. attachment.HttpPath = conf.URLForWithCdnImage(string(attachment.HttpPath[1:]))
  535. }
  536. isAttach = false
  537. }
  538. err = attachment.Insert()
  539. if err != nil {
  540. os.Remove(filePath)
  541. logs.Error("文件保存失败 ->", err)
  542. c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed"))
  543. }
  544. if attachment.HttpPath == "" {
  545. attachment.HttpPath = conf.URLForNotHost("DocumentController.DownloadAttachment", ":key", identify, ":attach_id", attachment.AttachmentId)
  546. if err := attachment.Update(); err != nil {
  547. logs.Error("保存文件失败 ->", err)
  548. c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
  549. }
  550. }
  551. result = map[string]interface{}{
  552. "errcode": 0,
  553. "success": 1,
  554. "message": "ok",
  555. "url": attachment.HttpPath,
  556. "link": attachment.HttpPath,
  557. "alt": attachment.FileName,
  558. "is_attach": isAttach,
  559. "attach": attachment,
  560. "resource_type": attachment.ResourceType,
  561. }
  562. result2 = append(result2, result)
  563. }
  564. if len(files) == 1 {
  565. // froala单文件上传
  566. c.Ctx.Output.JSON(result, true, false)
  567. } else {
  568. c.Ctx.Output.JSON(result2, true, false)
  569. }
  570. c.StopRun()
  571. }
  572. // 下载附件
  573. func (c *DocumentController) DownloadAttachment() {
  574. c.Prepare()
  575. identify := c.Ctx.Input.Param(":key")
  576. attachId, _ := strconv.Atoi(c.Ctx.Input.Param(":attach_id"))
  577. token := c.GetString("token")
  578. memberId := 0
  579. if c.Member != nil {
  580. memberId = c.Member.MemberId
  581. }
  582. bookId := 0
  583. // 判断用户是否参与了项目
  584. bookResult, err := models.NewBookResult().FindByIdentify(identify, memberId)
  585. if err != nil {
  586. // 判断项目公开状态
  587. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  588. if err != nil {
  589. if err == orm.ErrNoRows {
  590. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.item_not_exist"))
  591. } else {
  592. logs.Error("查找项目时出错 ->", err)
  593. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.system_error"))
  594. }
  595. }
  596. // 如果不是超级管理员则判断权限
  597. if c.Member == nil || c.Member.Role != conf.MemberSuperRole {
  598. // 如果项目是私有的,并且 token 不正确
  599. if (book.PrivatelyOwned == 1 && token == "") || (book.PrivatelyOwned == 1 && book.PrivateToken != token) {
  600. c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.no_permission"))
  601. }
  602. }
  603. bookId = book.BookId
  604. } else {
  605. bookId = bookResult.BookId
  606. }
  607. // 查找附件
  608. attachment, err := models.NewAttachment().Find(attachId)
  609. if err != nil {
  610. logs.Error("查找附件时出错 -> ", err)
  611. if err == orm.ErrNoRows {
  612. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.attachment_not_exist"))
  613. } else {
  614. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.system_error"))
  615. }
  616. }
  617. if attachment.BookId != bookId {
  618. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.attachment_not_exist"))
  619. }
  620. c.Ctx.Output.Download(filepath.Join(conf.WorkingDirectory, attachment.FilePath), attachment.FileName)
  621. c.StopRun()
  622. }
  623. // 删除附件
  624. func (c *DocumentController) RemoveAttachment() {
  625. c.Prepare()
  626. attachId, _ := c.GetInt("attach_id")
  627. if attachId <= 0 {
  628. c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
  629. }
  630. attach, err := models.NewAttachment().Find(attachId)
  631. if err != nil {
  632. logs.Error(err)
  633. c.JsonResult(6002, i18n.Tr(c.Lang, "message.attachment_not_exist"))
  634. }
  635. document, err := models.NewDocument().Find(attach.DocumentId)
  636. if err != nil {
  637. logs.Error(err)
  638. c.JsonResult(6003, i18n.Tr(c.Lang, "message.doc_not_exist"))
  639. }
  640. if c.Member.Role != conf.MemberSuperRole {
  641. rel, err := models.NewRelationship().FindByBookIdAndMemberId(document.BookId, c.Member.MemberId)
  642. if err != nil {
  643. logs.Error(err)
  644. c.JsonResult(6004, i18n.Tr(c.Lang, "message.no_permission"))
  645. }
  646. if rel.RoleId == conf.BookObserver {
  647. c.JsonResult(6004, i18n.Tr(c.Lang, "message.no_permission"))
  648. }
  649. }
  650. err = attach.Delete()
  651. if err != nil {
  652. logs.Error(err)
  653. c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
  654. }
  655. os.Remove(filepath.Join(conf.WorkingDirectory, attach.FilePath))
  656. c.JsonResult(0, "ok", attach)
  657. }
  658. // 删除文档
  659. func (c *DocumentController) Delete() {
  660. c.Prepare()
  661. identify := c.GetString("identify")
  662. docId, err := c.GetInt("doc_id", 0)
  663. bookId := 0
  664. // 如果是超级管理员则忽略权限判断
  665. if c.Member.IsAdministrator() {
  666. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  667. if err != nil {
  668. logs.Error("FindByIdentify => ", err)
  669. c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
  670. }
  671. bookId = book.BookId
  672. } else {
  673. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  674. if err != nil || bookResult.RoleId == conf.BookObserver {
  675. logs.Error("FindByIdentify => ", err)
  676. c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
  677. }
  678. bookId = bookResult.BookId
  679. }
  680. if docId <= 0 {
  681. c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
  682. }
  683. doc, err := models.NewDocument().Find(docId)
  684. if err != nil {
  685. logs.Error("Delete => ", err)
  686. c.JsonResult(6003, i18n.Tr(c.Lang, "message.failed"))
  687. }
  688. // 如果文档所属项目错误
  689. if doc.BookId != bookId {
  690. c.JsonResult(6004, i18n.Tr(c.Lang, "message.param_error"))
  691. }
  692. // 递归删除项目下的文档以及子文档
  693. err = doc.RecursiveDocument(doc.DocumentId)
  694. if err != nil {
  695. c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed"))
  696. }
  697. // 重置文档数量统计
  698. models.NewBook().ResetDocumentNumber(doc.BookId)
  699. c.JsonResult(0, "ok")
  700. }
  701. // 获取文档内容
  702. func (c *DocumentController) Content() {
  703. c.Prepare()
  704. identify := c.Ctx.Input.Param(":key")
  705. docId, err := c.GetInt("doc_id")
  706. if err != nil {
  707. docId, _ = strconv.Atoi(c.Ctx.Input.Param(":id"))
  708. }
  709. bookId := 0
  710. autoRelease := false
  711. // 如果是超级管理员,则忽略权限
  712. if c.Member.IsAdministrator() {
  713. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  714. if err != nil || book == nil {
  715. c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
  716. return
  717. }
  718. bookId = book.BookId
  719. autoRelease = book.AutoRelease == 1
  720. } else {
  721. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  722. if err != nil || bookResult.RoleId == conf.BookObserver {
  723. logs.Error("项目不存在或权限不足 -> ", err)
  724. c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
  725. }
  726. bookId = bookResult.BookId
  727. autoRelease = bookResult.AutoRelease
  728. }
  729. if docId <= 0 {
  730. c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
  731. }
  732. if c.Ctx.Input.IsPost() {
  733. markdown := strings.TrimSpace(c.GetString("markdown", ""))
  734. content := c.GetString("html")
  735. markdownTheme := c.GetString("markdown_theme", "theme__light")
  736. version, _ := c.GetInt64("version", 0)
  737. isCover := c.GetString("cover")
  738. doc, err := models.NewDocument().Find(docId)
  739. if err != nil || doc == nil {
  740. c.JsonResult(6003, i18n.Tr(c.Lang, "message.read_file_error"))
  741. return
  742. }
  743. if doc.BookId != bookId {
  744. c.JsonResult(6004, i18n.Tr(c.Lang, "message.dock_not_belong_project"))
  745. }
  746. if doc.Version != version && !strings.EqualFold(isCover, "yes") {
  747. logs.Info("%d|", version, doc.Version)
  748. c.JsonResult(6005, i18n.Tr(c.Lang, "message.confirm_override_doc"))
  749. }
  750. history := models.NewDocumentHistory()
  751. history.DocumentId = docId
  752. history.Content = doc.Content
  753. history.Markdown = doc.Markdown
  754. history.DocumentName = doc.DocumentName
  755. history.ModifyAt = c.Member.MemberId
  756. history.MemberId = doc.MemberId
  757. history.ParentId = doc.ParentId
  758. history.Version = time.Now().Unix()
  759. history.Action = "modify"
  760. history.ActionName = i18n.Tr(c.Lang, "doc.modify_doc")
  761. if markdown == "" && content != "" {
  762. doc.Markdown = content
  763. } else {
  764. doc.Markdown = markdown
  765. doc.MarkdownTheme = markdownTheme
  766. }
  767. doc.Version = time.Now().Unix()
  768. doc.Content = content
  769. doc.ModifyAt = c.Member.MemberId
  770. if err := doc.InsertOrUpdate(); err != nil {
  771. logs.Error("InsertOrUpdate => ", err)
  772. c.JsonResult(6006, i18n.Tr(c.Lang, "message.failed"))
  773. }
  774. // 如果启用了文档历史,则添加历史文档
  775. ///如果两次保存的MD5值不同则保存为历史,否则忽略
  776. go func(history *models.DocumentHistory) {
  777. if c.EnableDocumentHistory && cryptil.Md5Crypt(history.Markdown) != cryptil.Md5Crypt(doc.Markdown) {
  778. _, err = history.InsertOrUpdate()
  779. if err != nil {
  780. logs.Error("DocumentHistory InsertOrUpdate => ", err)
  781. }
  782. }
  783. }(history)
  784. //如果启用了自动发布
  785. if autoRelease {
  786. go func() {
  787. doc.Lang = c.Lang
  788. err := doc.ReleaseContent()
  789. if err == nil {
  790. logs.Informational(i18n.Tr(c.Lang, "message.doc_auto_published")+"-> document_id=%d;document_name=%s", doc.DocumentId, doc.DocumentName)
  791. }
  792. }()
  793. }
  794. c.JsonResult(0, "ok", doc)
  795. }
  796. doc, err := models.NewDocument().Find(docId)
  797. if err != nil {
  798. c.JsonResult(6003, i18n.Tr(c.Lang, "message.doc_not_exist"))
  799. return
  800. }
  801. attach, err := models.NewAttachment().FindListByDocumentId(doc.DocumentId)
  802. if err == nil {
  803. doc.AttachList = attach
  804. }
  805. c.JsonResult(0, "ok", doc)
  806. }
  807. // Export 导出
  808. func (c *DocumentController) Export() {
  809. c.Prepare()
  810. identify := c.Ctx.Input.Param(":key")
  811. if identify == "" {
  812. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.param_error"))
  813. }
  814. output := c.GetString("output")
  815. token := c.GetString("token")
  816. // 如果没有开启匿名访问则跳转到登录
  817. if !c.EnableAnonymous && !c.isUserLoggedIn() {
  818. promptUserToLogIn(c)
  819. return
  820. }
  821. if !conf.GetEnableExport() {
  822. c.ShowErrorPage(500, i18n.Tr(c.Lang, "export_func_disable"))
  823. }
  824. bookResult := models.NewBookResult()
  825. if c.Member != nil && c.Member.IsAdministrator() {
  826. book, err := models.NewBook().FindByIdentify(identify)
  827. if err != nil {
  828. if err == orm.ErrNoRows {
  829. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.item_not_exist"))
  830. } else {
  831. logs.Error("查找项目时出错 ->", err)
  832. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.system_error"))
  833. }
  834. }
  835. bookResult = models.NewBookResult().ToBookResult(*book)
  836. } else {
  837. bookResult = c.isReadable(identify, token)
  838. }
  839. if !bookResult.IsDownload {
  840. c.ShowErrorPage(200, i18n.Tr(c.Lang, "message.cur_project_export_func_disable"))
  841. }
  842. if !strings.HasPrefix(bookResult.Cover, "http:://") && !strings.HasPrefix(bookResult.Cover, "https:://") {
  843. bookResult.Cover = conf.URLForWithCdnImage(bookResult.Cover)
  844. }
  845. if output == Markdown {
  846. if bookResult.Editor != EditorMarkdown && bookResult.Editor != EditorCherryMarkdown {
  847. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.cur_project_not_support_md"))
  848. }
  849. p, err := bookResult.ExportMarkdown(c.CruSession.SessionID(context.TODO()))
  850. if err != nil {
  851. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.failed"))
  852. }
  853. c.Ctx.Output.Download(p, bookResult.BookName+".zip")
  854. c.StopRun()
  855. return
  856. }
  857. outputPath := filepath.Join(conf.GetExportOutputPath(), strconv.Itoa(bookResult.BookId))
  858. pdfpath := filepath.Join(outputPath, "book.pdf")
  859. epubpath := filepath.Join(outputPath, "book.epub")
  860. mobipath := filepath.Join(outputPath, "book.mobi")
  861. docxpath := filepath.Join(outputPath, "book.docx")
  862. if output == "pdf" && filetil.FileExists(pdfpath) {
  863. c.Ctx.Output.Download(pdfpath, bookResult.BookName+".pdf")
  864. c.Abort("200")
  865. } else if output == "epub" && filetil.FileExists(epubpath) {
  866. c.Ctx.Output.Download(epubpath, bookResult.BookName+".epub")
  867. c.Abort("200")
  868. } else if output == "mobi" && filetil.FileExists(mobipath) {
  869. c.Ctx.Output.Download(mobipath, bookResult.BookName+".mobi")
  870. c.Abort("200")
  871. } else if output == "docx" && filetil.FileExists(docxpath) {
  872. c.Ctx.Output.Download(docxpath, bookResult.BookName+".docx")
  873. c.Abort("200")
  874. } else if output == "pdf" || output == "epub" || output == "docx" || output == "mobi" {
  875. if err := models.BackgroundConvert(c.CruSession.SessionID(context.TODO()), bookResult); err != nil && err != gopool.ErrHandlerIsExist {
  876. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.export_failed"))
  877. }
  878. c.ShowErrorPage(200, i18n.Tr(c.Lang, "message.file_converting"))
  879. } else {
  880. c.ShowErrorPage(200, i18n.Tr(c.Lang, "message.unsupport_file_type"))
  881. }
  882. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.no_exportable_file"))
  883. }
  884. // 生成项目访问的二维码
  885. func (c *DocumentController) QrCode() {
  886. c.Prepare()
  887. identify := c.GetString(":key")
  888. book, err := models.NewBook().FindByIdentify(identify)
  889. if err != nil || book.BookId <= 0 {
  890. c.ShowErrorPage(404, i18n.Tr(c.Lang, "message.item_not_exist"))
  891. }
  892. uri := conf.URLFor("DocumentController.Index", ":key", identify)
  893. code, err := qr.Encode(uri, qr.L, qr.Unicode)
  894. if err != nil {
  895. logs.Error("生成二维码失败 ->", err)
  896. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.gen_qrcode_failed"))
  897. }
  898. code, err = barcode.Scale(code, 150, 150)
  899. if err != nil {
  900. logs.Error("生成二维码失败 ->", err)
  901. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.gen_qrcode_failed"))
  902. }
  903. c.Ctx.ResponseWriter.Header().Set("Content-Type", "image/png")
  904. // imgpath := filepath.Join("cache","qrcode",identify + ".png")
  905. err = png.Encode(c.Ctx.ResponseWriter, code)
  906. if err != nil {
  907. logs.Error("生成二维码失败 ->", err)
  908. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.gen_qrcode_failed"))
  909. }
  910. }
  911. // 项目内搜索
  912. func (c *DocumentController) Search() {
  913. c.Prepare()
  914. identify := c.Ctx.Input.Param(":key")
  915. token := c.GetString("token")
  916. keyword := strings.TrimSpace(c.GetString("keyword"))
  917. if identify == "" {
  918. c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
  919. }
  920. if !c.EnableAnonymous && !c.isUserLoggedIn() {
  921. promptUserToLogIn(c)
  922. return
  923. }
  924. bookResult := c.isReadable(identify, token)
  925. docs, err := models.NewDocumentSearchResult().SearchDocument(keyword, bookResult.BookId)
  926. if err != nil {
  927. logs.Error(err)
  928. c.JsonResult(6002, i18n.Tr(c.Lang, "message.search_result_error"))
  929. }
  930. if len(docs) < 0 {
  931. c.JsonResult(404, i18n.Tr(c.Lang, "message.no_data"))
  932. }
  933. for _, doc := range docs {
  934. doc.BookId = bookResult.BookId
  935. doc.BookName = bookResult.BookName
  936. doc.Description = bookResult.Description
  937. doc.BookIdentify = bookResult.Identify
  938. }
  939. c.JsonResult(0, "ok", docs)
  940. }
  941. // 文档历史列表
  942. func (c *DocumentController) History() {
  943. c.Prepare()
  944. c.TplName = "document/history.tpl"
  945. identify := c.GetString("identify")
  946. docId, err := c.GetInt("doc_id", 0)
  947. pageIndex, _ := c.GetInt("page", 1)
  948. bookId := 0
  949. // 如果是超级管理员则忽略权限判断
  950. if c.Member.IsAdministrator() {
  951. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  952. if err != nil {
  953. logs.Error("查找项目失败 ->", err)
  954. c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit")
  955. return
  956. }
  957. bookId = book.BookId
  958. c.Data["Model"] = book
  959. } else {
  960. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  961. if err != nil || bookResult.RoleId == conf.BookObserver {
  962. logs.Error("查找项目失败 ->", err)
  963. c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit")
  964. return
  965. }
  966. bookId = bookResult.BookId
  967. c.Data["Model"] = bookResult
  968. }
  969. if docId <= 0 {
  970. c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.param_error")
  971. return
  972. }
  973. doc, err := models.NewDocument().Find(docId)
  974. if err != nil {
  975. logs.Error("Delete => ", err)
  976. c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.get_doc_his_failed")
  977. return
  978. }
  979. // 如果文档所属项目错误
  980. if doc.BookId != bookId {
  981. c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.param_error")
  982. return
  983. }
  984. histories, totalCount, err := models.NewDocumentHistory().FindToPager(docId, pageIndex, conf.PageSize)
  985. if err != nil {
  986. logs.Error("分页查找文档历史失败 ->", err)
  987. c.Data["ErrorMessage"] = i18n.Tr(c.Lang, "message.get_doc_his_failed")
  988. return
  989. }
  990. c.Data["List"] = histories
  991. c.Data["PageHtml"] = ""
  992. c.Data["Document"] = doc
  993. if totalCount > 0 {
  994. pager := pagination.NewPagination(c.Ctx.Request, totalCount, conf.PageSize, c.BaseUrl())
  995. c.Data["PageHtml"] = pager.HtmlPages()
  996. }
  997. }
  998. func (c *DocumentController) DeleteHistory() {
  999. c.Prepare()
  1000. c.TplName = "document/history.tpl"
  1001. identify := c.GetString("identify")
  1002. docId, err := c.GetInt("doc_id", 0)
  1003. historyId, _ := c.GetInt("history_id", 0)
  1004. if historyId <= 0 {
  1005. c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
  1006. }
  1007. bookId := 0
  1008. // 如果是超级管理员则忽略权限判断
  1009. if c.Member.IsAdministrator() {
  1010. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  1011. if err != nil {
  1012. logs.Error("查找项目失败 ->", err)
  1013. c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
  1014. }
  1015. bookId = book.BookId
  1016. } else {
  1017. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  1018. if err != nil || bookResult.RoleId == conf.BookObserver {
  1019. logs.Error("查找项目失败 ->", err)
  1020. c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
  1021. }
  1022. bookId = bookResult.BookId
  1023. }
  1024. if docId <= 0 {
  1025. c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
  1026. }
  1027. doc, err := models.NewDocument().Find(docId)
  1028. if err != nil {
  1029. logs.Error("Delete => ", err)
  1030. c.JsonResult(6001, i18n.Tr(c.Lang, "message.get_doc_his_failed"))
  1031. }
  1032. // 如果文档所属项目错误
  1033. if doc.BookId != bookId {
  1034. c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
  1035. }
  1036. err = models.NewDocumentHistory().Delete(historyId, docId)
  1037. if err != nil {
  1038. logs.Error(err)
  1039. c.JsonResult(6002, i18n.Tr(c.Lang, "message.failed"))
  1040. }
  1041. c.JsonResult(0, "ok")
  1042. }
  1043. // 通过文档历史恢复文档
  1044. func (c *DocumentController) RestoreHistory() {
  1045. c.Prepare()
  1046. c.TplName = "document/history.tpl"
  1047. identify := c.GetString("identify")
  1048. docId, err := c.GetInt("doc_id", 0)
  1049. historyId, _ := c.GetInt("history_id", 0)
  1050. if historyId <= 0 {
  1051. c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
  1052. }
  1053. bookId := 0
  1054. // 如果是超级管理员则忽略权限判断
  1055. if c.Member.IsAdministrator() {
  1056. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  1057. if err != nil {
  1058. logs.Error("FindByIdentify => ", err)
  1059. c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
  1060. }
  1061. bookId = book.BookId
  1062. } else {
  1063. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  1064. if err != nil || bookResult.RoleId == conf.BookObserver {
  1065. logs.Error("FindByIdentify => ", err)
  1066. c.JsonResult(6002, i18n.Tr(c.Lang, "message.item_not_exist_or_no_permit"))
  1067. }
  1068. bookId = bookResult.BookId
  1069. }
  1070. if docId <= 0 {
  1071. c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
  1072. }
  1073. doc, err := models.NewDocument().Find(docId)
  1074. if err != nil {
  1075. logs.Error("Delete => ", err)
  1076. c.JsonResult(6001, i18n.Tr(c.Lang, "message.get_doc_his_failed"))
  1077. }
  1078. // 如果文档所属项目错误
  1079. if doc.BookId != bookId {
  1080. c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error"))
  1081. }
  1082. err = models.NewDocumentHistory().Restore(historyId, docId, c.Member.MemberId)
  1083. if err != nil {
  1084. logs.Error(err)
  1085. c.JsonResult(6002, i18n.Tr(c.Lang, "message.failed"))
  1086. }
  1087. c.JsonResult(0, "ok", doc)
  1088. }
  1089. func (c *DocumentController) Compare() {
  1090. c.Prepare()
  1091. c.TplName = "document/compare.tpl"
  1092. historyId, _ := strconv.Atoi(c.Ctx.Input.Param(":id"))
  1093. identify := c.Ctx.Input.Param(":key")
  1094. bookId := 0
  1095. editor := EditorMarkdown
  1096. // 如果是超级管理员则忽略权限判断
  1097. if c.Member.IsAdministrator() {
  1098. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  1099. if err != nil {
  1100. logs.Error("DocumentController.Compare => ", err)
  1101. c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.no_permission"))
  1102. return
  1103. }
  1104. bookId = book.BookId
  1105. c.Data["Model"] = book
  1106. editor = book.Editor
  1107. } else {
  1108. bookResult, err := models.NewBookResult().FindByIdentify(identify, c.Member.MemberId)
  1109. if err != nil || bookResult.RoleId == conf.BookObserver {
  1110. logs.Error("FindByIdentify => ", err)
  1111. c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.no_permission"))
  1112. return
  1113. }
  1114. bookId = bookResult.BookId
  1115. c.Data["Model"] = bookResult
  1116. editor = bookResult.Editor
  1117. }
  1118. if historyId <= 0 {
  1119. c.ShowErrorPage(60002, i18n.Tr(c.Lang, "message.param_error"))
  1120. }
  1121. history, err := models.NewDocumentHistory().Find(historyId)
  1122. if err != nil {
  1123. logs.Error("DocumentController.Compare => ", err)
  1124. c.ShowErrorPage(60003, err.Error())
  1125. }
  1126. doc, err := models.NewDocument().Find(history.DocumentId)
  1127. if err != nil || doc == nil || doc.BookId != bookId {
  1128. c.ShowErrorPage(60002, i18n.Tr(c.Lang, "message.doc_not_exist"))
  1129. return
  1130. }
  1131. c.Data["HistoryId"] = historyId
  1132. c.Data["DocumentId"] = doc.DocumentId
  1133. if editor == EditorMarkdown || editor == EditorCherryMarkdown {
  1134. c.Data["HistoryContent"] = history.Markdown
  1135. c.Data["Content"] = doc.Markdown
  1136. } else {
  1137. c.Data["HistoryContent"] = template.HTML(history.Content)
  1138. c.Data["Content"] = template.HTML(doc.Content)
  1139. }
  1140. }
  1141. // 判断用户是否可以阅读文档
  1142. func (c *DocumentController) isReadable(identify, token string) *models.BookResult {
  1143. book, err := models.NewBook().FindByFieldFirst("identify", identify)
  1144. if err != nil {
  1145. logs.Error(err)
  1146. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.item_not_exist"))
  1147. }
  1148. bookResult := models.NewBookResult().ToBookResult(*book)
  1149. isOk := false
  1150. if c.isUserLoggedIn() {
  1151. roleId, err := models.NewBook().FindForRoleId(book.BookId, c.Member.MemberId)
  1152. if err == nil {
  1153. isOk = true
  1154. bookResult.MemberId = c.Member.MemberId
  1155. bookResult.RoleId = roleId
  1156. }
  1157. }
  1158. /* 私有项目:
  1159. * 管理员可以直接访问
  1160. * 参与者可以直接访问
  1161. * 其他用户(支持匿名访问)
  1162. * token设置情况
  1163. * 已设置:可以通过token访问
  1164. * 未设置:不可以通过token访问
  1165. * password设置情况
  1166. * 已设置:可以通过password访问
  1167. * 未设置:不可以通过password访问
  1168. * 注意:
  1169. * 1. 第一次访问需要存session
  1170. * 2. 有session优先使用session中的token或者password,再使用携带的token或者password
  1171. * 3. 私有项目如果token和password都没有设置,则除管理员和参与者的其他用户不可以访问
  1172. * 4. 使用token访问如果不通过,则提示输入密码
  1173. */
  1174. if book.PrivatelyOwned == 1 {
  1175. if c.isUserLoggedIn() && c.Member.IsAdministrator() {
  1176. return bookResult
  1177. }
  1178. if isOk { // Project participant.
  1179. return bookResult
  1180. }
  1181. // Use session in preference.
  1182. if tokenOrPassword, ok := c.GetSession(identify).(string); ok {
  1183. if strings.EqualFold(book.PrivateToken, tokenOrPassword) || strings.EqualFold(book.BookPassword, tokenOrPassword) {
  1184. return bookResult
  1185. }
  1186. }
  1187. // Next: Session not exist or not correct.
  1188. if book.PrivateToken != "" && book.PrivateToken == token {
  1189. c.SetSession(identify, token)
  1190. return bookResult
  1191. } else if book.BookPassword != "" {
  1192. // Send a page for inputting password.
  1193. // For verification, see function DocumentController.CheckPassword
  1194. body, err := c.ExecuteViewPathTemplate("document/document_password.tpl",
  1195. map[string]string{"Identify": book.Identify, "Lang": c.Lang})
  1196. if err != nil {
  1197. logs.Error("显示密码页面失败 ->", err)
  1198. c.ShowErrorPage(500, i18n.Tr(c.Lang, "message.system_error"))
  1199. }
  1200. c.CustomAbort(200, body)
  1201. } else {
  1202. // No permission to access this book.
  1203. logs.Info("尝试访问文档但权限不足 ->", identify, token)
  1204. c.ShowErrorPage(403, i18n.Tr(c.Lang, "message.no_permission"))
  1205. }
  1206. }
  1207. return bookResult
  1208. }
  1209. func promptUserToLogIn(c *DocumentController) {
  1210. logs.Info("Access " + c.Ctx.Request.URL.RequestURI() + " not permitted.")
  1211. logs.Info(" Access will be redirected to login page(SessionId: " + c.CruSession.SessionID(context.TODO()) + ").")
  1212. if c.IsAjax() {
  1213. c.JsonResult(6000, i18n.Tr(c.Lang, "message.need_relogin"))
  1214. } else {
  1215. c.Redirect(conf.URLFor("AccountController.Login")+"?url="+url.PathEscape(conf.BaseUrl+c.Ctx.Request.URL.RequestURI()), 302)
  1216. }
  1217. }