include-general.go (3683B)
1 package main 2 3 import ( 4 "log" 5 "net/http" 6 "net/url" 7 "strings" 8 ) 9 10 type TagDisplay struct { 11 Value string 12 Count int 13 } 14 15 type ListData struct { 16 Tagged []File 17 Untagged []File 18 Breadcrumbs []Breadcrumb 19 } 20 21 type PageData struct { 22 Title string 23 Data interface{} 24 Query string 25 IP string 26 Port string 27 Files []File 28 Tags map[string][]TagDisplay 29 Properties map[string][]PropertyDisplay 30 Breadcrumbs []Breadcrumb 31 Pagination *Pagination 32 GallerySize string 33 UntaggedCount int 34 } 35 36 type File struct { 37 ID int 38 Filename string 39 EscapedFilename string 40 Path string 41 Description string 42 Tags map[string][]string 43 } 44 45 func sanitizeFilename(filename string) string { 46 if filename == "" { 47 return "file" 48 } 49 filename = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(filename, "/", "_"), "\\", "_"), "..", "_") 50 if filename == "" { 51 return "file" 52 } 53 return filename 54 } 55 56 func renderError(w http.ResponseWriter, message string, statusCode int) { 57 http.Error(w, message, statusCode) 58 } 59 60 func renderTemplate(w http.ResponseWriter, tmplName string, data PageData) { 61 if err := tmpl.ExecuteTemplate(w, tmplName, data); err != nil { 62 log.Printf("Error: renderTemplate: failed to execute template %s: %v", tmplName, err) 63 renderError(w, "Template rendering failed", http.StatusInternalServerError) 64 } 65 } 66 67 func redirectWithWarning(w http.ResponseWriter, r *http.Request, baseURL, warningMsg string) { 68 redirectURL := baseURL 69 if warningMsg != "" { 70 redirectURL += "?warning=" + url.QueryEscape(warningMsg) 71 } 72 http.Redirect(w, r, redirectURL, http.StatusSeeOther) 73 } 74 75 func errorString(err error) string { 76 if err != nil { 77 return err.Error() 78 } 79 return "" 80 } 81 82 func successString(err error, msg string) string { 83 if err == nil { 84 return msg 85 } 86 return "" 87 } 88 89 func buildPageData(title string, data interface{}) PageData { 90 tagMap, err := getTagData() 91 if err != nil { 92 log.Printf("Warning: buildPageData: failed to load tag data for page %q: %v", title, err) 93 } 94 propMap, err := getPropertyNav() 95 if err != nil { 96 log.Printf("Warning: buildPageData: failed to load property nav for page %q: %v", title, err) 97 } 98 return PageData{ 99 Title: title, 100 Data: data, 101 Tags: tagMap, 102 Properties: propMap, 103 GallerySize: config.GallerySize, 104 UntaggedCount: getUntaggedCount(), 105 } 106 } 107 108 func getTagData() (map[string][]TagDisplay, error) { 109 rows, err := db.Query(` 110 SELECT c.name, t.value, COUNT(ft.file_id) 111 FROM tags t 112 JOIN categories c ON c.id = t.category_id 113 LEFT JOIN file_tags ft ON ft.tag_id = t.id 114 GROUP BY t.id 115 HAVING COUNT(ft.file_id) > 0 116 ORDER BY c.name, t.value`) 117 if err != nil { 118 return nil, err 119 } 120 defer rows.Close() 121 122 tagMap := make(map[string][]TagDisplay) 123 for rows.Next() { 124 var cat, val string 125 var count int 126 if err := rows.Scan(&cat, &val, &count); err != nil { 127 log.Printf("Warning: getTagData: failed to scan row: %v", err) 128 continue 129 } 130 tagMap[cat] = append(tagMap[cat], TagDisplay{Value: val, Count: count}) 131 } 132 if err := rows.Err(); err != nil { 133 return tagMap, err 134 } 135 return tagMap, nil 136 } 137 138 func tagsHandler(w http.ResponseWriter, r *http.Request) { 139 pageData := buildPageData("All Tags", nil) 140 pageData.Data = pageData.Tags 141 renderTemplate(w, "tags.html", pageData) 142 } 143 144 func getUntaggedCount() int { 145 var count int 146 147 err := db.QueryRow(` 148 SELECT COUNT(*) 149 FROM files f 150 LEFT JOIN file_tags ft ON ft.file_id = f.id 151 WHERE ft.file_id IS NULL 152 `).Scan(&count) 153 154 if err != nil { 155 log.Printf("Warning: getUntaggedCount: %v", err) 156 return 0 157 } 158 return count 159 }