1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-08-20 09:26:23 -07:00
gochan/pkg/gctemplates/funcs.go

299 lines
7.3 KiB
Go
Raw Normal View History

package gctemplates
2013-02-02 15:01:01 -08:00
import (
"fmt"
"html"
"html/template"
"reflect"
"strconv"
"strings"
"time"
"github.com/gochan-org/gochan/pkg/config"
"github.com/gochan-org/gochan/pkg/gcsql"
"github.com/gochan-org/gochan/pkg/gcutil"
x_html "golang.org/x/net/html"
)
var funcMap = template.FuncMap{
// Arithmetic functions
2015-12-24 23:26:13 -08:00
"add": func(a, b int) int {
return a + b
},
2015-12-24 23:26:13 -08:00
"subtract": func(a, b int) int {
return a - b
},
// Comparison functions (some copied from text/template for compatibility)
"ge": func(a int, b int) bool {
return a >= b
},
"gt": func(a int, b int) bool {
return a > b
},
"le": func(a int, b int) bool {
return a <= b
},
"lt": func(a int, b int) bool {
return a < b
},
"intEq": func(a, b int) bool {
return a == b
},
"isNil": func(i interface{}) bool {
return i == nil
},
// Array functions
"getSlice": func(arr []interface{}, start, length int) []interface{} {
if start < 0 {
start = 0
}
if length > len(arr) {
length = len(arr)
}
return arr[start:length]
},
"len": func(arr []interface{}) int {
return len(arr)
2014-06-16 19:20:36 -07:00
},
// String functions
// "arrToString": arrToString,
"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)
2018-04-06 01:03:57 -07:00
}
return fmt.Sprintf("%0.2f GB", size/1024/1024/1024)
2015-12-24 23:26:13 -08:00
},
"formatTimestamp": func(t time.Time) string {
return t.Format(config.Config.DateTimeFormat)
},
"stringAppend": func(strings ...string) string {
var appended string
for _, str := range strings {
appended += str
}
return appended
2013-05-29 13:43:12 -07:00
},
"truncateMessage": func(msg string, limit int, maxLines int) string {
var truncated bool
split := strings.Split(msg, "<br />")
if len(split) > maxLines {
split = split[:maxLines]
2015-12-24 23:26:13 -08:00
msg = strings.Join(split, "<br />")
truncated = true
}
if len(msg) < limit {
if truncated {
msg = msg + "..."
}
return msg
}
msg = msg[:limit]
truncated = true
if truncated {
msg = msg + "..."
}
return msg
},
2020-05-24 21:59:39 +02:00
"truncateHTMLMessage": truncateHTML,
"stripHTML": func(htmlStr template.HTML) string {
dom := x_html.NewTokenizer(strings.NewReader(string(htmlStr)))
for tokenType := dom.Next(); tokenType != x_html.ErrorToken; {
if tokenType != x_html.TextToken {
tokenType = dom.Next()
continue
}
txtContent := strings.TrimSpace(x_html.UnescapeString(string(dom.Text())))
if len(txtContent) > 0 {
return x_html.EscapeString(txtContent)
}
tokenType = dom.Next()
}
return ""
},
"truncateString": func(msg string, limit int, ellipsis bool) string {
if len(msg) > limit {
if ellipsis {
return msg[:limit] + "..."
}
return msg[:limit]
}
return msg
},
// Imageboard functions
2020-05-22 17:35:59 +02:00
"bannedForever": func(banInfo *gcsql.BanInfo) bool {
return banInfo.BannedForever()
},
"isBanned": func(banInfo *gcsql.BanInfo, board string) bool {
return banInfo.IsBanned(board)
},
2018-10-22 22:02:06 -07:00
"getCatalogThumbnail": func(img string) string {
return gcutil.GetThumbnailPath("catalog", img)
2018-10-22 22:02:06 -07:00
},
"getThreadID": func(postInterface interface{}) (thread int) {
post, ok := postInterface.(gcsql.Post)
if !ok {
thread = 0
} else if post.ParentID == 0 {
thread = post.ID
} else {
thread = post.ParentID
}
return
},
"getPostURL": func(postInterface interface{}, typeOf string, withDomain bool) (postURL string) {
if withDomain {
postURL = config.Config.SiteDomain
}
postURL += config.Config.SiteWebfolder
if typeOf == "recent" {
post, ok := postInterface.(*gcsql.RecentPost)
if !ok {
return
}
postURL = post.GetURL(withDomain)
} else {
post, ok := postInterface.(*gcsql.Post)
if !ok {
return
}
postURL = post.GetURL(withDomain)
}
return
},
2018-10-22 22:02:06 -07:00
"getThreadThumbnail": func(img string) string {
return gcutil.GetThumbnailPath("thread", img)
},
2018-01-28 15:01:59 -08:00
"getUploadType": func(name string) string {
extension := gcutil.GetFileExtension(name)
2018-01-28 15:01:59 -08:00
var uploadType string
switch extension {
case "":
fallthrough
2018-01-28 15:01:59 -08:00
case "deleted":
uploadType = ""
case "webm":
fallthrough
2018-01-28 15:01:59 -08:00
case "jpg":
fallthrough
case "jpeg":
fallthrough
2018-01-28 15:01:59 -08:00
case "gif":
uploadType = "jpg"
case "png":
uploadType = "png"
}
return uploadType
},
2018-10-22 22:02:06 -07:00
"imageToThumbnailPath": func(thumbType string, img string) string {
2018-01-28 15:01:59 -08:00
filetype := strings.ToLower(img[strings.LastIndex(img, ".")+1:])
if filetype == "gif" || filetype == "webm" {
filetype = "jpg"
}
index := strings.LastIndex(img, ".")
if index < 0 || index > len(img) {
return ""
}
2018-10-22 22:02:06 -07:00
thumbSuffix := "t." + filetype
if thumbType == "catalog" {
thumbSuffix = "c." + filetype
}
return img[0:index] + thumbSuffix
},
"numReplies": func(boardid, threadid int) int {
num, err := gcsql.GetReplyCount(threadid)
if err != nil {
return 0
}
return num
},
"getBoardDir": func(id int) string {
var board gcsql.Board
2020-04-20 17:42:48 +02:00
if err := board.PopulateData(id); err != nil {
return ""
}
return board.Dir
},
// Template convenience functions
"makeLoop": func(n int, offset int) []int {
loopArr := make([]int, n)
for i := range loopArr {
loopArr[i] = i + offset
}
return loopArr
},
"generateConfigTable": func() string {
configType := reflect.TypeOf(*config.Config)
tableOut := `<table style="border-collapse: collapse;" id="config"><tr><th>Field name</th><th>Value</th><th>Type</th><th>Description</th></tr>`
numFields := configType.NumField()
for f := 17; f < numFields-2; f++ {
// starting at Lockdown because the earlier fields can't be safely edited from a web interface
field := configType.Field(f)
if field.Tag.Get("critical") != "" {
continue
}
name := field.Name
tableOut += "<tr><th>" + name + "</th><td>"
f := reflect.Indirect(reflect.ValueOf(config.Config)).FieldByName(name)
kind := f.Kind()
switch kind {
case reflect.Int:
tableOut += `<input name="` + name + `" type="number" value="` + html.EscapeString(fmt.Sprintf("%v", f)) + `" class="config-text"/>`
case reflect.String:
tableOut += `<input name="` + name + `" type="text" value="` + html.EscapeString(fmt.Sprintf("%v", f)) + `" class="config-text"/>`
case reflect.Bool:
checked := ""
if f.Bool() {
checked = "checked"
}
tableOut += `<input name="` + name + `" type="checkbox" ` + checked + " />"
case reflect.Slice:
tableOut += `<textarea name="` + name + `" rows="4" cols="28">`
arrLength := f.Len()
for s := 0; s < arrLength; s++ {
newLine := "\n"
if s == arrLength-1 {
newLine = ""
}
tableOut += html.EscapeString(f.Slice(s, s+1).Index(0).String()) + newLine
}
tableOut += "</textarea>"
default:
tableOut += fmt.Sprintf("%v", kind)
}
tableOut += "</td><td>" + kind.String() + "</td><td>"
defaultTag := field.Tag.Get("default")
var defaultTagHTML string
if defaultTag != "" {
defaultTagHTML = " <b>Default: " + defaultTag + "</b>"
}
tableOut += field.Tag.Get("description") + defaultTagHTML + "</td>"
tableOut += "</tr>"
}
tableOut += "</table>"
return tableOut
},
"isStyleDefault": func(style string) bool {
return style == config.Config.DefaultStyle
},
"version": func() string {
return config.Config.Version.String()
},
}