diff --git a/boardvoting.go b/boardvoting.go
index c1fa28f..a0d6359 100644
--- a/boardvoting.go
+++ b/boardvoting.go
@@ -10,7 +10,7 @@ import (
"encoding/pem"
"flag"
"fmt"
-
+ "git.cacert.org/cacert-boardvoting/boardvoting"
"github.com/Masterminds/sprig"
"github.com/gorilla/sessions"
_ "github.com/mattn/go-sqlite3"
@@ -35,21 +35,31 @@ var log *logging.Logger
const sessionCookieName = "votesession"
-func getTemplateFilenames(templates []string) (result []string) {
- result = make([]string, len(templates))
- for i := range templates {
- result[i] = fmt.Sprintf("templates/%s", templates[i])
- }
- return result
-}
-
func renderTemplate(w http.ResponseWriter, templates []string, context interface{}) {
funcMaps := sprig.FuncMap()
funcMaps["nl2br"] = func(text string) template.HTML {
return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "
", -1))
}
- t := template.Must(template.New(templates[0]).Funcs(funcMaps).ParseFiles(getTemplateFilenames(templates)...))
- if err := t.Execute(w, context); err != nil {
+
+ var baseTemplate *template.Template
+
+ for count, t := range templates {
+ if assetBytes, err := boardvoting.Asset(fmt.Sprintf("templates/%s", t)); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ } else {
+ if count == 0 {
+ if baseTemplate, err = template.New(t).Funcs(funcMaps).Parse(string(assetBytes)); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+ } else {
+ if _, err := baseTemplate.New(t).Funcs(funcMaps).Parse(string(assetBytes)); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+ }
+ }
+ }
+
+ if err := baseTemplate.Execute(w, context); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
@@ -733,7 +743,7 @@ func readConfig() {
}
func setupDbConfig(ctx context.Context) {
- database, err := sql.Open("sqlite3", config.DatabaseFile)
+ database, err := sql.Open("sqlite3", config.DatabaseFile)
if err != nil {
log.Panicf("Opening database failed: %v", err)
}
@@ -777,7 +787,7 @@ func setupHandlers() {
http.Handle("/newmotion/", motionsHandler{})
http.Handle("/proxy/", &decisionVoteHandler{})
http.Handle("/vote/", &decisionVoteHandler{})
- http.Handle("/static/", http.FileServer(http.Dir(".")))
+ http.Handle("/static/", http.FileServer(boardvoting.GetAssetFS()))
http.Handle("/", http.RedirectHandler("/motions/", http.StatusMovedPermanently))
}
diff --git a/boardvoting/main.go b/boardvoting/main.go
index 5c8e6eb..7cc6005 100644
--- a/boardvoting/main.go
+++ b/boardvoting/main.go
@@ -1,3 +1,143 @@
package boardvoting
//go:generate go-bindata -pkg $GOPACKAGE -o assets.go ./migrations/... ./static/... ./templates/...
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+)
+
+var defaultFileTimestamp = time.Now()
+
+type AssetFS struct {
+ Asset func(path string) ([]byte, error)
+ AssetDir func(path string) ([]string, error)
+ AssetInfo func(path string) (os.FileInfo, error)
+}
+
+type FakeFile struct {
+ Path string
+ Dir bool
+ Len int64
+ Timestamp time.Time
+}
+
+func (f *FakeFile) Name() string {
+ _, name := filepath.Split(f.Path)
+ return name
+}
+
+func (f *FakeFile) Size() int64 { return f.Len }
+
+func (f *FakeFile) Mode() os.FileMode {
+ mode := os.FileMode(0644)
+ if f.Dir {
+ return mode | os.ModeDir
+ }
+ return mode
+}
+
+func (f *FakeFile) ModTime() time.Time { return f.Timestamp }
+func (f *FakeFile) IsDir() bool { return f.Dir }
+func (f *FakeFile) Sys() interface{} { return nil }
+
+type AssetFile struct {
+ *bytes.Reader
+ io.Closer
+ FakeFile
+}
+
+func NewAssetFile(name string, content []byte, timestamp time.Time) *AssetFile {
+ if timestamp.IsZero() {
+ timestamp = defaultFileTimestamp
+ }
+ return &AssetFile{
+ bytes.NewReader(content),
+ ioutil.NopCloser(nil),
+ FakeFile{name, false, int64(len(content)), timestamp},
+ }
+}
+
+func (f *AssetFile) Readdir(count int) ([]os.FileInfo, error) {
+ return nil, errors.New("not a directory")
+}
+
+func (f *AssetFile) Size() int64 {
+ return f.FakeFile.Size()
+}
+
+func (f *AssetFile) Stat() (os.FileInfo, error) {
+ return f, nil
+}
+
+type AssetDirectory struct {
+ AssetFile
+ ChildrenRead int
+ Children []os.FileInfo
+}
+
+func (f *AssetDirectory) Readdir(count int) ([]os.FileInfo, error) {
+ if count <= 0 {
+ return f.Children, nil
+ }
+ if f.ChildrenRead+count > len(f.Children) {
+ count = len(f.Children) - f.ChildrenRead
+ }
+ rv := f.Children[f.ChildrenRead : f.ChildrenRead+count]
+ f.ChildrenRead += count
+ return rv, nil
+}
+
+func (f *AssetDirectory) Stat() (os.FileInfo, error) {
+ return f, nil
+}
+
+func NewAssetDirectory(name string, children []string, fs *AssetFS) *AssetDirectory {
+ fileInfos := make([]os.FileInfo, 0, len(children))
+ for _, child := range children {
+ _, err := fs.AssetDir(filepath.Join(name, child))
+ fileInfos = append(fileInfos, &FakeFile{child, err == nil, 0, time.Time{}})
+ }
+ return &AssetDirectory{
+ AssetFile{
+ bytes.NewReader(nil),
+ ioutil.NopCloser(nil),
+ FakeFile{name, true, 0, time.Time{}},
+ },
+ 0,
+ fileInfos}
+}
+
+func (f *AssetFS) Open(name string) (http.File, error) {
+ if len(name) > 0 && name[0] == '/' {
+ name = name[1:]
+ }
+ if b, err := f.Asset(name); err == nil {
+ timestamp := defaultFileTimestamp
+ if f.AssetInfo != nil {
+ if info, err := f.AssetInfo(name); err == nil {
+ timestamp = info.ModTime()
+ }
+ }
+ return NewAssetFile(name, b, timestamp), nil
+ }
+ if children, err := f.AssetDir(name); err == nil {
+ return NewAssetDirectory(name, children, f), nil
+ } else {
+ if strings.Contains(err.Error(), "not found") {
+ return nil, os.ErrNotExist
+ }
+ return nil, err
+ }
+}
+
+func GetAssetFS() *AssetFS {
+ return &AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: AssetInfo}
+}
diff --git a/boardvoting/templates/create_motion_form.html b/boardvoting/templates/create_motion_form.html
index b3cbadb..5958aa7 100644
--- a/boardvoting/templates/create_motion_form.html
+++ b/boardvoting/templates/create_motion_form.html
@@ -1,4 +1,4 @@
-{{ template "header" . }}
+{{ template "header.html" . }}