1 package server
2
3 import (
4 "fmt"
5 "net/http"
6 "strconv"
7 "strings"
8
9 "codeberg.org/tslocum/sriracha/internal/database"
10 . "codeberg.org/tslocum/sriracha/model"
11 . "codeberg.org/tslocum/sriracha/util"
12 )
13
14 func (s *Server) loadCategoryForm(db *database.DB, r *http.Request, k *Category) {
15 parent := FormInt(r, "parent")
16 if parent == 0 {
17 k.Parent = nil
18 } else {
19 k.Parent = db.CategoryByID(parent)
20 }
21 k.Name = FormString(r, "name")
22 k.Description = FormString(r, "description")
23 }
24
25 func (s *Server) serveCategory(data *templateData, db *database.DB, w http.ResponseWriter, r *http.Request) {
26 data.Template = "manage_category"
27 data.Boards = db.AllBoards()
28 data.Manage.Category = &Category{}
29 data.Categories = db.AllCategories()
30
31 categoryAndBoard := func(s string) (*Category, *Board) {
32 split := strings.Split(s, "/")
33 if len(split) != 2 {
34 return nil, nil
35 }
36 categoryID, err := strconv.Atoi(split[0])
37 if err != nil {
38 return nil, nil
39 }
40 boardID, err := strconv.Atoi(split[1])
41 if err != nil {
42 return nil, nil
43 }
44 return db.CategoryByID(categoryID), db.BoardByID(boardID)
45 }
46
47 deleteBoardCategory := PathString(r, "/sriracha/category/board/delete/")
48 if deleteBoardCategory != "" {
49 if s.forbidden(w, data, "category.update") {
50 return
51 }
52 category, board := categoryAndBoard(deleteBoardCategory)
53 if category == nil {
54 data.ManageError("Invalid category.")
55 return
56 } else if board == nil {
57 data.ManageError("Invalid board.")
58 return
59 }
60
61 for i, b := range category.Boards {
62 if b.ID == board.ID {
63 category.Boards = append(category.Boards[:i], category.Boards[i+1:]...)
64 db.UpdateCategory(category)
65 s.refreshCategoryCache(db)
66 s.rebuildAll(db, false)
67 break
68 }
69 }
70
71 data.Redirect(w, r, "/sriracha/category/")
72 return
73 }
74
75 deleteCategoryID := PathInt(r, "/sriracha/category/delete/")
76 if deleteCategoryID > 0 {
77 if s.forbidden(w, data, "category.delete") {
78 return
79 }
80 c := db.CategoryByID(deleteCategoryID)
81 if c == nil {
82 data.ManageError("Invalid category.")
83 return
84 }
85 db.DeleteCategory(c.ID)
86
87 allCategories := db.AllCategories()
88 if len(allCategories) > 0 {
89 var haveRoot bool
90 for _, c := range allCategories {
91 if c.Parent == nil {
92 haveRoot = true
93 break
94 }
95 }
96 if !haveRoot {
97 db.RollBack()
98 data.ManageError("Failed to delete category: At least one category without any parent categories must exist.")
99 return
100 }
101 }
102
103 s.refreshCategoryCache(db)
104 s.rebuildAll(db, false)
105
106 s.log(db, data.Account, nil, fmt.Sprintf("Deleted category #%d", c.ID), "")
107
108 data.Redirect(w, r, "/sriracha/category/")
109 return
110 }
111
112 boardUp := PathString(r, "/sriracha/category/board/up/")
113 if boardUp != "" {
114 if s.forbidden(w, data, "category.update") {
115 return
116 }
117 category, board := categoryAndBoard(boardUp)
118 if category != nil && board != nil {
119 for i, b := range category.Boards {
120 if b.ID == board.ID && i > 0 {
121 category.Boards[i], category.Boards[i-1] = category.Boards[i-1], category.Boards[i]
122 db.UpdateCategory(category)
123 s.refreshCategoryCache(db)
124 s.rebuildAll(db, false)
125 break
126 }
127 }
128 }
129 data.Redirect(w, r, "/sriracha/category/")
130 return
131 }
132 boardDown := PathString(r, "/sriracha/category/board/down/")
133 if boardDown != "" {
134 if s.forbidden(w, data, "category.update") {
135 return
136 }
137 category, board := categoryAndBoard(boardDown)
138 if category != nil && board != nil {
139 for i, b := range category.Boards {
140 if b.ID == board.ID && i < len(category.Boards)-1 {
141 category.Boards[i], category.Boards[i+1] = category.Boards[i+1], category.Boards[i]
142 db.UpdateCategory(category)
143 s.refreshCategoryCache(db)
144 s.rebuildAll(db, false)
145 break
146 }
147 }
148 }
149 data.Redirect(w, r, "/sriracha/category/")
150 return
151 }
152
153 categoryUpID := PathInt(r, "/sriracha/category/up/")
154 if categoryUpID != 0 {
155 if s.forbidden(w, data, "category.update") {
156 return
157 }
158 category := db.CategoryByID(categoryUpID)
159 if category != nil {
160 if category.Parent != nil {
161 category.Parent.Categories = db.ChildCategories(category.Parent.ID)
162 for i, c := range category.Parent.Categories {
163 if c.ID == category.ID && i > 0 {
164 category.Parent.Categories[i], category.Parent.Categories[i-1] = category.Parent.Categories[i-1], category.Parent.Categories[i]
165 break
166 }
167 }
168 for i, c := range category.Parent.Categories {
169 c.Sort = i
170 db.UpdateCategory(c)
171 s.refreshCategoryCache(db)
172 s.rebuildAll(db, false)
173 }
174 } else {
175 var rootCategories []*Category
176 for _, c := range data.Categories {
177 if c.Parent == nil {
178 rootCategories = append(rootCategories, c)
179 }
180 }
181 for i, c := range rootCategories {
182 if c.ID == category.ID && i > 0 {
183 rootCategories[i], rootCategories[i-1] = rootCategories[i-1], rootCategories[i]
184 break
185 }
186 }
187 for i, c := range rootCategories {
188 c.Sort = i
189 db.UpdateCategory(c)
190 s.refreshCategoryCache(db)
191 s.rebuildAll(db, false)
192 }
193 }
194 }
195 data.Redirect(w, r, "/sriracha/category/")
196 return
197 }
198 categoryDownID := PathInt(r, "/sriracha/category/down/")
199 if categoryDownID != 0 {
200 if s.forbidden(w, data, "category.update") {
201 return
202 }
203 category := db.CategoryByID(categoryDownID)
204 if category != nil {
205 if category.Parent != nil {
206 category.Parent.Categories = db.ChildCategories(category.Parent.ID)
207 for i, c := range category.Parent.Categories {
208 if c.ID == category.ID && i < len(category.Parent.Categories)-1 {
209 category.Parent.Categories[i], category.Parent.Categories[i+1] = category.Parent.Categories[i+1], category.Parent.Categories[i]
210 break
211 }
212 }
213 for i, c := range category.Parent.Categories {
214 c.Sort = i
215 db.UpdateCategory(c)
216 s.refreshCategoryCache(db)
217 s.rebuildAll(db, false)
218 }
219 } else {
220 var rootCategories []*Category
221 for _, c := range data.Categories {
222 if c.Parent == nil {
223 rootCategories = append(rootCategories, c)
224 }
225 }
226 for i, c := range rootCategories {
227 if c.ID == category.ID && i < len(rootCategories)-1 {
228 rootCategories[i], rootCategories[i+1] = rootCategories[i+1], rootCategories[i]
229 break
230 }
231 }
232 for i, c := range rootCategories {
233 c.Sort = i
234 db.UpdateCategory(c)
235 s.refreshCategoryCache(db)
236 s.rebuildAll(db, false)
237 }
238 }
239 }
240 data.Redirect(w, r, "/sriracha/category/")
241 return
242 }
243
244 boardCategoryID := PathInt(r, "/sriracha/category/board/")
245 if boardCategoryID > 0 {
246 if s.forbidden(w, data, "category.update") {
247 return
248 }
249 data.Manage.Category = db.CategoryByID(boardCategoryID)
250 data.Extra = "board"
251
252 if data.Manage.Category != nil && r.Method == http.MethodPost {
253 boardID := FormInt(r, "board")
254 board := db.BoardByID(boardID)
255 if board == nil {
256 data.ManageError("Invalid board.")
257 return
258 }
259
260 if !data.Manage.Category.HasBoard(board.ID) {
261 data.Manage.Category.Boards = append(data.Manage.Category.Boards, board)
262 db.UpdateCategory(data.Manage.Category)
263 s.refreshCategoryCache(db)
264 s.rebuildAll(db, false)
265 }
266
267 data.Redirect(w, r, "/sriracha/category/")
268 return
269 }
270 return
271 }
272
273 categoryID, err := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/sriracha/category/"))
274 if err == nil && categoryID > 0 {
275 if s.forbidden(w, data, "category.update") {
276 return
277 }
278 data.Manage.Category = db.CategoryByID(categoryID)
279
280 if data.Manage.Category != nil && r.Method == http.MethodPost {
281 oldCategory := *data.Manage.Category
282 s.loadCategoryForm(db, r, data.Manage.Category)
283
284 db.UpdateCategory(data.Manage.Category)
285
286 var haveRoot bool
287 for _, c := range db.AllCategories() {
288 if c.Parent == nil {
289 haveRoot = true
290 break
291 }
292 }
293 if !haveRoot {
294 db.RollBack()
295 data.ManageError("Failed to delete category: At least one category without any parent categories must exist.")
296 return
297 }
298
299 s.refreshCategoryCache(db)
300 s.rebuildAll(db, false)
301
302 changes := printChanges(oldCategory, *data.Manage.Category)
303 s.log(db, data.Account, nil, fmt.Sprintf("Updated >>/category/%d", data.Manage.Category.ID), changes)
304
305 data.Redirect(w, r, "/sriracha/category/")
306 return
307 }
308 return
309 }
310
311 if r.Method == http.MethodPost {
312 if s.forbidden(w, data, "category.add") {
313 return
314 }
315 c := &Category{}
316 s.loadCategoryForm(db, r, c)
317
318 var cSort int
319 if c.Parent != nil {
320 for _, cat := range c.Categories {
321 if cat.Sort > cSort {
322 cSort = cat.Sort
323 }
324 }
325 } else {
326 for _, cat := range data.Categories {
327 if cat.Parent == nil && cat.Sort > cSort {
328 cSort = cat.Sort
329 }
330 }
331 }
332 c.Sort = cSort + 1
333
334 db.AddCategory(c)
335
336 s.refreshCategoryCache(db)
337 s.rebuildAll(db, false)
338
339 s.log(db, data.Account, nil, fmt.Sprintf("Added >>/category/%d", c.ID), "")
340
341 data.Redirect(w, r, "/sriracha/category/")
342 return
343 }
344 }
345
View as plain text