Commit ed031b3530
Unsigned
Layout: unified · split
app/router.go +2 −2
| @@ -52,7 +52,7 @@ func Router() { | ||
| 52 | 52 | // пути |
| 53 | 53 | switch e { |
| 54 | 54 | default: |
| 55 | skunky.httperr(404) | |
| 55 | skunky.ReturnHTTPError(404) | |
| 56 | 56 | case "/", "": |
| 57 | 57 | open_n_send("html/index.htm") |
| 58 | 58 | case "post": |
| @@ -62,7 +62,7 @@ func Router() { | ||
| 62 | 62 | skunky.Search() |
| 63 | 63 | case "dd": |
| 64 | 64 | skunky.DD() |
| 65 | case "group": | |
| 65 | case "group_user": | |
| 66 | 66 | skunky.GRUser() |
| 67 | 67 | |
| 68 | 68 | case "media": |
app/util.go +198
| @@ -2,11 +2,38 @@ package app | ||
| 2 | 2 | |
| 3 | 3 | import ( |
| 4 | 4 | "encoding/json" |
| 5 | "net/http" | |
| 6 | "strconv" | |
| 5 | 7 | "strings" |
| 8 | "text/template" | |
| 6 | 9 | |
| 7 | 10 | "git.macaw.me/skunky/devianter" |
| 8 | 11 | ) |
| 9 | 12 | |
| 13 | // парсинг темплейтов | |
| 14 | func (s skunkyart) ExecuteTemplate(file string, data any) { | |
| 15 | var buf strings.Builder | |
| 16 | tmp, e := template.ParseFiles(file) | |
| 17 | err(e) | |
| 18 | err(tmp.Execute(&buf, &data)) | |
| 19 | wr(s.Writer, buf.String()) | |
| 20 | } | |
| 21 | ||
| 22 | func (s skunkyart) ReturnHTTPError(status int) { | |
| 23 | s.Writer.WriteHeader(status) | |
| 24 | ||
| 25 | // пострйока с помощью strings.Builder, потому что такой метод быстрее обычного сложения | |
| 26 | var msg strings.Builder | |
| 27 | msg.WriteString(`<html><link rel="stylesheet" href="/gui/css/skunky.css" />`) | |
| 28 | msg.WriteString("<h1>") | |
| 29 | msg.WriteString(strconv.Itoa(status)) | |
| 30 | msg.WriteString(" - ") | |
| 31 | msg.WriteString(http.StatusText(status)) | |
| 32 | msg.WriteString("</h1></html>") | |
| 33 | ||
| 34 | wr(s.Writer, msg.String()) | |
| 35 | } | |
| 36 | ||
| 10 | 37 | type text struct { |
| 11 | 38 | TXT string |
| 12 | 39 | from int |
| @@ -87,3 +114,174 @@ func ParseDescription(dscr devianter.Text) string { | ||
| 87 | 114 | |
| 88 | 115 | return parseddescription.String() |
| 89 | 116 | } |
| 117 | ||
| 118 | // навигация по страницам | |
| 119 | type dlist struct { | |
| 120 | Pages int | |
| 121 | More bool | |
| 122 | } | |
| 123 | ||
| 124 | // FIXME: на некоротрых артах первая страница может вызывать полное отсутствие панели навигации. | |
| 125 | func (s skunkyart) NavBase(c dlist) string { | |
| 126 | // TODO: сделать понятнее | |
| 127 | // навигация по страницам | |
| 128 | var list strings.Builder | |
| 129 | list.WriteString("<br>") | |
| 130 | p := s.Page | |
| 131 | ||
| 132 | // функция для генерации ссылок | |
| 133 | prevrev := func(msg string, page int, onpage bool) { | |
| 134 | if !onpage { | |
| 135 | list.WriteString(`<a href="?p=`) | |
| 136 | list.WriteString(strconv.Itoa(page)) | |
| 137 | if s.Type != 0 { | |
| 138 | list.WriteString("&type=") | |
| 139 | list.WriteRune(s.Type) | |
| 140 | } | |
| 141 | if s.Query != "" { | |
| 142 | list.WriteString("&q=") | |
| 143 | list.WriteString(s.Query) | |
| 144 | } | |
| 145 | list.WriteString(`">`) | |
| 146 | list.WriteString(msg) | |
| 147 | list.WriteString("</a> ") | |
| 148 | } else { | |
| 149 | list.WriteString(strconv.Itoa(page)) | |
| 150 | list.WriteString(" ") | |
| 151 | } | |
| 152 | } | |
| 153 | ||
| 154 | // вперёд-назад | |
| 155 | if p > 1 { | |
| 156 | prevrev("<= Prev |", p-1, false) | |
| 157 | } else { | |
| 158 | p = 1 | |
| 159 | } | |
| 160 | ||
| 161 | if c.Pages > 0 { | |
| 162 | // назад | |
| 163 | for x := p - 6; x < p && x > 0; x++ { | |
| 164 | prevrev(strconv.Itoa(x), x, false) | |
| 165 | } | |
| 166 | ||
| 167 | // вперёд | |
| 168 | for x := p; x <= p+6; x++ { | |
| 169 | if x == p { | |
| 170 | prevrev("", x, true) | |
| 171 | x++ | |
| 172 | } | |
| 173 | ||
| 174 | if x > p { | |
| 175 | prevrev(strconv.Itoa(x), x, false) | |
| 176 | } | |
| 177 | } | |
| 178 | } | |
| 179 | ||
| 180 | // вперёд-назад | |
| 181 | if c.More { | |
| 182 | prevrev("| Next =>", p+1, false) | |
| 183 | } | |
| 184 | ||
| 185 | return list.String() | |
| 186 | } | |
| 187 | ||
| 188 | func (s skunkyart) DeviationList(devs []devianter.Deviation, content ...dlist) string { | |
| 189 | var list strings.Builder | |
| 190 | list.WriteString(`<div class="content">`) | |
| 191 | for _, data := range devs { | |
| 192 | url := devianter.UrlFromMedia(data.Media) | |
| 193 | ||
| 194 | list.WriteString(`<a title="open/download" href="`) | |
| 195 | list.WriteString(url) | |
| 196 | list.WriteString(`"><div class="block"><img src="`) | |
| 197 | list.WriteString(url) | |
| 198 | list.WriteString(`" width="15%"></a><br><a href="`) | |
| 199 | list.WriteString("/post/") | |
| 200 | list.WriteString(data.Author.Username) | |
| 201 | list.WriteString("/") | |
| 202 | list.WriteString(data.Url[27:][strings.Index(data.Url[27:], "/art/")+5:]) | |
| 203 | list.WriteString(`">`) | |
| 204 | list.WriteString(data.Author.Username) | |
| 205 | list.WriteString(" - ") | |
| 206 | list.WriteString(data.Title) | |
| 207 | ||
| 208 | // шильдики нсфв, аи и ежедневного поста | |
| 209 | if data.NSFW { | |
| 210 | list.WriteString(` [<span class="nsfw">NSFW</span>]`) | |
| 211 | } | |
| 212 | if data.AI { | |
| 213 | list.WriteString(" [🤖]") | |
| 214 | } | |
| 215 | if data.DD { | |
| 216 | list.WriteString(` [<span class="dd">DD</span>]`) | |
| 217 | } | |
| 218 | ||
| 219 | list.WriteString("</a></div>") | |
| 220 | } | |
| 221 | list.WriteString("</div>") | |
| 222 | list.WriteString(s.NavBase(content[0])) | |
| 223 | ||
| 224 | return list.String() | |
| 225 | } | |
| 226 | ||
| 227 | // FIXME: первый комментарий не отображается. | |
| 228 | func (s skunkyart) ParseComments(c devianter.Comments) string { | |
| 229 | var cmmts strings.Builder | |
| 230 | replied := make(map[int]string) | |
| 231 | ||
| 232 | cmmts.WriteString("<details><summary>Comments: <b>") | |
| 233 | cmmts.WriteString(strconv.Itoa(c.Total)) | |
| 234 | cmmts.WriteString("</b></summary>") | |
| 235 | for _, x := range c.Thread { | |
| 236 | replied[x.ID] = x.User.Username | |
| 237 | cmmts.WriteString(`<div class="msg`) | |
| 238 | if x.Parent > 0 { | |
| 239 | cmmts.WriteString(` reply`) | |
| 240 | } | |
| 241 | cmmts.WriteString(`"><p id="`) | |
| 242 | cmmts.WriteString(strconv.Itoa(x.ID)) | |
| 243 | cmmts.WriteString(`"><img src="/media/`) | |
| 244 | cmmts.WriteString(x.User.Username) | |
| 245 | cmmts.WriteString(`?type=a" width="30px" height="30px"><a href="/group_user?type=about&q=`) | |
| 246 | cmmts.WriteString(x.User.Username) | |
| 247 | cmmts.WriteString(`"><b`) | |
| 248 | cmmts.WriteString(` class="`) | |
| 249 | if x.User.Banned { | |
| 250 | cmmts.WriteString(`banned`) | |
| 251 | } | |
| 252 | if x.Author { | |
| 253 | cmmts.WriteString(`author`) | |
| 254 | } | |
| 255 | cmmts.WriteString(`">`) | |
| 256 | cmmts.WriteString(x.User.Username) | |
| 257 | cmmts.WriteString("</b></a> ") | |
| 258 | ||
| 259 | if x.Parent > 0 { | |
| 260 | cmmts.WriteString(` In reply to <a href="#`) | |
| 261 | cmmts.WriteString(strconv.Itoa(x.Parent)) | |
| 262 | cmmts.WriteString(`">`) | |
| 263 | if replied[x.Parent] == "" { | |
| 264 | cmmts.WriteString("???") | |
| 265 | } else { | |
| 266 | cmmts.WriteString(replied[x.Parent]) | |
| 267 | } | |
| 268 | cmmts.WriteString("</a>") | |
| 269 | } | |
| 270 | cmmts.WriteString(" [") | |
| 271 | cmmts.WriteString(x.Posted.UTC().String()) | |
| 272 | cmmts.WriteString("]<p>") | |
| 273 | ||
| 274 | cmmts.WriteString(x.Comment) | |
| 275 | cmmts.WriteString("<p>👍: ") | |
| 276 | cmmts.WriteString(strconv.Itoa(x.Likes)) | |
| 277 | cmmts.WriteString(" ⏩: ") | |
| 278 | cmmts.WriteString(strconv.Itoa(x.Replies)) | |
| 279 | cmmts.WriteString("</p></div>\n") | |
| 280 | } | |
| 281 | cmmts.WriteString(s.NavBase(dlist{ | |
| 282 | Pages: 0, | |
| 283 | More: c.HasMore, | |
| 284 | })) | |
| 285 | cmmts.WriteString("</details>") | |
| 286 | return cmmts.String() | |
| 287 | } | |
app/wraper.go +103 −265
| @@ -1,14 +1,13 @@ | ||
| 1 | 1 | package app |
| 2 | 2 | |
| 3 | 3 | import ( |
| 4 | "bytes" | |
| 4 | "fmt" | |
| 5 | 5 | "io" |
| 6 | 6 | "net/http" |
| 7 | 7 | "net/url" |
| 8 | 8 | "regexp" |
| 9 | 9 | "strconv" |
| 10 | 10 | "strings" |
| 11 | "text/template" | |
| 12 | 11 | "time" |
| 13 | 12 | |
| 14 | 13 | "git.macaw.me/skunky/devianter" |
| @@ -17,206 +16,108 @@ import ( | ||
| 17 | 16 | var wr = io.WriteString |
| 18 | 17 | |
| 19 | 18 | type skunkyart struct { |
| 20 | Writer http.ResponseWriter | |
| 21 | Args url.Values | |
| 22 | Type rune | |
| 23 | Query string | |
| 24 | Page int | |
| 25 | } | |
| 26 | ||
| 27 | // парсинг темплейтов | |
| 28 | func (s skunkyart) exe(file string, data any) { | |
| 29 | var buf bytes.Buffer | |
| 30 | tmp, e := template.ParseFiles(file) | |
| 31 | err(e) | |
| 32 | tmp.Execute(&buf, &data) | |
| 33 | wr(s.Writer, buf.String()) | |
| 34 | } | |
| 35 | ||
| 36 | func (s skunkyart) httperr(status int) { | |
| 37 | s.Writer.WriteHeader(status) | |
| 38 | ||
| 39 | // пострйока с помощью strings.Builder, потому что такой метод быстрее обычного сложения | |
| 40 | var msg strings.Builder | |
| 41 | msg.WriteString(`<html><link rel="stylesheet" href="/gui/css/skunky.css" />`) | |
| 42 | msg.WriteString("<h1>") | |
| 43 | msg.WriteString(strconv.Itoa(status)) | |
| 44 | msg.WriteString(" - ") | |
| 45 | msg.WriteString(http.StatusText(status)) | |
| 46 | msg.WriteString("</h1></html>") | |
| 47 | ||
| 48 | wr(s.Writer, msg.String()) | |
| 49 | } | |
| 50 | ||
| 51 | // навигация по страницам | |
| 52 | type dlist struct { | |
| 53 | Pages int | |
| 54 | More bool | |
| 55 | } | |
| 56 | ||
| 57 | // FIXME: на некоротрых артах первая страница может вызывать полное отсутствие панели навигации. | |
| 58 | func (s skunkyart) NavBase(c dlist) string { | |
| 59 | // TODO: сделать понятнее | |
| 60 | // навигация по страницам | |
| 61 | var list strings.Builder | |
| 62 | list.WriteString("<br>") | |
| 63 | p := s.Page | |
| 64 | ||
| 65 | // функция для генерации ссылок | |
| 66 | prevrev := func(msg string, page int, onpage bool) { | |
| 67 | if !onpage { | |
| 68 | list.WriteString(`<a href="?p=`) | |
| 69 | list.WriteString(strconv.Itoa(page)) | |
| 70 | if s.Type != 0 { | |
| 71 | list.WriteString("&type=") | |
| 72 | list.WriteRune(s.Type) | |
| 73 | } | |
| 74 | if s.Query != "" { | |
| 75 | list.WriteString("&q=") | |
| 76 | list.WriteString(s.Query) | |
| 77 | } | |
| 78 | list.WriteString(`">`) | |
| 79 | list.WriteString(msg) | |
| 80 | list.WriteString("</a> ") | |
| 81 | } else { | |
| 82 | list.WriteString(strconv.Itoa(page)) | |
| 83 | list.WriteString(" ") | |
| 84 | } | |
| 85 | } | |
| 86 | ||
| 87 | // вперёд-назад | |
| 88 | if p > 1 { | |
| 89 | prevrev("<= Prev |", p-1, false) | |
| 90 | } else { | |
| 91 | p = 1 | |
| 92 | } | |
| 93 | ||
| 94 | if c.Pages > 0 { | |
| 95 | // назад | |
| 96 | for x := p - 6; x < p && x > 0; x++ { | |
| 97 | prevrev(strconv.Itoa(x), x, false) | |
| 98 | } | |
| 99 | ||
| 100 | // вперёд | |
| 101 | for x := p; x <= p+6; x++ { | |
| 102 | if x == p { | |
| 103 | prevrev("", x, true) | |
| 104 | x++ | |
| 19 | Writer http.ResponseWriter | |
| 20 | Args url.Values | |
| 21 | Type rune | |
| 22 | Query string | |
| 23 | Page int | |
| 24 | Templates struct { | |
| 25 | GroupUser struct { | |
| 26 | GR devianter.GRuser | |
| 27 | CreationDate string | |
| 28 | ||
| 29 | About struct { | |
| 30 | A devianter.About | |
| 31 | ||
| 32 | DescriptionFormatted string | |
| 33 | Interests, Social string | |
| 34 | Comments string | |
| 35 | BG string | |
| 36 | BGMeta devianter.Deviation | |
| 105 | 37 | } |
| 106 | 38 | |
| 107 | if x > p { | |
| 108 | prevrev(strconv.Itoa(x), x, false) | |
| 39 | Gallery struct { | |
| 40 | Pages int | |
| 41 | List string | |
| 109 | 42 | } |
| 110 | 43 | } |
| 44 | Search struct { | |
| 45 | Content devianter.Search | |
| 46 | List string | |
| 47 | } | |
| 111 | 48 | } |
| 112 | ||
| 113 | // вперёд-назад | |
| 114 | if c.More { | |
| 115 | prevrev("| Next =>", p+1, false) | |
| 116 | } | |
| 117 | ||
| 118 | return list.String() | |
| 119 | 49 | } |
| 120 | 50 | |
| 121 | 51 | func (s skunkyart) GRUser() { |
| 122 | var group struct { | |
| 123 | GR devianter.GRuser | |
| 124 | CreationDate string | |
| 125 | ||
| 126 | About struct { | |
| 127 | A devianter.About | |
| 128 | ||
| 129 | DescriptionFormatted string | |
| 130 | Interests string | |
| 131 | Social string | |
| 132 | BG devianter.Deviation | |
| 133 | } | |
| 134 | } | |
| 135 | ||
| 136 | 52 | if len(s.Query) < 1 { |
| 137 | s.httperr(400) | |
| 53 | s.ReturnHTTPError(400) | |
| 138 | 54 | return |
| 139 | 55 | } |
| 140 | 56 | |
| 141 | 57 | var g devianter.Group |
| 142 | 58 | g.Name = s.Query |
| 143 | group.GR = g.GroupFunc() | |
| 144 | ||
| 145 | if g := group.GR; !g.Owner.Group { | |
| 146 | for _, x := range g.Gruser.Page.Modules { | |
| 147 | var about = group.About.A | |
| 148 | if x.ModuleData.About.RegDate != 0 { | |
| 149 | about = x.ModuleData.About | |
| 150 | } | |
| 151 | group.About.DescriptionFormatted = ParseDescription(about.Description) | |
| 59 | s.Templates.GroupUser.GR = g.GroupFunc() | |
| 60 | group := &s.Templates.GroupUser | |
| 152 | 61 | |
| 153 | for _, val := range x.ModuleData.About.Interests { | |
| 154 | var interest strings.Builder | |
| 155 | interest.WriteString(val.Label) | |
| 156 | interest.WriteString(": <b>") | |
| 157 | interest.WriteString(val.Value) | |
| 158 | interest.WriteString("</b><br>") | |
| 159 | group.About.Interests += interest.String() | |
| 160 | } | |
| 161 | ||
| 162 | for _, val := range x.ModuleData.About.SocialLinks { | |
| 163 | var social strings.Builder | |
| 164 | social.WriteString(`<a target="_blank" href="`) | |
| 165 | social.WriteString(val.Value) | |
| 166 | social.WriteString(`">`) | |
| 167 | social.WriteString(val.Value) | |
| 168 | social.WriteString("</a><br>") | |
| 169 | group.About.Social += social.String() | |
| 170 | } | |
| 171 | ||
| 172 | if rd := x.ModuleData.About.RegDate; rd != 0 { | |
| 173 | group.CreationDate = time.Unix(time.Now().Unix()-rd, 0).UTC().String() | |
| 62 | switch s.Type { | |
| 63 | case 'a': | |
| 64 | if g := group.GR; !g.Owner.Group { | |
| 65 | for _, x := range g.Gruser.Page.Modules { | |
| 66 | switch x.Name { | |
| 67 | case "about": | |
| 68 | group.About.A = x.ModuleData.About | |
| 69 | var about = group.About.A | |
| 70 | group.About.DescriptionFormatted = ParseDescription(about.Description) | |
| 71 | group.About.Comments = s.ParseComments(devianter.CommentsFunc( | |
| 72 | strconv.Itoa(group.GR.Gruser.ID), | |
| 73 | "", | |
| 74 | s.Page, | |
| 75 | 4, | |
| 76 | )) | |
| 77 | ||
| 78 | for _, val := range x.ModuleData.About.SocialLinks { | |
| 79 | var social strings.Builder | |
| 80 | social.WriteString(`<a target="_blank" href="`) | |
| 81 | social.WriteString(val.Value) | |
| 82 | social.WriteString(`">`) | |
| 83 | social.WriteString(val.Value) | |
| 84 | social.WriteString("</a><br>") | |
| 85 | group.About.Social += social.String() | |
| 86 | } | |
| 87 | ||
| 88 | for _, val := range x.ModuleData.About.Interests { | |
| 89 | var interest strings.Builder | |
| 90 | interest.WriteString(val.Label) | |
| 91 | interest.WriteString(": <b>") | |
| 92 | interest.WriteString(val.Value) | |
| 93 | interest.WriteString("</b><br>") | |
| 94 | group.About.Interests += interest.String() | |
| 95 | } | |
| 96 | ||
| 97 | if rd := x.ModuleData.About.RegDate; rd != 0 { | |
| 98 | group.CreationDate = time.Unix(time.Now().Unix()-rd, 0).UTC().String() | |
| 99 | } | |
| 100 | case "cover_deviation": | |
| 101 | group.About.BGMeta = x.ModuleData.CoverDeviation.Deviation | |
| 102 | group.About.BG = devianter.UrlFromMedia(group.About.BGMeta.Media) | |
| 103 | } | |
| 174 | 104 | } |
| 175 | } | |
| 176 | } else { | |
| 177 | ||
| 178 | } | |
| 179 | ||
| 180 | s.exe("html/gruser.htm", &group) | |
| 181 | } | |
| 182 | ||
| 183 | func (s skunkyart) DeviationList(devs []devianter.Deviation, content ...dlist) string { | |
| 184 | var list strings.Builder | |
| 185 | list.WriteString(`<div class="content">`) | |
| 186 | for _, data := range devs { | |
| 187 | url := devianter.UrlFromMedia(data.Media) | |
| 188 | ||
| 189 | list.WriteString(`<a title="open/download" href="`) | |
| 190 | list.WriteString(url) | |
| 191 | list.WriteString(`"><div class="block"><img src="`) | |
| 192 | list.WriteString(url) | |
| 193 | list.WriteString(`" width="15%"></a><br><a href="`) | |
| 194 | list.WriteString("/post/") | |
| 195 | list.WriteString(data.Author.Username) | |
| 196 | list.WriteString("/") | |
| 197 | list.WriteString(data.Url[27:][strings.Index(data.Url[27:], "/art/")+5:]) | |
| 198 | list.WriteString(`">`) | |
| 199 | list.WriteString(data.Author.Username) | |
| 200 | list.WriteString(" - ") | |
| 201 | list.WriteString(data.Title) | |
| 105 | } else { | |
| 202 | 106 | |
| 203 | // шильдики нсфв, аи и ежедневного поста | |
| 204 | if data.NSFW { | |
| 205 | list.WriteString(` [<span class="nsfw">NSFW</span>]`) | |
| 206 | 107 | } |
| 207 | if data.AI { | |
| 208 | list.WriteString(" [🤖]") | |
| 108 | case 'g': | |
| 109 | gallery := g.Gallery(s.Page) | |
| 110 | fmt.Println(gallery) | |
| 111 | for _, x := range gallery.Content.Gruser.Page.Modules { | |
| 112 | group.Gallery.List = s.DeviationList(x.ModuleData.Folder.Deviations, dlist{ | |
| 113 | Pages: x.ModuleData.Folder.Pages, | |
| 114 | }) | |
| 209 | 115 | } |
| 210 | if data.DD { | |
| 211 | list.WriteString(` [<span class="dd">DD</span>]`) | |
| 212 | } | |
| 213 | ||
| 214 | list.WriteString("</a></div>") | |
| 116 | default: | |
| 117 | s.ReturnHTTPError(400) | |
| 215 | 118 | } |
| 216 | list.WriteString("</div>") | |
| 217 | list.WriteString(s.NavBase(content[0])) | |
| 218 | 119 | |
| 219 | return list.String() | |
| 120 | s.ExecuteTemplate("html/gruser.htm", &s) | |
| 220 | 121 | } |
| 221 | 122 | |
| 222 | 123 | // посты |
| @@ -250,115 +151,52 @@ func (s skunkyart) Deviation(author, postname string) { | ||
| 250 | 151 | post.Tags += tag.String() |
| 251 | 152 | } |
| 252 | 153 | |
| 253 | // FIXME: первый комментарий не отображается. | |
| 254 | // генерация комментов | |
| 255 | var cmmts strings.Builder | |
| 256 | replied := make(map[int]string) | |
| 257 | c := devianter.CommentsFunc(id, post.Post.Comments.Cursor, s.Page, 1) | |
| 258 | ||
| 259 | cmmts.WriteString("<details><summary>Comments: <b>") | |
| 260 | cmmts.WriteString(strconv.Itoa(c.Total)) | |
| 261 | cmmts.WriteString("</b></summary>") | |
| 262 | for _, x := range c.Thread { | |
| 263 | replied[x.ID] = x.User.Username | |
| 264 | cmmts.WriteString(`<div class="msg`) | |
| 265 | if x.Parent > 0 { | |
| 266 | cmmts.WriteString(` reply`) | |
| 267 | } | |
| 268 | cmmts.WriteString(`"><p id="`) | |
| 269 | cmmts.WriteString(strconv.Itoa(x.ID)) | |
| 270 | cmmts.WriteString(`"><img src="/media/`) | |
| 271 | cmmts.WriteString(x.User.Username) | |
| 272 | cmmts.WriteString(`?type=a" width="30px" height="30px"><a href="/user/`) | |
| 273 | cmmts.WriteString(x.User.Username) | |
| 274 | cmmts.WriteString(`"><b`) | |
| 275 | cmmts.WriteString(` class="`) | |
| 276 | if x.User.Banned { | |
| 277 | cmmts.WriteString(`banned`) | |
| 278 | } | |
| 279 | if x.Author { | |
| 280 | cmmts.WriteString(`author`) | |
| 281 | } | |
| 282 | cmmts.WriteString(`">`) | |
| 283 | cmmts.WriteString(x.User.Username) | |
| 284 | cmmts.WriteString("</b></a> ") | |
| 154 | post.Comments = s.ParseComments(devianter.CommentsFunc(id, post.Post.Comments.Cursor, s.Page, 1)) | |
| 285 | 155 | |
| 286 | if x.Parent > 0 { | |
| 287 | cmmts.WriteString(` In reply to <a href="#`) | |
| 288 | cmmts.WriteString(strconv.Itoa(x.Parent)) | |
| 289 | cmmts.WriteString(`">`) | |
| 290 | if replied[x.Parent] == "" { | |
| 291 | cmmts.WriteString("???") | |
| 292 | } else { | |
| 293 | cmmts.WriteString(replied[x.Parent]) | |
| 294 | } | |
| 295 | cmmts.WriteString("</a>") | |
| 296 | } | |
| 297 | cmmts.WriteString(" [") | |
| 298 | cmmts.WriteString(x.Posted.UTC().String()) | |
| 299 | cmmts.WriteString("]<p>") | |
| 300 | ||
| 301 | cmmts.WriteString(x.Comment) | |
| 302 | cmmts.WriteString("<p>👍: ") | |
| 303 | cmmts.WriteString(strconv.Itoa(x.Likes)) | |
| 304 | cmmts.WriteString(" ⏩: ") | |
| 305 | cmmts.WriteString(strconv.Itoa(x.Replies)) | |
| 306 | cmmts.WriteString("</p></div>\n") | |
| 307 | } | |
| 308 | cmmts.WriteString(s.NavBase(dlist{ | |
| 309 | Pages: 0, | |
| 310 | More: c.HasMore, | |
| 311 | })) | |
| 312 | cmmts.WriteString("</details>") | |
| 313 | ||
| 314 | post.Comments = cmmts.String() | |
| 315 | ||
| 316 | s.exe("html/deviantion.htm", &post) | |
| 156 | s.ExecuteTemplate("html/deviantion.htm", &post) | |
| 317 | 157 | } else { |
| 318 | s.httperr(400) | |
| 158 | s.ReturnHTTPError(400) | |
| 319 | 159 | } |
| 320 | 160 | } |
| 321 | 161 | |
| 322 | 162 | func (s skunkyart) DD() { |
| 323 | 163 | dd := devianter.DailyDeviationsFunc(s.Page) |
| 324 | s.exe("html/list.htm", s.DeviationList(dd.Deviations, dlist{ | |
| 164 | s.ExecuteTemplate("html/list.htm", s.DeviationList(dd.Deviations, dlist{ | |
| 325 | 165 | Pages: 0, |
| 326 | 166 | More: dd.HasMore, |
| 327 | 167 | })) |
| 328 | 168 | } |
| 329 | 169 | |
| 330 | 170 | func (s skunkyart) Search() { |
| 331 | // тут всё и так понятно | |
| 171 | var e error | |
| 172 | ss := &s.Templates.Search | |
| 332 | 173 | switch s.Type { |
| 333 | case 'a', 't', 'g': | |
| 334 | var srch struct { | |
| 335 | Search devianter.Search | |
| 336 | List string | |
| 337 | } | |
| 338 | ||
| 339 | var e error | |
| 340 | srch.Search, e = devianter.SearchFunc(s.Query, s.Page, s.Type) | |
| 341 | err(e) | |
| 342 | srch.List = s.DeviationList(srch.Search.Results, dlist{ | |
| 343 | Pages: srch.Search.Pages, | |
| 344 | More: srch.Search.HasMore, | |
| 345 | }) | |
| 346 | ||
| 347 | s.exe("html/search.htm", &srch) | |
| 174 | case 'a', 't': | |
| 175 | ss.Content, e = devianter.SearchFunc(s.Query, s.Page, s.Type) | |
| 176 | case 'g': | |
| 177 | ss.Content, e = devianter.SearchFunc(s.Query, s.Page, s.Type, s.Args.Get("usr")) | |
| 348 | 178 | default: |
| 349 | s.httperr(400) | |
| 179 | s.ReturnHTTPError(400) | |
| 350 | 180 | } |
| 181 | err(e) | |
| 182 | ||
| 183 | ss.List = s.DeviationList(ss.Content.Results, dlist{ | |
| 184 | Pages: ss.Content.Pages, | |
| 185 | More: ss.Content.HasMore, | |
| 186 | }) | |
| 187 | ||
| 188 | s.ExecuteTemplate("html/search.htm", &s) | |
| 351 | 189 | } |
| 352 | 190 | |
| 353 | 191 | func (s skunkyart) Emojitar(name string) { |
| 354 | 192 | if name != "" && (s.Type == 'a' || s.Type == 'e') { |
| 355 | 193 | ae, e := devianter.AEmedia(name, s.Type) |
| 356 | 194 | if e != nil { |
| 357 | s.httperr(404) | |
| 195 | s.ReturnHTTPError(404) | |
| 358 | 196 | println(e.Error()) |
| 359 | 197 | } |
| 360 | 198 | wr(s.Writer, ae) |
| 361 | 199 | } else { |
| 362 | s.httperr(400) | |
| 200 | s.ReturnHTTPError(400) | |
| 363 | 201 | } |
| 364 | 202 | } |
css/skunky.css +8 −1
| @@ -83,4 +83,11 @@ form input, button, select { | ||
| 83 | 83 | .block p { |
| 84 | 84 | word-break: break-all; |
| 85 | 85 | } |
| 86 | ||
| 86 | .ubg { | |
| 87 | display: flex; | |
| 88 | flex-wrap: wrap; | |
| 89 | justify-content: center; | |
| 90 | } | |
| 91 | .ubg img { | |
| 92 | width: 20%; | |
| 93 | } | |
html/deviantion.htm +1 −1
| @@ -18,7 +18,7 @@ | ||
| 18 | 18 | </header> |
| 19 | 19 | <figure> |
| 20 | 20 | <img src="/media/{{.Post.Deviation.Author.Username}}?type=a" width="30px"> |
| 21 | <span><strong><a href="/user/{{.Post.Deviation.Author.Username}}">{{.Post.Deviation.Author.Username}}</a></strong> — {{if (.Post.Deviation.DD)}} | |
| 21 | <span><strong><a href="/group_user?type=about&q={{.Post.Deviation.Author.Username}}">{{.Post.Deviation.Author.Username}}</a></strong> — {{if (.Post.Deviation.DD)}} | |
| 22 | 22 | <span class="dd" title="Daily Deviation!"><b>{{.Post.Deviation.Title}}</b></span> |
| 23 | 23 | {{else}}{{.Post.Deviation.Title}}{{end}} |
| 24 | 24 | {{if (ne .Post.Deviation.License "none")}}<mark title="License">{{.Post.Deviation.License}}</mark>{{end}} {{if (.Post.Deviation.AI)}}[🤖]{{end}} |
html/gruser.htm +56 −25
| @@ -1,43 +1,74 @@ | ||
| 1 | 1 | <!DOCTYPE html> |
| 2 | 2 | <html> |
| 3 | 3 | <head> |
| 4 | <title>SkunkyArt | {{.GR.Owner.Username}}</title> | |
| 4 | <title>SkunkyArt | | |
| 5 | {{if eq .Type 'a'}} | |
| 6 | {{.Templates.GroupUser.GR.Owner.Username}} | |
| 7 | {{else}} | |
| 8 | gallery of {{.Templates.GroupUser.GR.Owner.Username}} | |
| 9 | {{end}} | |
| 10 | </title> | |
| 5 | 11 | <link rel="stylesheet" href="/gui/css/skunky.css"> |
| 6 | 12 | </head> |
| 7 | 13 | <main> |
| 8 | 14 | <header> |
| 9 | <h1><a href="/">HOME</a> | <a href="/dd">DD</a></h1> | |
| 10 | <form method="get" action="/search"> | |
| 11 | <input type="text" name="q" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false"> | |
| 15 | <h1><a href="/">HOME</a> | <a href="/dd">DD</a> | |
| 16 | | <a href="?q={{.Templates.GroupUser.GR.Owner.Username}}&type={{if eq .Type 'a'}}gallery">Gallery</a></h1>{{else}}about">About</a></h1> | |
| 17 | {{end}} | |
| 18 | <form method="get" action="/search"> | |
| 19 | <input type="gallery" name="q" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false"> | |
| 20 | <input type="hidden" name="usr" value="{{.Templates.GroupUser.GR.Owner.Username}}"> | |
| 12 | 21 | <select name="type"> |
| 13 | 22 | <option value="all">All</option> |
| 14 | 23 | <option value="tag">Tag</option> |
| 24 | <option value="gallery">Gallery</option> | |
| 15 | 25 | </select> |
| 16 | 26 | <button type="submit">Search!</button> |
| 17 | 27 | </form> |
| 18 | 28 | </header> |
| 19 | <img src="/media/{{.GR.Owner.Username}}?type=a" width="30px" height="30px"> <b>{{.GR.Owner.Username}}</b> | |
| 20 | {{if (eq .About.A.Gender "male")}} | |
| 21 | ♂️ | |
| 22 | {{end}} | |
| 23 | {{if (eq .About.A.Gender "female")}} | |
| 24 | ♀️ | |
| 25 | {{end}} | |
| 26 | [<span title="UID">{{.GR.Gruser.ID}}</span>] | |
| 27 | [<span title="Registration date">{{.CreationDate}}</span>] | |
| 28 | <i title="User's Tag">"{{.GR.Extra.Tag}}"</i> | |
| 29 | {{if eq .Type 'a'}} | |
| 30 | {{if ne .Templates.GroupUser.About.BG ""}} | |
| 31 | <a href="{{.Templates.GroupUser.About.BGMeta.Url}}" class="ubg"><img title="{{if ne .Templates.GroupUser.GR.Owner.Username .Templates.GroupUser.About.BGMeta.Author.Username}} | |
| 32 | {{.Templates.GroupUser.About.BGMeta.Author.Username}} - {{end}}{{.Templates.GroupUser.About.BGMeta.Title}}" src="{{.Templates.GroupUser.About.BG}}"></a> | |
| 33 | {{end}} | |
| 29 | 34 | |
| 30 | <h3 id="stats"><a href="#stats">#</a> Statistics</h3> | |
| 31 | <p>Favourites: <b>{{.GR.Extra.Stats.Favourites}}</b>; Deviations: <b>{{.GR.Extra.Stats.Deviations}}</b>; Watchers: <b>{{.GR.Extra.Stats.Watchers}}</b> | |
| 32 | <p>Watching: <b>{{.GR.Extra.Stats.Watching}}</b>; Pageviews: <b>{{.GR.Extra.Stats.Pageviews}}</b>; Comments Made: <b>{{.GR.Extra.Stats.CommentsMade}}</b>; Friends: <b>{{.GR.Extra.Stats.Friends}}</b></p> | |
| 33 | ||
| 34 | <h3 id="interests"><a href="#interests">#</a> Interests</h3> | |
| 35 | {{.About.Interests}} | |
| 35 | <img src="/media/{{.Templates.GroupUser.GR.Owner.Username}}?type=a" width="30px" height="30px"> <b>{{.Templates.GroupUser.GR.Owner.Username}}</b> | |
| 36 | {{if (eq .Templates.GroupUser.About.A.Gender "male")}} | |
| 37 | ♂️ | |
| 38 | {{end}} | |
| 39 | {{if (eq .Templates.GroupUser.About.A.Gender "female")}} | |
| 40 | ♀️ | |
| 41 | {{end}} | |
| 42 | [<span title="UID">{{.Templates.GroupUser.GR.Gruser.ID}}</span>] | |
| 43 | [<span title="Registration date">{{.Templates.GroupUser.CreationDate}}</span>] | |
| 44 | {{if ne .Templates.GroupUser.GR.Extra.Tag ""}} | |
| 45 | <i title="User's Tag">"{{.Templates.GroupUser.GR.Extra.Tag}}"</i>{{end}} (<b>{{.Templates.GroupUser.About.A.Country}}</b>) | |
| 36 | 46 | |
| 37 | <h3 id="social"><a href="#social">#</a> Social Links</h3> | |
| 38 | {{.About.Social}} | |
| 47 | <h3 id="stats"><a href="#stats">#</a> Statistics</h3> | |
| 48 | <p>Favourites: <b>{{.Templates.GroupUser.GR.Extra.Stats.Favourites}}</b>; Deviations: <b>{{.Templates.GroupUser.GR.Extra.Stats.Deviations}}</b>; Watchers: <b>{{.Templates.GroupUser.GR.Extra.Stats.Watchers}}</b> | |
| 49 | <p>Watching: <b>{{.Templates.GroupUser.GR.Extra.Stats.Watching}}</b>; Pageviews: <b>{{.Templates.GroupUser.GR.Extra.Stats.Pageviews}}</b>; Comments Made: <b>{{.Templates.GroupUser.GR.Extra.Stats.CommentsMade}}</b>; Friends: <b>{{.Templates.GroupUser.GR.Extra.Stats.Friends}}</b></p> | |
| 50 | ||
| 51 | {{if ne .Templates.GroupUser.About.Interests ""}} | |
| 52 | <h3 id="interests"><a href="#interests">#</a> Interests</h3> | |
| 53 | {{.Templates.GroupUser.About.Interests}} | |
| 54 | {{end}} | |
| 39 | 55 | |
| 40 | <h3 id="about"><a href="#about">#</a> About me</h3> | |
| 41 | {{.About.DescriptionFormatted}} | |
| 56 | {{if ne .Templates.GroupUser.About.Social ""}} | |
| 57 | <h3 id="social"><a href="#social">#</a> Social Links</h3> | |
| 58 | {{.Templates.GroupUser.About.Social}} | |
| 59 | {{end}} | |
| 60 | ||
| 61 | {{if ne .Templates.GroupUser.About.DescriptionFormatted ""}} | |
| 62 | <h3 id="about"><a href="#about">#</a> About me</h3> | |
| 63 | {{.Templates.GroupUser.About.DescriptionFormatted}} | |
| 64 | {{end}} | |
| 65 | {{if ne .Templates.GroupUser.About.Comments ""}} | |
| 66 | <br> | |
| 67 | <h3 id="comments"><a href="#comments">#</a> Comments</h3> | |
| 68 | {{.Templates.GroupUser.About.Comments}} | |
| 69 | {{end}} | |
| 70 | {{else}} | |
| 71 | {{.Templates.GroupUser.Gallery.List}} | |
| 72 | {{end}} | |
| 42 | 73 | </main> |
| 43 | </html> | |
| \ No newline at end of file | ||
| 74 | </html> | |
html/search.htm +5 −5
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | <!DOCTYPE html> |
| 2 | 2 | <html> |
| 3 | 3 | <head> |
| 4 | <title>SkunkyArt | Search</title> | |
| 4 | <title>SkunkyArt | Search "{{.Query}}"</title> | |
| 5 | 5 | <link rel="stylesheet" href="/gui/css/skunky.css"> |
| 6 | 6 | </head> |
| 7 | 7 | <main> |
| @@ -16,11 +16,11 @@ | ||
| 16 | 16 | <button type="submit">Search!</button> |
| 17 | 17 | </form> |
| 18 | 18 | </header> |
| 19 | {{if (ne .List "")}} | |
| 20 | {{if (ne .Search.Total 0)}} | |
| 21 | <h1>Total resuls: {{.Search.Total}}</h1> | |
| 19 | {{if ne .Templates.Search.List ""}} | |
| 20 | {{if ne .Templates.Search.Content.Total 0}} | |
| 21 | <h1>Total resuls: {{.Templates.Search.Content.Total}}</h1> | |
| 22 | 22 | {{end}} |
| 23 | {{.List}} | |
| 23 | {{.Templates.Search.List}} | |
| 24 | 24 | {{else}} |
| 25 | 25 | <p>No results :(</p> |
| 26 | 26 | {{end}} |