mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-09-09 22:16:23 -07:00
212 lines
4.7 KiB
Go
212 lines
4.7 KiB
Go
package gctemplates
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"html"
|
|
"html/template"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"maps"
|
|
|
|
"github.com/gochan-org/gochan/pkg/config"
|
|
"github.com/gochan-org/gochan/pkg/gcutil"
|
|
)
|
|
|
|
const (
|
|
dateTimeFormat = "2006-01-02T15:04:05Z"
|
|
maxFilename = 10
|
|
)
|
|
|
|
var (
|
|
ErrInvalidKey = errors.New("template map expects string keys")
|
|
ErrInvalidMap = errors.New("invalid template map call")
|
|
)
|
|
|
|
var funcMap = template.FuncMap{
|
|
// Arithmetic functions
|
|
"add": func(a, b int) int {
|
|
return a + b
|
|
},
|
|
"subtract": func(a, b int) int {
|
|
return a - b
|
|
},
|
|
|
|
// Pointer checking/dereferencing functions
|
|
"isNil": func(i any) bool {
|
|
return i == nil
|
|
},
|
|
"dereference": func(a *int) int {
|
|
if a == nil {
|
|
return 0
|
|
}
|
|
return *a
|
|
},
|
|
|
|
// Array functions
|
|
"getSlice": func(arr []any, start, end int) []any {
|
|
if start < 0 {
|
|
start = 0
|
|
}
|
|
if end > len(arr) {
|
|
return arr[start:]
|
|
}
|
|
return arr[start:end]
|
|
},
|
|
|
|
// String functions
|
|
"intToString": strconv.Itoa,
|
|
"escapeString": html.EscapeString,
|
|
"formatFilesize": func(sizeInt int) string {
|
|
size := float32(sizeInt)
|
|
if size < 1000 {
|
|
return fmt.Sprintf("%d B", sizeInt)
|
|
} else if size <= 100000 {
|
|
return fmt.Sprintf("%0.1f KB", size/1024)
|
|
} else if size <= 100000000 {
|
|
return fmt.Sprintf("%0.2f MB", size/1024/1024)
|
|
}
|
|
return fmt.Sprintf("%0.2f GB", size/1024/1024/1024)
|
|
},
|
|
"formatTimestamp": func(t time.Time) string {
|
|
return t.UTC().Format(config.GetBoardConfig("").DateTimeFormat)
|
|
},
|
|
"formatTimestampAttribute": func(t time.Time) string {
|
|
return t.UTC().Format(dateTimeFormat)
|
|
},
|
|
"stringAppend": func(strings ...string) string {
|
|
var appended string
|
|
for _, str := range strings {
|
|
appended += str
|
|
}
|
|
return appended
|
|
},
|
|
"truncateFilename": func(filename string) string {
|
|
if len(filename) <= maxFilename {
|
|
return filename
|
|
}
|
|
arr := strings.Split(filename, ".")
|
|
if len(arr) == 1 {
|
|
return arr[0][:maxFilename]
|
|
}
|
|
base := strings.Join(arr[:len(arr)-1], ".")
|
|
if len(base) >= maxFilename {
|
|
base = base[:maxFilename]
|
|
}
|
|
ext := arr[len(arr)-1:][0]
|
|
return base + "." + ext
|
|
},
|
|
"truncateMessage": func(msg string, limit int, maxLines int) string {
|
|
var truncated bool
|
|
split := strings.Split(msg, "\n")
|
|
|
|
if len(split) > maxLines {
|
|
split = split[:maxLines]
|
|
msg = strings.Join(split, "\n")
|
|
truncated = true
|
|
}
|
|
|
|
if len(msg) < limit {
|
|
if truncated {
|
|
msg = msg + "..."
|
|
}
|
|
return msg
|
|
}
|
|
truncated = len(msg) > limit
|
|
msg = strings.TrimSpace(msg[:limit])
|
|
|
|
if truncated && msg != "" {
|
|
msg = msg + "..."
|
|
}
|
|
return msg
|
|
},
|
|
"truncateHTMLMessage": truncateHTML,
|
|
"stripHTML": func(htmlStr template.HTML) string {
|
|
return gcutil.StripHTML(string(htmlStr))
|
|
},
|
|
"truncateString": func(msg string, limit int, ellipsis bool) string {
|
|
if len(msg) > limit {
|
|
if ellipsis {
|
|
return msg[:limit] + "..."
|
|
}
|
|
return msg[:limit]
|
|
}
|
|
return msg
|
|
},
|
|
"map": func(values ...any) (map[string]any, error) {
|
|
if len(values)%2 != 0 {
|
|
return nil, ErrInvalidMap
|
|
}
|
|
dict := make(map[string]any)
|
|
for k := 0; k < len(values); k += 2 {
|
|
key, ok := values[k].(string)
|
|
if !ok {
|
|
return nil, ErrInvalidKey
|
|
}
|
|
dict[key] = values[k+1]
|
|
}
|
|
return dict, nil
|
|
},
|
|
"until": func(t time.Time) string {
|
|
return time.Until(t).String()
|
|
},
|
|
|
|
// Imageboard functions
|
|
"customFlagsEnabled": func(board string) bool {
|
|
return config.GetBoardConfig(board).CustomFlags != nil
|
|
},
|
|
"webPath": config.WebPath,
|
|
"webPathDir": func(part ...string) string {
|
|
dir := config.WebPath(part...)
|
|
if dir == "" {
|
|
dir = "/"
|
|
} else if !strings.HasSuffix(dir, "/") {
|
|
dir += "/"
|
|
}
|
|
return dir
|
|
},
|
|
|
|
// Embed functions
|
|
"embedURL": func(filename, originalFilename string, boardDir string) (string, error) {
|
|
if strings.HasPrefix(filename, "embed:") {
|
|
boardCfg := config.GetBoardConfig(boardDir)
|
|
tmpl, err := boardCfg.GetLinkTemplate(filename[6:])
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
var buf bytes.Buffer
|
|
err = tmpl.Execute(&buf, config.EmbedTemplateData{
|
|
MediaID: originalFilename,
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
return "", errors.New("not an embed")
|
|
},
|
|
|
|
// Template convenience functions
|
|
"makeLoop": func(n int, offset int) []int {
|
|
loopArr := make([]int, n)
|
|
for i := range loopArr {
|
|
loopArr[i] = i + offset
|
|
}
|
|
return loopArr
|
|
},
|
|
"isStyleDefault": func(style string) bool {
|
|
return style == config.GetBoardConfig("").DefaultStyle
|
|
},
|
|
"version": func() string {
|
|
return config.GochanVersion
|
|
},
|
|
}
|
|
|
|
// AddTemplateFuncs adds the functions in the given FuncMap (map[string]any, with "any" expected to be a function)
|
|
// to the map of functions available to templates
|
|
func AddTemplateFuncs(funcs template.FuncMap) {
|
|
maps.Copy(funcMap, funcs)
|
|
}
|