The backend for the AiteStory website
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
6.9 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. package controllers
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strings"
  6. "time"
  7. "git.aiterp.net/gisle/wrouter/response"
  8. "git.aiterp.net/AiteRP/aitestory/model"
  9. "git.aiterp.net/AiteRP/aitestory/viewmodel"
  10. "git.aiterp.net/AiteRP/aitestory/view"
  11. "git.aiterp.net/gisle/wrouter"
  12. "git.aiterp.net/gisle/wrouter/auth"
  13. )
  14. // PageController serves page creation, viewing, editing and deleting through the website.
  15. var PageController = wrouter.Router{}
  16. func pageCreate(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool {
  17. if (req.Method != "GET" && req.Method != "POST") || len(req.URL.Path) > len(path) {
  18. return false
  19. }
  20. pc := viewmodel.PageForm{}
  21. pc.Setup(user)
  22. pc.Operation = "Create"
  23. // Make sure the user is logged in
  24. if user == nil {
  25. // It's completely safe to eject logged-out users if they're not submitting
  26. if req.Method == "GET" {
  27. http.Redirect(w, req, "/user/login?form=/page/create", 302)
  28. return true
  29. }
  30. // Logged in users would probably want a chance to log in on another form, though.
  31. pc.Error = "You are not logged in."
  32. }
  33. // Respect the banhammer's authority
  34. if user != nil && user.Level == "restricted" {
  35. pc.Error = "Your user account is not permitted to do this"
  36. }
  37. // Handle submissions if nothing has complained yet
  38. if req.Method == "POST" && pc.Error == "" {
  39. // Validate form
  40. req.ParseForm()
  41. errs := pc.Page.ParseForm(req.Form)
  42. if len(errs) > 0 {
  43. pc.Error = "Validation failed: " + errs[0].Error()
  44. }
  45. // Parse the tags textbox
  46. pc.TagInput = req.Form.Get("tags")
  47. tagLines := strings.Split(pc.TagInput, "\n")
  48. for _, line := range tagLines {
  49. var tagType, tagName string
  50. // Skip empty lines, and allow some accidental letters
  51. if len(line) < 2 {
  52. continue
  53. }
  54. // Parse tokens
  55. tokens := strings.SplitN(line, ":", 2)
  56. if len(tokens) == 2 {
  57. tagType = strings.Trim(tokens[0], "  \t\r")
  58. tagName = strings.Trim(tokens[1], "  \t\r")
  59. } else {
  60. tagType = "*" // Permit untyped tags if it exists.
  61. tagName = strings.Trim(tokens[0], "  \t\r")
  62. }
  63. // Grab the tag
  64. tag, err := model.EnsureTag(tagType, tagName)
  65. if err != nil {
  66. pc.Error = "Check your tags: " + err.Error()
  67. break
  68. }
  69. // Take a copy of it
  70. pc.Page.Tags = append(pc.Page.Tags, *tag)
  71. }
  72. // If everything worked out, fill in the last bits and send it off
  73. if len(errs) == 0 && pc.Error == "" {
  74. pc.Page.Author = user.FullID()
  75. pc.Page.PublishDate = time.Now()
  76. pc.Page.EditDate = pc.Page.PublishDate.Add(-time.Hour)
  77. err := pc.Page.Insert()
  78. if err != nil {
  79. pc.Error = "Insert failed: " + err.Error()
  80. } else {
  81. // Let us see what you have wrought upon the world
  82. http.Redirect(w, req, "/page/"+pc.Page.ID, 302)
  83. return true
  84. }
  85. }
  86. } else {
  87. // Losing input makes the user sad, let's see to it
  88. // that it doesn't happen.
  89. req.ParseForm()
  90. pc.Page.ParseForm(req.Form)
  91. pc.TagInput = req.Form.Get("tags")
  92. }
  93. view.Render(w, "page/create", 200, pc)
  94. return true
  95. }
  96. func pageView(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool {
  97. if req.Method != "GET" || strings.LastIndex(req.URL.Path, "/") > len(path) {
  98. return false
  99. }
  100. page, err := model.FindPage(req.URL.Path[len(path):])
  101. if err != nil {
  102. response.Text(w, 500, err.Error())
  103. return true
  104. } else if page == nil {
  105. return false
  106. }
  107. pv := viewmodel.PageView{}
  108. pv.Page = page
  109. pv.Setup(user)
  110. view.Render(w, "page/view", 200, pv)
  111. return true
  112. }
  113. func pageDelete(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool {
  114. if (req.Method != "GET" && req.Method != "POST") || strings.LastIndex(req.URL.Path, "/") > len(path) {
  115. return false
  116. }
  117. page, err := model.FindPage(req.URL.Path[len(path):])
  118. if err != nil {
  119. response.Text(w, 500, err.Error())
  120. return true
  121. } else if page == nil {
  122. return false
  123. }
  124. pv := viewmodel.PageView{}
  125. pv.Page = page
  126. pv.Setup(user)
  127. if user == nil || user.FullID() != page.Author {
  128. view.Render(w, "message/error-access", http.StatusForbidden, path)
  129. return true
  130. }
  131. if req.Method == "POST" {
  132. req.ParseForm()
  133. // Catch sneaky shenanigans
  134. if req.Form.Get("aft") != user.Session.ID {
  135. view.Render(w, "message/error-forgery", http.StatusForbidden, path)
  136. return true
  137. }
  138. // Thy will be done
  139. err := page.Delete()
  140. if err != nil {
  141. // It wasn't done D:
  142. view.Render(w, "message/error-internal", http.StatusInternalServerError, err)
  143. return true
  144. }
  145. // It has been done
  146. view.Render(w, "message/page-deleted", 200, pv)
  147. return true
  148. }
  149. view.Render(w, "page/delete", 200, pv)
  150. return true
  151. }
  152. func pageEdit(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool {
  153. if (req.Method != "GET" && req.Method != "POST") || strings.LastIndex(req.URL.Path, "/") > len(path) {
  154. return false
  155. }
  156. page, err := model.FindPage(req.URL.Path[len(path):])
  157. if err != nil {
  158. response.Text(w, 500, err.Error())
  159. return true
  160. } else if page == nil {
  161. return false
  162. }
  163. pf := viewmodel.PageForm{}
  164. pf.Setup(user)
  165. pf.Operation = "Edit"
  166. pf.Page = *page
  167. if user == nil {
  168. pf.Error = "You are not logged in"
  169. }
  170. if user != nil && user.FullID() != page.Author {
  171. pf.Error = "Permission denied"
  172. }
  173. if req.Method == "POST" {
  174. req.ParseForm()
  175. errs := pf.Page.ParseForm(req.Form)
  176. if len(errs) > 0 {
  177. pf.Error = "Validation failed: " + errs[0].Error()
  178. }
  179. if len(errs) == 0 && pf.Error == "" {
  180. pf.TagInput = req.Form.Get("tags")
  181. pf.Page.Tags = make([]model.Tag, 0, strings.Count(pf.TagInput, "\n"))
  182. tagLines := strings.Split(pf.TagInput, "\n")
  183. for _, line := range tagLines {
  184. var tagType, tagName string
  185. // Skip empty lines, and allow some accidental letters
  186. if len(line) < 2 {
  187. continue
  188. }
  189. // Parse tokens
  190. tokens := strings.SplitN(line, ":", 2)
  191. if len(tokens) == 2 {
  192. tagType = strings.Trim(tokens[0], "  \t\r")
  193. tagName = strings.Trim(tokens[1], "  \t\r")
  194. } else {
  195. tagType = "*" // Permit untyped tags if it exists.
  196. tagName = strings.Trim(tokens[0], "  \t\r")
  197. }
  198. // Grab the tag
  199. tag, err := model.EnsureTag(tagType, tagName)
  200. if err != nil {
  201. pf.Error = "Check your tags: " + err.Error()
  202. break
  203. }
  204. // Take a copy of it
  205. pf.Page.Tags = append(pf.Page.Tags, *tag)
  206. }
  207. if pf.Error == "" {
  208. pf.Page.EditDate = time.Now()
  209. err := pf.Page.Update()
  210. if err != nil {
  211. pf.Error = "Edit failed: " + err.Error()
  212. } else {
  213. http.Redirect(w, req, "/page/"+pf.Page.ID, 302)
  214. return true
  215. }
  216. }
  217. }
  218. } else {
  219. for _, tag := range page.Tags {
  220. line := fmt.Sprintf("%s: %s\n", tag.Type, tag.Name)
  221. if page.PrimaryTag().ID == tag.ID {
  222. pf.TagInput = line + pf.TagInput
  223. } else {
  224. pf.TagInput += line
  225. }
  226. }
  227. }
  228. view.Render(w, "page/edit", 200, pf)
  229. return true
  230. }
  231. func init() {
  232. PageController.Function("/create", pageCreate)
  233. PageController.Function("/edit/", pageEdit)
  234. PageController.Function("/delete/", pageDelete)
  235. PageController.Function("/", pageView)
  236. }