add support for alternative template implementation
This commit is contained in:
parent
3c0122e8a2
commit
38a162e416
@ -68,7 +68,7 @@ func (self *catalogModel) Navbar() string {
|
|||||||
})
|
})
|
||||||
param["prefix"] = self.prefix
|
param["prefix"] = self.prefix
|
||||||
param["links"] = links
|
param["links"] = links
|
||||||
return template.renderTemplate("navbar.mustache", param, self._i18n)
|
return template.renderTemplate("navbar", param, self._i18n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *catalogModel) MarshalJSON() (b []byte, err error) {
|
func (self *catalogModel) MarshalJSON() (b []byte, err error) {
|
||||||
@ -162,7 +162,7 @@ func (self *boardModel) Navbar() string {
|
|||||||
param["frontend"] = self.frontend
|
param["frontend"] = self.frontend
|
||||||
param["prefix"] = self.prefix
|
param["prefix"] = self.prefix
|
||||||
param["links"] = self.PageList()
|
param["links"] = self.PageList()
|
||||||
return template.renderTemplate("navbar.mustache", param, self._i18n)
|
return template.renderTemplate("navbar", param, self._i18n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *boardModel) Board() string {
|
func (self *boardModel) Board() string {
|
||||||
@ -517,7 +517,7 @@ func (self *post) SetIndex(idx int) {
|
|||||||
func (self *post) RenderPost() string {
|
func (self *post) RenderPost() string {
|
||||||
param := make(map[string]interface{})
|
param := make(map[string]interface{})
|
||||||
param["post"] = self
|
param["post"] = self
|
||||||
return template.renderTemplate("post.mustache", param, self._i18n)
|
return template.renderTemplate("post", param, self._i18n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *post) RenderTruncatedPost() string {
|
func (self *post) RenderTruncatedPost() string {
|
||||||
@ -623,7 +623,7 @@ func (self *thread) Navbar() string {
|
|||||||
param["frontend"] = self.Board()
|
param["frontend"] = self.Board()
|
||||||
param["links"] = self.links
|
param["links"] = self.links
|
||||||
param["prefix"] = self.prefix
|
param["prefix"] = self.prefix
|
||||||
return template.renderTemplate("navbar.mustache", param, self._i18n)
|
return template.renderTemplate("navbar", param, self._i18n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *thread) Board() string {
|
func (self *thread) Board() string {
|
||||||
@ -649,11 +649,6 @@ func (self *thread) ImageCount() (count int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get our default template dir
|
|
||||||
func defaultTemplateDir() string {
|
|
||||||
return filepath.Join("contrib", "templates", "default")
|
|
||||||
}
|
|
||||||
|
|
||||||
func createThreadModel(posts ...PostModel) ThreadModel {
|
func createThreadModel(posts ...PostModel) ThreadModel {
|
||||||
op := posts[0]
|
op := posts[0]
|
||||||
group := op.Board()
|
group := op.Board()
|
||||||
|
@ -5,534 +5,59 @@
|
|||||||
package srnd
|
package srnd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/cbroglie/mustache"
|
|
||||||
tinyhtml "github.com/whyrusleeping/tinyhtml"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type templateEngine struct {
|
type TemplateDriver interface {
|
||||||
// loaded templates
|
RenderString(template string, obj interface{}) (string, error)
|
||||||
templates map[string]string
|
Render(template string, obj interface{}, w io.Writer) error
|
||||||
// root directory for templates
|
Ext() string
|
||||||
template_dir string
|
|
||||||
// mutex for accessing templates
|
|
||||||
templates_mtx sync.RWMutex
|
|
||||||
// do we want to minimize the html generated?
|
|
||||||
Minimize bool
|
|
||||||
// database
|
|
||||||
DB Database
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *templateEngine) templateCached(name string) (ok bool) {
|
// get our default template dir
|
||||||
self.templates_mtx.Lock()
|
func defaultTemplateDir() string {
|
||||||
_, ok = self.templates[name]
|
p, _ := filepath.Abs(filepath.Join("contrib", "templates", "default"))
|
||||||
self.templates_mtx.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// explicitly reload a template
|
|
||||||
func (self *templateEngine) reloadTemplate(name string) {
|
|
||||||
self.templates_mtx.Lock()
|
|
||||||
self.templates[name] = self.loadTemplate(name)
|
|
||||||
self.templates_mtx.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if we have this template
|
|
||||||
func (self *templateEngine) hasTemplate(name string) bool {
|
|
||||||
return CheckFile(self.templateFilepath(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// explicitly reload all loaded templates
|
|
||||||
func (self *templateEngine) reloadAllTemplates() {
|
|
||||||
loadThese := []string{}
|
|
||||||
// get all the names of the templates we have loaded
|
|
||||||
self.templates_mtx.Lock()
|
|
||||||
for tname, _ := range self.templates {
|
|
||||||
loadThese = append(loadThese, tname)
|
|
||||||
}
|
|
||||||
self.templates_mtx.Unlock()
|
|
||||||
// for each template we have loaded, reload the contents from file
|
|
||||||
for _, tname := range loadThese {
|
|
||||||
self.reloadTemplate(tname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get cached post model from cache after updating it
|
|
||||||
func (self *templateEngine) updatePostModel(prefix, frontend, msgid, rootmsgid, group string, db Database) PostModel {
|
|
||||||
return db.GetPostModel(prefix, msgid)
|
|
||||||
/*
|
|
||||||
// get board
|
|
||||||
self.groups_mtx.Lock()
|
|
||||||
board := self.groups[group]
|
|
||||||
self.groups_mtx.Unlock()
|
|
||||||
|
|
||||||
var th ThreadModel
|
|
||||||
if msgid == rootmsgid {
|
|
||||||
// new thread
|
|
||||||
if len(board) > 0 {
|
|
||||||
page := board[0]
|
|
||||||
page.Update(db)
|
|
||||||
th = page.GetThread(rootmsgid)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// reply
|
|
||||||
for _, page := range board {
|
|
||||||
t := page.GetThread(rootmsgid)
|
|
||||||
if t != nil {
|
|
||||||
th = t
|
|
||||||
th.Update(db)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if th == nil {
|
|
||||||
// reload board, this will be a heavy operation
|
|
||||||
board.UpdateAll(db)
|
|
||||||
// find it
|
|
||||||
for _, page := range board {
|
|
||||||
t := page.GetThread(rootmsgid)
|
|
||||||
if t != nil {
|
|
||||||
th = t
|
|
||||||
th.Update(db)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, page := range board {
|
|
||||||
updateLinkCacheForBoard(page)
|
|
||||||
}
|
|
||||||
self.groups_mtx.Lock()
|
|
||||||
self.groups[group] = board
|
|
||||||
self.groups_mtx.Unlock()
|
|
||||||
}
|
|
||||||
if th == nil {
|
|
||||||
if rootmsgid == msgid {
|
|
||||||
return db.GetPostModel(prefix, rootmsgid)
|
|
||||||
}
|
|
||||||
log.Println("template could not find thread", rootmsgid, "in", group)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// found
|
|
||||||
m := th.OP()
|
|
||||||
if m.MessageID() == msgid {
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
for _, p := range th.Replies() {
|
|
||||||
if p.MessageID() == msgid {
|
|
||||||
// found as reply
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
}
|
|
||||||
log.Println("template could not find post model for thread", rootmsgid, "in", group)
|
func templateDriverForFile(fname string) TemplateDriver {
|
||||||
// not found
|
switch strings.ToLower(filepath.Ext(fname)) {
|
||||||
|
case ".mustache":
|
||||||
|
return new(mustacheDriver)
|
||||||
|
case ".tmpl":
|
||||||
|
return new(stdTemplateDriver)
|
||||||
|
default:
|
||||||
return nil
|
return nil
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the filepath to a template
|
|
||||||
func (self *templateEngine) templateFilepath(name string) string {
|
|
||||||
if strings.Count(name, "..") > 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return filepath.Join(self.template_dir, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// load a template from file, return as string
|
|
||||||
func (self *templateEngine) loadTemplate(name string) (t string) {
|
|
||||||
b, err := ioutil.ReadFile(self.templateFilepath(name))
|
|
||||||
if err == nil {
|
|
||||||
t = string(b)
|
|
||||||
} else {
|
|
||||||
log.Println("error loading template", err)
|
|
||||||
t = err.Error()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// get a template, if it's not cached load from file and cache it
|
|
||||||
func (self *templateEngine) getTemplate(name string) (t string) {
|
|
||||||
if !self.templateCached(name) {
|
|
||||||
self.templates_mtx.Lock()
|
|
||||||
self.templates[name] = self.loadTemplate(name)
|
|
||||||
self.templates_mtx.Unlock()
|
|
||||||
}
|
|
||||||
self.templates_mtx.Lock()
|
|
||||||
t, _ = self.templates[name]
|
|
||||||
self.templates_mtx.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// render a template, self explanitory
|
|
||||||
func (self *templateEngine) renderTemplate(name string, obj map[string]interface{}, i18n *I18N) string {
|
|
||||||
t := self.getTemplate(name)
|
|
||||||
if i18n == nil {
|
|
||||||
i18n = I18nProvider
|
|
||||||
}
|
|
||||||
obj["i18n"] = i18n
|
|
||||||
s, err := mustache.Render(t, obj)
|
|
||||||
if err == nil {
|
|
||||||
return s
|
|
||||||
} else {
|
|
||||||
return err.Error()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// write a template to an io.Writer
|
func templateDriverFromDir(dir string) TemplateDriver {
|
||||||
func (self *templateEngine) writeTemplate(name string, obj map[string]interface{}, wr io.Writer, i18n *I18N) (err error) {
|
files, err := ioutil.ReadDir(dir)
|
||||||
str := self.renderTemplate(name, obj, i18n)
|
if err == nil && len(files) > 0 {
|
||||||
var r io.Reader
|
for idx := range files {
|
||||||
r = bytes.NewBufferString(str)
|
if !files[idx].IsDir() {
|
||||||
if self.Minimize {
|
return templateDriverForFile(files[idx].Name())
|
||||||
r = tinyhtml.New(r)
|
|
||||||
}
|
|
||||||
_, err = io.Copy(wr, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// easy wrapper for json.NewEncoder
|
|
||||||
func (self *templateEngine) renderJSON(wr io.Writer, obj interface{}) {
|
|
||||||
err := json.NewEncoder(wr).Encode(obj)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("error rendering json", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get a board model given a newsgroup
|
|
||||||
// load un updated board model if we don't have it
|
|
||||||
func (self *templateEngine) obtainBoard(prefix, frontend, group string, db Database) (model GroupModel) {
|
|
||||||
// warning, we attempt to do smart reloading
|
|
||||||
// dark magic may lurk here
|
|
||||||
p := db.GetGroupPageCount(group)
|
|
||||||
pages := int(p)
|
|
||||||
perpage, _ := db.GetThreadsPerPage(group)
|
|
||||||
// reload all the pages
|
|
||||||
var newModel GroupModel
|
|
||||||
for page := 0; page < pages; page++ {
|
|
||||||
newModel = append(newModel, db.GetGroupForPage(prefix, frontend, group, page, int(perpage)))
|
|
||||||
}
|
|
||||||
model = newModel
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *templateEngine) genCatalog(prefix, frontend, group string, wr io.Writer, db Database, i18n *I18N) {
|
|
||||||
board := self.obtainBoard(prefix, frontend, group, db)
|
|
||||||
catalog := new(catalogModel)
|
|
||||||
catalog.prefix = prefix
|
|
||||||
catalog.frontend = frontend
|
|
||||||
catalog.board = group
|
|
||||||
catalog.I18N(i18n)
|
|
||||||
for page, bm := range board {
|
|
||||||
for _, th := range bm.Threads() {
|
|
||||||
th.Update(db)
|
|
||||||
catalog.threads = append(catalog.threads, &catalogItemModel{op: th.OP(), page: page, replycount: len(th.Replies())})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.writeTemplate("catalog.mustache", map[string]interface{}{"board": catalog}, wr, i18n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate a board page
|
|
||||||
func (self *templateEngine) genBoardPage(allowFiles, requireCaptcha bool, prefix, frontend, newsgroup string, page int, wr io.Writer, db Database, json bool, i18n *I18N) {
|
|
||||||
// get the board page model
|
|
||||||
perpage, _ := db.GetThreadsPerPage(newsgroup)
|
|
||||||
boardPage := db.GetGroupForPage(prefix, frontend, newsgroup, page, int(perpage))
|
|
||||||
boardPage.Update(db)
|
|
||||||
boardPage.I18N(i18n)
|
|
||||||
// render it
|
|
||||||
if json {
|
|
||||||
self.renderJSON(wr, boardPage)
|
|
||||||
} else {
|
|
||||||
form := renderPostForm(prefix, newsgroup, "", allowFiles, requireCaptcha, i18n)
|
|
||||||
self.writeTemplate("board.mustache", map[string]interface{}{"board": boardPage, "page": page, "form": form}, wr, i18n)
|
|
||||||
}
|
}
|
||||||
}
|
log.Println("no template found in ", dir, " ", err)
|
||||||
|
return nil
|
||||||
func (self *templateEngine) genUkko(prefix, frontend string, wr io.Writer, database Database, json bool, i18n *I18N) {
|
|
||||||
self.genUkkoPaginated(prefix, frontend, wr, database, 0, json, i18n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *templateEngine) genUkkoPaginated(prefix, frontend string, wr io.Writer, database Database, page int, json bool, i18n *I18N) {
|
|
||||||
var threads []ThreadModel
|
|
||||||
for _, article := range database.GetLastBumpedThreadsPaginated("", 10, page*10) {
|
|
||||||
root := article[0]
|
|
||||||
thread, err := database.GetThreadModel(prefix, root)
|
|
||||||
if err == nil {
|
|
||||||
thread.I18N(i18n)
|
|
||||||
threads = append(threads, thread)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obj := map[string]interface{}{"prefix": prefix, "threads": threads, "page": page}
|
|
||||||
if page > 0 {
|
|
||||||
obj["prev"] = map[string]interface{}{"no": page - 1}
|
|
||||||
}
|
|
||||||
if page < 10 {
|
|
||||||
obj["next"] = map[string]interface{}{"no": page + 1}
|
|
||||||
}
|
|
||||||
if json {
|
|
||||||
self.renderJSON(wr, obj)
|
|
||||||
} else {
|
|
||||||
// render ukko navbar
|
|
||||||
navbar := make(map[string]interface{})
|
|
||||||
navbar["name"] = "Overboard"
|
|
||||||
navbar["frontend"] = frontend
|
|
||||||
navbar["prefix"] = prefix
|
|
||||||
// inject navbar
|
|
||||||
obj["navbar"] = self.renderTemplate("navbar.mustache", navbar, i18n)
|
|
||||||
// render
|
|
||||||
self.writeTemplate("ukko.mustache", obj, wr, i18n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *templateEngine) genThread(allowFiles, requireCaptcha bool, root ArticleEntry, prefix, frontend string, wr io.Writer, db Database, json bool, i18n *I18N) {
|
|
||||||
newsgroup := root.Newsgroup()
|
|
||||||
msgid := root.MessageID()
|
|
||||||
|
|
||||||
/*
|
|
||||||
if !db.HasArticleLocal(msgid) {
|
|
||||||
log.Println("don't have", msgid, "locally, not regenerating")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
t, err := db.GetThreadModel(prefix, msgid)
|
|
||||||
if err == nil {
|
|
||||||
if json {
|
|
||||||
self.renderJSON(wr, t)
|
|
||||||
} else {
|
|
||||||
t.I18N(i18n)
|
|
||||||
form := renderPostForm(prefix, newsgroup, msgid, allowFiles, requireCaptcha, i18n)
|
|
||||||
self.writeTemplate("thread.mustache", map[string]interface{}{"thread": t, "board": map[string]interface{}{"Name": newsgroup, "Frontend": frontend, "AllowFiles": allowFiles}, "form": form, "prefix": prefix}, wr, i18n)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Println("templates: error getting thread for ", msgid, err.Error())
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
// get the board model, don't update the board
|
|
||||||
board := self.obtainBoard(prefix, frontend, newsgroup, false, db)
|
|
||||||
// find the thread model in question
|
|
||||||
for _, pagemodel := range board {
|
|
||||||
t := pagemodel.GetThread(msgid)
|
|
||||||
if t != nil {
|
|
||||||
// update thread
|
|
||||||
t.Update(db)
|
|
||||||
// render it
|
|
||||||
if json {
|
|
||||||
self.renderJSON(wr, t)
|
|
||||||
} else {
|
|
||||||
form := renderPostForm(prefix, newsgroup, msgid, allowFiles)
|
|
||||||
self.writeTemplate("thread.mustache", map[string]interface{}{"thread": t, "board": pagemodel, "form": form}, wr)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Println("thread not found for message id", msgid)
|
|
||||||
return
|
|
||||||
|
|
||||||
// we didn't find it D:
|
|
||||||
// reload everything
|
|
||||||
// TODO: should we reload everything!?
|
|
||||||
b := self.obtainBoard(prefix, frontend, newsgroup, true, db)
|
|
||||||
// find the thread model in question
|
|
||||||
for _, pagemodel := range b {
|
|
||||||
t := pagemodel.GetThread(msgid)
|
|
||||||
if t != nil {
|
|
||||||
// we found it
|
|
||||||
// render thread
|
|
||||||
t.Update(db)
|
|
||||||
if json {
|
|
||||||
self.renderJSON(wr, t)
|
|
||||||
} else {
|
|
||||||
form := renderPostForm(prefix, newsgroup, msgid, allowFiles)
|
|
||||||
self.writeTemplate("thread.mustache", map[string]interface{}{"thread": t, "board": pagemodel, "form": form}, wr)
|
|
||||||
}
|
|
||||||
self.groups_mtx.Lock()
|
|
||||||
self.groups[newsgroup] = b
|
|
||||||
self.groups_mtx.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// it's not there wtf
|
|
||||||
log.Println("thread not found for message id", msgid)
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
// change the directory we are using for templates
|
|
||||||
func (self *templateEngine) changeTemplateDir(dirname string) {
|
|
||||||
log.Println("change template directory to", dirname)
|
|
||||||
self.template_dir = dirname
|
|
||||||
self.reloadAllTemplates()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *templateEngine) createNotFoundHandler(prefix, frontend string) (h http.Handler) {
|
|
||||||
h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
self.renderNotFound(w, r, prefix, frontend, nil)
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// default renderer of 404 pages
|
|
||||||
func (self *templateEngine) renderNotFound(wr http.ResponseWriter, r *http.Request, prefix, frontend string, i18n *I18N) {
|
|
||||||
wr.WriteHeader(404)
|
|
||||||
opts := make(map[string]interface{})
|
|
||||||
opts["prefix"] = prefix
|
|
||||||
opts["frontend"] = frontend
|
|
||||||
self.writeTemplate("404.mustache", opts, wr, i18n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTemplateEngine(dir string) *templateEngine {
|
func newTemplateEngine(dir string) *templateEngine {
|
||||||
return &templateEngine{
|
return &templateEngine{
|
||||||
templates: make(map[string]string),
|
templates: make(map[string]string),
|
||||||
template_dir: dir,
|
template_dir: dir,
|
||||||
|
driver: templateDriverFromDir(dir),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *templateEngine) findLink(prefix, hash string) (url string) {
|
|
||||||
ents, _ := self.DB.GetCitesByPostHashLike(hash)
|
|
||||||
if len(ents) > 0 {
|
|
||||||
url = fmt.Sprintf("%st/%s/#%s", prefix, HashMessageID(ents[0].Reference()), HashMessageID(ents[0].MessageID()))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var template = newTemplateEngine(defaultTemplateDir())
|
var template = newTemplateEngine(defaultTemplateDir())
|
||||||
|
|
||||||
func renderPostForm(prefix, board, op_msg_id string, files, captcha bool, i18n *I18N) string {
|
|
||||||
url := prefix + "post/" + board
|
|
||||||
button := "New Thread"
|
|
||||||
if op_msg_id != "" {
|
|
||||||
button = "Reply"
|
|
||||||
if i18n != nil {
|
|
||||||
b := i18n.Translate("postbutton_reply")
|
|
||||||
if b != "" {
|
|
||||||
button = b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if i18n != nil {
|
|
||||||
b := i18n.Translate("postbutton_thread")
|
|
||||||
if b != "" {
|
|
||||||
button = b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return template.renderTemplate("postform.mustache", map[string]interface{}{"post_url": url, "reference": op_msg_id, "button": button, "files": files, "prefix": prefix, "DisableCaptcha": !captcha}, i18n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate misc graphs
|
|
||||||
func (self *templateEngine) genGraphs(prefix string, wr io.Writer, db Database, i18n *I18N) {
|
|
||||||
|
|
||||||
//
|
|
||||||
// begin gen history.html
|
|
||||||
//
|
|
||||||
|
|
||||||
var all_posts postsGraph
|
|
||||||
// this may take a bit
|
|
||||||
log.Println("getting monthly post history...")
|
|
||||||
posts := db.GetMonthlyPostHistory()
|
|
||||||
|
|
||||||
if posts == nil {
|
|
||||||
// wtf?
|
|
||||||
log.Println("no monthly posts gotten wtfug yo?")
|
|
||||||
} else {
|
|
||||||
for _, entry := range posts {
|
|
||||||
all_posts = append(all_posts, postsGraphRow{
|
|
||||||
day: entry.Time(),
|
|
||||||
Num: entry.Count(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Sort(all_posts)
|
|
||||||
|
|
||||||
_, err := io.WriteString(wr, self.renderTemplate("graph_history.mustache", map[string]interface{}{"history": all_posts}, i18n))
|
|
||||||
if err != nil {
|
|
||||||
log.Println("error writing history graph", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// end gen history.html
|
|
||||||
//
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *templateEngine) genBoardList(prefix, name string, wr io.Writer, db Database, i18n *I18N) {
|
|
||||||
// the graph for the front page
|
|
||||||
var frontpage_graph boardPageRows
|
|
||||||
|
|
||||||
// for each group
|
|
||||||
groups := db.GetAllNewsgroups()
|
|
||||||
for _, group := range groups {
|
|
||||||
// posts this hour
|
|
||||||
hour := db.CountPostsInGroup(group, 3600)
|
|
||||||
// posts today
|
|
||||||
day := db.CountPostsInGroup(group, 86400)
|
|
||||||
// posts total
|
|
||||||
all := db.CountPostsInGroup(group, 0)
|
|
||||||
frontpage_graph = append(frontpage_graph, boardPageRow{
|
|
||||||
All: all,
|
|
||||||
Day: day,
|
|
||||||
Hour: hour,
|
|
||||||
Board: group,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
param := map[string]interface{}{
|
|
||||||
"prefix": prefix,
|
|
||||||
"frontend": name,
|
|
||||||
}
|
|
||||||
sort.Sort(frontpage_graph)
|
|
||||||
param["graph"] = frontpage_graph
|
|
||||||
_, err := io.WriteString(wr, self.renderTemplate("boardlist.mustache", param, i18n))
|
|
||||||
if err != nil {
|
|
||||||
log.Println("error writing board list page", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate front page
|
|
||||||
func (self *templateEngine) genFrontPage(top_count int, prefix, frontend_name string, indexwr, boardswr io.Writer, db Database, i18n *I18N) {
|
|
||||||
|
|
||||||
models := db.GetLastPostedPostModels(prefix, 20)
|
|
||||||
|
|
||||||
for idx := range models {
|
|
||||||
models[idx].I18N(i18n)
|
|
||||||
}
|
|
||||||
|
|
||||||
wr := indexwr
|
|
||||||
|
|
||||||
param := make(map[string]interface{})
|
|
||||||
|
|
||||||
param["overview"] = self.renderTemplate("overview.mustache", map[string]interface{}{"overview": overviewModel(models)}, i18n)
|
|
||||||
/*
|
|
||||||
sort.Sort(posts_graph)
|
|
||||||
param["postsgraph"] = self.renderTemplate("posts_graph.mustache", map[string]interface{}{"graph": posts_graph})
|
|
||||||
|
|
||||||
if len(frontpage_graph) > top_count {
|
|
||||||
param["boardgraph"] = frontpage_graph[:top_count]
|
|
||||||
} else {
|
|
||||||
param["boardgraph"] = frontpage_graph
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
param["frontend"] = frontend_name
|
|
||||||
param["totalposts"] = db.ArticleCount()
|
|
||||||
param["prefix"] = prefix
|
|
||||||
// render and inject navbar
|
|
||||||
param["navbar"] = self.renderTemplate("navbar.mustache", map[string]interface{}{"name": "Front Page", "frontend": frontend_name, "prefix": prefix}, i18n)
|
|
||||||
|
|
||||||
_, err := io.WriteString(wr, self.renderTemplate("frontpage.mustache", param, i18n))
|
|
||||||
if err != nil {
|
|
||||||
log.Println("error writing front page", err)
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
wr = boardswr
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReloadTemplates() {
|
func ReloadTemplates() {
|
||||||
log.Println("reload templates")
|
log.Println("reload templates")
|
||||||
template.reloadAllTemplates()
|
template.reloadAllTemplates()
|
||||||
|
522
contrib/backends/srndv2/src/srnd/templates_impl.go
Normal file
522
contrib/backends/srndv2/src/srnd/templates_impl.go
Normal file
@ -0,0 +1,522 @@
|
|||||||
|
//
|
||||||
|
// templates.go
|
||||||
|
// template model interfaces
|
||||||
|
//
|
||||||
|
package srnd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type templateEngine struct {
|
||||||
|
// loaded templates
|
||||||
|
templates map[string]string
|
||||||
|
// root directory for templates
|
||||||
|
template_dir string
|
||||||
|
// mutex for accessing templates
|
||||||
|
templates_mtx sync.RWMutex
|
||||||
|
// do we want to minimize the html generated?
|
||||||
|
Minimize bool
|
||||||
|
// database
|
||||||
|
DB Database
|
||||||
|
// template driver
|
||||||
|
driver TemplateDriver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *templateEngine) templateCached(name string) (ok bool) {
|
||||||
|
self.templates_mtx.Lock()
|
||||||
|
_, ok = self.templates[name]
|
||||||
|
self.templates_mtx.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// explicitly reload a template
|
||||||
|
func (self *templateEngine) reloadTemplate(name string) {
|
||||||
|
self.templates_mtx.Lock()
|
||||||
|
self.templates[name] = self.loadTemplate(name)
|
||||||
|
self.templates_mtx.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we have this template
|
||||||
|
func (self *templateEngine) hasTemplate(name string) bool {
|
||||||
|
return CheckFile(self.templateFilepath(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// explicitly reload all loaded templates
|
||||||
|
func (self *templateEngine) reloadAllTemplates() {
|
||||||
|
loadThese := []string{}
|
||||||
|
// get all the names of the templates we have loaded
|
||||||
|
self.templates_mtx.Lock()
|
||||||
|
for tname, _ := range self.templates {
|
||||||
|
loadThese = append(loadThese, tname)
|
||||||
|
}
|
||||||
|
self.templates_mtx.Unlock()
|
||||||
|
// for each template we have loaded, reload the contents from file
|
||||||
|
for _, tname := range loadThese {
|
||||||
|
self.reloadTemplate(tname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get cached post model from cache after updating it
|
||||||
|
func (self *templateEngine) updatePostModel(prefix, frontend, msgid, rootmsgid, group string, db Database) PostModel {
|
||||||
|
return db.GetPostModel(prefix, msgid)
|
||||||
|
/*
|
||||||
|
// get board
|
||||||
|
self.groups_mtx.Lock()
|
||||||
|
board := self.groups[group]
|
||||||
|
self.groups_mtx.Unlock()
|
||||||
|
|
||||||
|
var th ThreadModel
|
||||||
|
if msgid == rootmsgid {
|
||||||
|
// new thread
|
||||||
|
if len(board) > 0 {
|
||||||
|
page := board[0]
|
||||||
|
page.Update(db)
|
||||||
|
th = page.GetThread(rootmsgid)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// reply
|
||||||
|
for _, page := range board {
|
||||||
|
t := page.GetThread(rootmsgid)
|
||||||
|
if t != nil {
|
||||||
|
th = t
|
||||||
|
th.Update(db)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if th == nil {
|
||||||
|
// reload board, this will be a heavy operation
|
||||||
|
board.UpdateAll(db)
|
||||||
|
// find it
|
||||||
|
for _, page := range board {
|
||||||
|
t := page.GetThread(rootmsgid)
|
||||||
|
if t != nil {
|
||||||
|
th = t
|
||||||
|
th.Update(db)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, page := range board {
|
||||||
|
updateLinkCacheForBoard(page)
|
||||||
|
}
|
||||||
|
self.groups_mtx.Lock()
|
||||||
|
self.groups[group] = board
|
||||||
|
self.groups_mtx.Unlock()
|
||||||
|
}
|
||||||
|
if th == nil {
|
||||||
|
if rootmsgid == msgid {
|
||||||
|
return db.GetPostModel(prefix, rootmsgid)
|
||||||
|
}
|
||||||
|
log.Println("template could not find thread", rootmsgid, "in", group)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// found
|
||||||
|
m := th.OP()
|
||||||
|
if m.MessageID() == msgid {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
for _, p := range th.Replies() {
|
||||||
|
if p.MessageID() == msgid {
|
||||||
|
// found as reply
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Println("template could not find post model for thread", rootmsgid, "in", group)
|
||||||
|
// not found
|
||||||
|
return nil
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the filepath to a template
|
||||||
|
func (self *templateEngine) templateFilepath(name string) string {
|
||||||
|
if strings.Count(name, "..") > 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return filepath.Join(self.template_dir, name+self.driver.Ext())
|
||||||
|
}
|
||||||
|
|
||||||
|
// load a template from file, return as string
|
||||||
|
func (self *templateEngine) loadTemplate(name string) (t string) {
|
||||||
|
b, err := ioutil.ReadFile(self.templateFilepath(name))
|
||||||
|
if err == nil {
|
||||||
|
t = string(b)
|
||||||
|
} else {
|
||||||
|
log.Println("error loading template", err)
|
||||||
|
t = err.Error()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a template, if it's not cached load from file and cache it
|
||||||
|
func (self *templateEngine) getTemplate(name string) (t string) {
|
||||||
|
if !self.templateCached(name) {
|
||||||
|
self.templates_mtx.Lock()
|
||||||
|
self.templates[name] = self.loadTemplate(name)
|
||||||
|
self.templates_mtx.Unlock()
|
||||||
|
}
|
||||||
|
self.templates_mtx.Lock()
|
||||||
|
t, _ = self.templates[name]
|
||||||
|
self.templates_mtx.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// render a template, self explanitory
|
||||||
|
func (self *templateEngine) renderTemplate(name string, obj map[string]interface{}, i18n *I18N) string {
|
||||||
|
t := self.getTemplate(name)
|
||||||
|
if i18n == nil {
|
||||||
|
i18n = I18nProvider
|
||||||
|
}
|
||||||
|
obj["i18n"] = i18n
|
||||||
|
s, err := self.driver.RenderString(t, obj)
|
||||||
|
if err == nil {
|
||||||
|
return s
|
||||||
|
} else {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write a template to an io.Writer
|
||||||
|
func (self *templateEngine) writeTemplate(name string, obj map[string]interface{}, wr io.Writer, i18n *I18N) (err error) {
|
||||||
|
t := self.getTemplate(name)
|
||||||
|
if i18n == nil {
|
||||||
|
i18n = I18nProvider
|
||||||
|
}
|
||||||
|
obj["i18n"] = i18n
|
||||||
|
return self.driver.Render(t, obj, wr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// easy wrapper for json.NewEncoder
|
||||||
|
func (self *templateEngine) renderJSON(wr io.Writer, obj interface{}) {
|
||||||
|
err := json.NewEncoder(wr).Encode(obj)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error rendering json", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a board model given a newsgroup
|
||||||
|
// load un updated board model if we don't have it
|
||||||
|
func (self *templateEngine) obtainBoard(prefix, frontend, group string, db Database) (model GroupModel) {
|
||||||
|
// warning, we attempt to do smart reloading
|
||||||
|
// dark magic may lurk here
|
||||||
|
p := db.GetGroupPageCount(group)
|
||||||
|
pages := int(p)
|
||||||
|
perpage, _ := db.GetThreadsPerPage(group)
|
||||||
|
// reload all the pages
|
||||||
|
var newModel GroupModel
|
||||||
|
for page := 0; page < pages; page++ {
|
||||||
|
newModel = append(newModel, db.GetGroupForPage(prefix, frontend, group, page, int(perpage)))
|
||||||
|
}
|
||||||
|
model = newModel
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *templateEngine) genCatalog(prefix, frontend, group string, wr io.Writer, db Database, i18n *I18N) {
|
||||||
|
board := self.obtainBoard(prefix, frontend, group, db)
|
||||||
|
catalog := new(catalogModel)
|
||||||
|
catalog.prefix = prefix
|
||||||
|
catalog.frontend = frontend
|
||||||
|
catalog.board = group
|
||||||
|
catalog.I18N(i18n)
|
||||||
|
for page, bm := range board {
|
||||||
|
for _, th := range bm.Threads() {
|
||||||
|
th.Update(db)
|
||||||
|
catalog.threads = append(catalog.threads, &catalogItemModel{op: th.OP(), page: page, replycount: len(th.Replies())})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.writeTemplate("catalog", map[string]interface{}{"board": catalog}, wr, i18n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a board page
|
||||||
|
func (self *templateEngine) genBoardPage(allowFiles, requireCaptcha bool, prefix, frontend, newsgroup string, page int, wr io.Writer, db Database, json bool, i18n *I18N) {
|
||||||
|
// get the board page model
|
||||||
|
perpage, _ := db.GetThreadsPerPage(newsgroup)
|
||||||
|
boardPage := db.GetGroupForPage(prefix, frontend, newsgroup, page, int(perpage))
|
||||||
|
boardPage.Update(db)
|
||||||
|
boardPage.I18N(i18n)
|
||||||
|
// render it
|
||||||
|
if json {
|
||||||
|
self.renderJSON(wr, boardPage)
|
||||||
|
} else {
|
||||||
|
form := renderPostForm(prefix, newsgroup, "", allowFiles, requireCaptcha, i18n)
|
||||||
|
self.writeTemplate("board", map[string]interface{}{"board": boardPage, "page": page, "form": form}, wr, i18n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *templateEngine) genUkko(prefix, frontend string, wr io.Writer, database Database, json bool, i18n *I18N) {
|
||||||
|
self.genUkkoPaginated(prefix, frontend, wr, database, 0, json, i18n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *templateEngine) genUkkoPaginated(prefix, frontend string, wr io.Writer, database Database, page int, json bool, i18n *I18N) {
|
||||||
|
var threads []ThreadModel
|
||||||
|
for _, article := range database.GetLastBumpedThreadsPaginated("", 10, page*10) {
|
||||||
|
root := article[0]
|
||||||
|
thread, err := database.GetThreadModel(prefix, root)
|
||||||
|
if err == nil {
|
||||||
|
thread.I18N(i18n)
|
||||||
|
threads = append(threads, thread)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj := map[string]interface{}{"prefix": prefix, "threads": threads, "page": page}
|
||||||
|
if page > 0 {
|
||||||
|
obj["prev"] = map[string]interface{}{"no": page - 1}
|
||||||
|
}
|
||||||
|
if page < 10 {
|
||||||
|
obj["next"] = map[string]interface{}{"no": page + 1}
|
||||||
|
}
|
||||||
|
if json {
|
||||||
|
self.renderJSON(wr, obj)
|
||||||
|
} else {
|
||||||
|
// render ukko navbar
|
||||||
|
navbar := make(map[string]interface{})
|
||||||
|
navbar["name"] = "Overboard"
|
||||||
|
navbar["frontend"] = frontend
|
||||||
|
navbar["prefix"] = prefix
|
||||||
|
// inject navbar
|
||||||
|
obj["navbar"] = self.renderTemplate("navbar", navbar, i18n)
|
||||||
|
// render
|
||||||
|
self.writeTemplate("ukko", obj, wr, i18n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *templateEngine) genThread(allowFiles, requireCaptcha bool, root ArticleEntry, prefix, frontend string, wr io.Writer, db Database, json bool, i18n *I18N) {
|
||||||
|
newsgroup := root.Newsgroup()
|
||||||
|
msgid := root.MessageID()
|
||||||
|
|
||||||
|
/*
|
||||||
|
if !db.HasArticleLocal(msgid) {
|
||||||
|
log.Println("don't have", msgid, "locally, not regenerating")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
t, err := db.GetThreadModel(prefix, msgid)
|
||||||
|
if err == nil {
|
||||||
|
if json {
|
||||||
|
self.renderJSON(wr, t)
|
||||||
|
} else {
|
||||||
|
t.I18N(i18n)
|
||||||
|
form := renderPostForm(prefix, newsgroup, msgid, allowFiles, requireCaptcha, i18n)
|
||||||
|
self.writeTemplate("thread", map[string]interface{}{"thread": t, "board": map[string]interface{}{"Name": newsgroup, "Frontend": frontend, "AllowFiles": allowFiles}, "form": form, "prefix": prefix}, wr, i18n)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Println("templates: error getting thread for ", msgid, err.Error())
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// get the board model, don't update the board
|
||||||
|
board := self.obtainBoard(prefix, frontend, newsgroup, false, db)
|
||||||
|
// find the thread model in question
|
||||||
|
for _, pagemodel := range board {
|
||||||
|
t := pagemodel.GetThread(msgid)
|
||||||
|
if t != nil {
|
||||||
|
// update thread
|
||||||
|
t.Update(db)
|
||||||
|
// render it
|
||||||
|
if json {
|
||||||
|
self.renderJSON(wr, t)
|
||||||
|
} else {
|
||||||
|
form := renderPostForm(prefix, newsgroup, msgid, allowFiles)
|
||||||
|
self.writeTemplate("thread.mustache", map[string]interface{}{"thread": t, "board": pagemodel, "form": form}, wr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Println("thread not found for message id", msgid)
|
||||||
|
return
|
||||||
|
|
||||||
|
// we didn't find it D:
|
||||||
|
// reload everything
|
||||||
|
// TODO: should we reload everything!?
|
||||||
|
b := self.obtainBoard(prefix, frontend, newsgroup, true, db)
|
||||||
|
// find the thread model in question
|
||||||
|
for _, pagemodel := range b {
|
||||||
|
t := pagemodel.GetThread(msgid)
|
||||||
|
if t != nil {
|
||||||
|
// we found it
|
||||||
|
// render thread
|
||||||
|
t.Update(db)
|
||||||
|
if json {
|
||||||
|
self.renderJSON(wr, t)
|
||||||
|
} else {
|
||||||
|
form := renderPostForm(prefix, newsgroup, msgid, allowFiles)
|
||||||
|
self.writeTemplate("thread.mustache", map[string]interface{}{"thread": t, "board": pagemodel, "form": form}, wr)
|
||||||
|
}
|
||||||
|
self.groups_mtx.Lock()
|
||||||
|
self.groups[newsgroup] = b
|
||||||
|
self.groups_mtx.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// it's not there wtf
|
||||||
|
log.Println("thread not found for message id", msgid)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// change the directory we are using for templates
|
||||||
|
func (self *templateEngine) changeTemplateDir(dirname string) {
|
||||||
|
log.Println("change template directory to", dirname)
|
||||||
|
self.template_dir = dirname
|
||||||
|
self.reloadAllTemplates()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *templateEngine) createNotFoundHandler(prefix, frontend string) (h http.Handler) {
|
||||||
|
h = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
self.renderNotFound(w, r, prefix, frontend, nil)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// default renderer of 404 pages
|
||||||
|
func (self *templateEngine) renderNotFound(wr http.ResponseWriter, r *http.Request, prefix, frontend string, i18n *I18N) {
|
||||||
|
wr.WriteHeader(404)
|
||||||
|
opts := make(map[string]interface{})
|
||||||
|
opts["prefix"] = prefix
|
||||||
|
opts["frontend"] = frontend
|
||||||
|
self.writeTemplate("404", opts, wr, i18n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *templateEngine) findLink(prefix, hash string) (url string) {
|
||||||
|
ents, _ := self.DB.GetCitesByPostHashLike(hash)
|
||||||
|
if len(ents) > 0 {
|
||||||
|
url = fmt.Sprintf("%st/%s/#%s", prefix, HashMessageID(ents[0].Reference()), HashMessageID(ents[0].MessageID()))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderPostForm(prefix, board, op_msg_id string, files, captcha bool, i18n *I18N) string {
|
||||||
|
url := prefix + "post/" + board
|
||||||
|
button := "New Thread"
|
||||||
|
if op_msg_id != "" {
|
||||||
|
button = "Reply"
|
||||||
|
if i18n != nil {
|
||||||
|
b := i18n.Translate("postbutton_reply")
|
||||||
|
if b != "" {
|
||||||
|
button = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if i18n != nil {
|
||||||
|
b := i18n.Translate("postbutton_thread")
|
||||||
|
if b != "" {
|
||||||
|
button = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return template.renderTemplate("postform", map[string]interface{}{"post_url": url, "reference": op_msg_id, "button": button, "files": files, "prefix": prefix, "DisableCaptcha": !captcha}, i18n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate misc graphs
|
||||||
|
func (self *templateEngine) genGraphs(prefix string, wr io.Writer, db Database, i18n *I18N) {
|
||||||
|
|
||||||
|
//
|
||||||
|
// begin gen history.html
|
||||||
|
//
|
||||||
|
|
||||||
|
var all_posts postsGraph
|
||||||
|
// this may take a bit
|
||||||
|
log.Println("getting monthly post history...")
|
||||||
|
posts := db.GetMonthlyPostHistory()
|
||||||
|
|
||||||
|
if posts == nil {
|
||||||
|
// wtf?
|
||||||
|
log.Println("no monthly posts gotten wtfug yo?")
|
||||||
|
} else {
|
||||||
|
for _, entry := range posts {
|
||||||
|
all_posts = append(all_posts, postsGraphRow{
|
||||||
|
day: entry.Time(),
|
||||||
|
Num: entry.Count(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Sort(all_posts)
|
||||||
|
|
||||||
|
_, err := io.WriteString(wr, self.renderTemplate("graph_history", map[string]interface{}{"history": all_posts}, i18n))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error writing history graph", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// end gen history.html
|
||||||
|
//
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *templateEngine) genBoardList(prefix, name string, wr io.Writer, db Database, i18n *I18N) {
|
||||||
|
// the graph for the front page
|
||||||
|
var frontpage_graph boardPageRows
|
||||||
|
|
||||||
|
// for each group
|
||||||
|
groups := db.GetAllNewsgroups()
|
||||||
|
for _, group := range groups {
|
||||||
|
// posts this hour
|
||||||
|
hour := db.CountPostsInGroup(group, 3600)
|
||||||
|
// posts today
|
||||||
|
day := db.CountPostsInGroup(group, 86400)
|
||||||
|
// posts total
|
||||||
|
all := db.CountPostsInGroup(group, 0)
|
||||||
|
frontpage_graph = append(frontpage_graph, boardPageRow{
|
||||||
|
All: all,
|
||||||
|
Day: day,
|
||||||
|
Hour: hour,
|
||||||
|
Board: group,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
param := map[string]interface{}{
|
||||||
|
"prefix": prefix,
|
||||||
|
"frontend": name,
|
||||||
|
}
|
||||||
|
sort.Sort(frontpage_graph)
|
||||||
|
param["graph"] = frontpage_graph
|
||||||
|
_, err := io.WriteString(wr, self.renderTemplate("boardlist", param, i18n))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error writing board list page", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate front page
|
||||||
|
func (self *templateEngine) genFrontPage(top_count int, prefix, frontend_name string, indexwr, boardswr io.Writer, db Database, i18n *I18N) {
|
||||||
|
|
||||||
|
models := db.GetLastPostedPostModels(prefix, 20)
|
||||||
|
|
||||||
|
for idx := range models {
|
||||||
|
models[idx].I18N(i18n)
|
||||||
|
}
|
||||||
|
|
||||||
|
wr := indexwr
|
||||||
|
|
||||||
|
param := make(map[string]interface{})
|
||||||
|
|
||||||
|
param["overview"] = self.renderTemplate("overview", map[string]interface{}{"overview": overviewModel(models)}, i18n)
|
||||||
|
/*
|
||||||
|
sort.Sort(posts_graph)
|
||||||
|
param["postsgraph"] = self.renderTemplate("posts_graph.mustache", map[string]interface{}{"graph": posts_graph})
|
||||||
|
|
||||||
|
if len(frontpage_graph) > top_count {
|
||||||
|
param["boardgraph"] = frontpage_graph[:top_count]
|
||||||
|
} else {
|
||||||
|
param["boardgraph"] = frontpage_graph
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
param["frontend"] = frontend_name
|
||||||
|
param["totalposts"] = db.ArticleCount()
|
||||||
|
param["prefix"] = prefix
|
||||||
|
// render and inject navbar
|
||||||
|
param["navbar"] = self.renderTemplate("navbar", map[string]interface{}{"name": "Front Page", "frontend": frontend_name, "prefix": prefix}, i18n)
|
||||||
|
|
||||||
|
_, err := io.WriteString(wr, self.renderTemplate("frontpage", param, i18n))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error writing front page", err)
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
wr = boardswr
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
29
contrib/backends/srndv2/src/srnd/templates_mustache.go
Normal file
29
contrib/backends/srndv2/src/srnd/templates_mustache.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
// templates.go
|
||||||
|
// template model interfaces
|
||||||
|
//
|
||||||
|
package srnd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cbroglie/mustache"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mustacheDriver struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *mustacheDriver) RenderString(templ string, obj interface{}) (string, error) {
|
||||||
|
return mustache.Render(templ, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *mustacheDriver) Render(templ string, obj interface{}, w io.Writer) error {
|
||||||
|
s, err := d.RenderString(templ, obj)
|
||||||
|
if err == nil {
|
||||||
|
_, err = io.WriteString(w, s)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *mustacheDriver) Ext() string {
|
||||||
|
return ".mustache"
|
||||||
|
}
|
27
contrib/backends/srndv2/src/srnd/templates_std.go
Normal file
27
contrib/backends/srndv2/src/srnd/templates_std.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package srnd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
stdtemplate "html/template"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stdTemplateDriver struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *stdTemplateDriver) Render(templ string, obj interface{}, w io.Writer) error {
|
||||||
|
return stdtemplate.Must(stdtemplate.New("").Parse(templ)).Execute(w, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *stdTemplateDriver) RenderString(templ string, obj interface{}) (string, error) {
|
||||||
|
buff := new(bytes.Buffer)
|
||||||
|
err := d.Render(templ, obj, buff)
|
||||||
|
if err == nil {
|
||||||
|
return buff.String(), nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *stdTemplateDriver) Ext() string {
|
||||||
|
return ".tmpl"
|
||||||
|
}
|
@ -1,45 +0,0 @@
|
|||||||
package srnd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func makeBenchmarkDB() Database {
|
|
||||||
return NewDatabase("postgres", "srnd", "/var/run/postgresql", "", "", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRenderBoardPage(b *testing.B) {
|
|
||||||
db := makeBenchmarkDB()
|
|
||||||
db.CreateTables()
|
|
||||||
defer db.Close()
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
|
||||||
for pb.Next() {
|
|
||||||
wr, err := os.Create("boardpage.html")
|
|
||||||
if err == nil {
|
|
||||||
template.genBoardPage(true, true, "prefix", "test", "overchan.random", 0, wr, db, false, nil)
|
|
||||||
} else {
|
|
||||||
log.Println("did not write", "boardpage.html", err)
|
|
||||||
}
|
|
||||||
wr.Close()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRenderThread(b *testing.B) {
|
|
||||||
db := makeBenchmarkDB()
|
|
||||||
db.CreateTables()
|
|
||||||
defer db.Close()
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
|
||||||
for pb.Next() {
|
|
||||||
wr, err := os.Create("thread.html")
|
|
||||||
if err == nil {
|
|
||||||
template.genThread(true, true, ArticleEntry{"<c49be1451427261@nntp.nsfl.tk>", "overchan.random"}, "prefix", "frontend", wr, db, false, nil)
|
|
||||||
} else {
|
|
||||||
log.Println("did not write", "thread.html", err)
|
|
||||||
}
|
|
||||||
wr.Close()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
Reference in New Issue
Block a user