diff options
| -rw-r--r-- | README.md | 21 | ||||
| -rw-r--r-- | filters.go | 69 | ||||
| -rw-r--r-- | main.go | 65 |
3 files changed, 96 insertions, 59 deletions
| @@ -163,28 +163,43 @@ Payload { | |||
| 163 | - first (gets first N posts) | 163 | - first (gets first N posts) |
| 164 | - last (gets last N posts) | 164 | - last (gets last N posts) |
| 165 | - random (gets random N posts) | 165 | - random (gets random N posts) |
| 166 | - filterbytype (get just the posts with specific type) | ||
| 166 | 167 | ||
| 167 | ```html | 168 | ```html |
| 168 | <!-- First 10 pages --> | 169 | <!-- First 10 pages --> |
| 169 | {{ range first 10 .Pages }} | 170 | {{ range .Pages | first 10 }} |
| 170 | {{ if and (eq .Type "post") (not .Draft) }} | 171 | {{ if and (eq .Type "post") (not .Draft) }} |
| 171 | <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li> | 172 | <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li> |
| 172 | {{ end }} | 173 | {{ end }} |
| 173 | {{ end }} | 174 | {{ end }} |
| 174 | 175 | ||
| 175 | <!-- Last 10 pages --> | 176 | <!-- Last 10 pages --> |
| 176 | {{ range last 10 .Pages }} | 177 | {{ range .Pages | last 10 }} |
| 177 | {{ if and (eq .Type "post") (not .Draft) }} | 178 | {{ if and (eq .Type "post") (not .Draft) }} |
| 178 | <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li> | 179 | <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li> |
| 179 | {{ end }} | 180 | {{ end }} |
| 180 | {{ end }} | 181 | {{ end }} |
| 181 | 182 | ||
| 182 | <!-- Random 10 pages --> | 183 | <!-- Random 10 pages --> |
| 183 | {{ range random 10 .Pages }} | 184 | {{ range .Pages | random 10 }} |
| 184 | {{ if and (eq .Type "post") (not .Draft) }} | 185 | {{ if and (eq .Type "post") (not .Draft) }} |
| 185 | <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li> | 186 | <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li> |
| 186 | {{ end }} | 187 | {{ end }} |
| 187 | {{ end }} | 188 | {{ end }} |
| 189 | |||
| 190 | <!-- Filter by type --> | ||
| 191 | {{ range .Pages | filterbytype "post" }} | ||
| 192 | {{ if not .Draft }} | ||
| 193 | <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li> | ||
| 194 | {{ end }} | ||
| 195 | {{ end }} | ||
| 196 | |||
| 197 | <!-- Chain multiple together --> | ||
| 198 | {{ range .Pages | filterbytype "post" | random 20 | first 5 }} | ||
| 199 | {{ if not .Draft }} | ||
| 200 | <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li> | ||
| 201 | {{ end }} | ||
| 202 | {{ end }} | ||
| 188 | ``` | 203 | ``` |
| 189 | 204 | ||
| 190 | ## Additional material | 205 | ## Additional material |
diff --git a/filters.go b/filters.go new file mode 100644 index 0000000..bb57599 --- /dev/null +++ b/filters.go | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | package main | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "math/rand" | ||
| 5 | "reflect" | ||
| 6 | "time" | ||
| 7 | ) | ||
| 8 | |||
| 9 | // firstN returns the first n items of a slice. | ||
| 10 | func firstN(n int, items interface{}) interface{} { | ||
| 11 | v := reflect.ValueOf(items) | ||
| 12 | if v.Kind() != reflect.Slice { | ||
| 13 | panic("firstN: not a slice") | ||
| 14 | } | ||
| 15 | if v.Len() < n { | ||
| 16 | return items | ||
| 17 | } | ||
| 18 | return v.Slice(0, n).Interface() | ||
| 19 | } | ||
| 20 | |||
| 21 | // lastN returns the last n items of any slice. | ||
| 22 | func lastN(n int, items interface{}) interface{} { | ||
| 23 | v := reflect.ValueOf(items) | ||
| 24 | if v.Kind() != reflect.Slice { | ||
| 25 | panic("lastN: not a slice") | ||
| 26 | } | ||
| 27 | l := v.Len() | ||
| 28 | if l < n { | ||
| 29 | return items | ||
| 30 | } | ||
| 31 | return v.Slice(l-n, l).Interface() | ||
| 32 | } | ||
| 33 | |||
| 34 | // randomN returns n random items of any slice. | ||
| 35 | func randomN(n int, items interface{}) interface{} { | ||
| 36 | v := reflect.ValueOf(items) | ||
| 37 | if v.Kind() != reflect.Slice { | ||
| 38 | panic("randomN: not a slice") | ||
| 39 | } | ||
| 40 | l := v.Len() | ||
| 41 | if l < n { | ||
| 42 | return items | ||
| 43 | } | ||
| 44 | rand.Seed(time.Now().UnixNano()) | ||
| 45 | indices := rand.Perm(l)[:n] | ||
| 46 | result := reflect.MakeSlice(v.Type(), n, n) | ||
| 47 | for i, idx := range indices { | ||
| 48 | result.Index(i).Set(v.Index(idx)) | ||
| 49 | } | ||
| 50 | return result.Interface() | ||
| 51 | } | ||
| 52 | |||
| 53 | // filterByType filters pages by their type. | ||
| 54 | func filterByType(pageType string, pages interface{}) interface{} { | ||
| 55 | v := reflect.ValueOf(pages) | ||
| 56 | if v.Kind() != reflect.Slice { | ||
| 57 | panic("filterByType: not a slice") | ||
| 58 | } | ||
| 59 | |||
| 60 | var filtered []interface{} | ||
| 61 | for i := 0; i < v.Len(); i++ { | ||
| 62 | page := v.Index(i).Interface().(Page) | ||
| 63 | if page.Type == pageType { | ||
| 64 | filtered = append(filtered, page) | ||
| 65 | } | ||
| 66 | } | ||
| 67 | return filtered | ||
| 68 | } | ||
| 69 | |||
| @@ -5,12 +5,10 @@ import ( | |||
| 5 | "fmt" | 5 | "fmt" |
| 6 | "html/template" | 6 | "html/template" |
| 7 | "log" | 7 | "log" |
| 8 | "math/rand" | ||
| 9 | "net/http" | 8 | "net/http" |
| 10 | "os" | 9 | "os" |
| 11 | "path" | 10 | "path" |
| 12 | "path/filepath" | 11 | "path/filepath" |
| 13 | "reflect" | ||
| 14 | "sort" | 12 | "sort" |
| 15 | "strings" | 13 | "strings" |
| 16 | "time" | 14 | "time" |
| @@ -89,50 +87,6 @@ var EmbedTemplatePost string | |||
| 89 | //go:embed "files/index.xml" | 87 | //go:embed "files/index.xml" |
| 90 | var EmbedTemplateFeed string | 88 | var EmbedTemplateFeed string |
| 91 | 89 | ||
| 92 | // firstN returns the first n items of a slice. | ||
| 93 | func firstN(n int, items interface{}) interface{} { | ||
| 94 | v := reflect.ValueOf(items) | ||
| 95 | if v.Kind() != reflect.Slice { | ||
| 96 | panic("firstN: not a slice") | ||
| 97 | } | ||
| 98 | if v.Len() < n { | ||
| 99 | return items | ||
| 100 | } | ||
| 101 | return v.Slice(0, n).Interface() | ||
| 102 | } | ||
| 103 | |||
| 104 | // lastN returns the last n items of any slice. | ||
| 105 | func lastN(n int, items interface{}) interface{} { | ||
| 106 | v := reflect.ValueOf(items) | ||
| 107 | if v.Kind() != reflect.Slice { | ||
| 108 | panic("lastN: not a slice") | ||
| 109 | } | ||
| 110 | l := v.Len() | ||
| 111 | if l < n { | ||
| 112 | return items | ||
| 113 | } | ||
| 114 | return v.Slice(l-n, l).Interface() | ||
| 115 | } | ||
| 116 | |||
| 117 | // randomN returns n random items of any slice. | ||
| 118 | func randomN(n int, items interface{}) interface{} { | ||
| 119 | v := reflect.ValueOf(items) | ||
| 120 | if v.Kind() != reflect.Slice { | ||
| 121 | panic("randomN: not a slice") | ||
| 122 | } | ||
| 123 | l := v.Len() | ||
| 124 | if l < n { | ||
| 125 | return items | ||
| 126 | } | ||
| 127 | rand.Seed(time.Now().UnixNano()) | ||
| 128 | indices := rand.Perm(l)[:n] | ||
| 129 | result := reflect.MakeSlice(v.Type(), n, n) | ||
| 130 | for i, idx := range indices { | ||
| 131 | result.Index(i).Set(v.Index(idx)) | ||
| 132 | } | ||
| 133 | return result.Interface() | ||
| 134 | } | ||
| 135 | |||
| 136 | // Function to clean HTML tags using bluemonday. | 90 | // Function to clean HTML tags using bluemonday. |
| 137 | func cleanHTMLTags(htmlString string) string { | 91 | func cleanHTMLTags(htmlString string) string { |
| 138 | p := bluemonday.StrictPolicy() | 92 | p := bluemonday.StrictPolicy() |
| @@ -318,6 +272,13 @@ func buildProject(projectRoot string) { | |||
| 318 | return | 272 | return |
| 319 | } | 273 | } |
| 320 | 274 | ||
| 275 | filters := template.FuncMap{ | ||
| 276 | "first": firstN, | ||
| 277 | "last": lastN, | ||
| 278 | "random": randomN, | ||
| 279 | "filterbytype": filterByType, | ||
| 280 | } | ||
| 281 | |||
| 321 | // Generate HTML files for all pages. | 282 | // Generate HTML files for all pages. |
| 322 | for _, page := range pages { | 283 | for _, page := range pages { |
| 323 | outFilepath := path.Join(projectRoot, "public", page.Meta["url"].(string)) | 284 | outFilepath := path.Join(projectRoot, "public", page.Meta["url"].(string)) |
| @@ -330,11 +291,7 @@ func buildProject(projectRoot string) { | |||
| 330 | templates = append([]string{templatePathname}, templates...) | 291 | templates = append([]string{templatePathname}, templates...) |
| 331 | templates = append([]string{baseTemplatePathname}, templates...) | 292 | templates = append([]string{baseTemplatePathname}, templates...) |
| 332 | 293 | ||
| 333 | t, err := template.New("base.html").Funcs(template.FuncMap{ | 294 | t, err := template.New("base.html").Funcs(filters).ParseFiles(templates...) |
| 334 | "first": firstN, | ||
| 335 | "last": lastN, | ||
| 336 | "random": randomN, | ||
| 337 | }).ParseFiles(templates...) | ||
| 338 | if err != nil { | 295 | if err != nil { |
| 339 | panic(err) | 296 | panic(err) |
| 340 | } | 297 | } |
| @@ -385,11 +342,7 @@ func buildProject(projectRoot string) { | |||
| 385 | templates = append([]string{templatePathname}, templates...) | 342 | templates = append([]string{templatePathname}, templates...) |
| 386 | templates = append([]string{baseTemplatePathname}, templates...) | 343 | templates = append([]string{baseTemplatePathname}, templates...) |
| 387 | 344 | ||
| 388 | t, err := template.New("base.html").Funcs(template.FuncMap{ | 345 | t, err := template.New("base.html").Funcs(filters).ParseFiles(templates...) |
| 389 | "first": firstN, | ||
| 390 | "last": lastN, | ||
| 391 | "random": randomN, | ||
| 392 | }).ParseFiles(templates...) | ||
| 393 | if err != nil { | 346 | if err != nil { |
| 394 | panic(err) | 347 | panic(err) |
| 395 | } | 348 | } |
