summaryrefslogtreecommitdiff
path: root/boardvoting.go
diff options
context:
space:
mode:
Diffstat (limited to 'boardvoting.go')
-rw-r--r--boardvoting.go117
1 files changed, 89 insertions, 28 deletions
diff --git a/boardvoting.go b/boardvoting.go
index 1fe031e..c467b40 100644
--- a/boardvoting.go
+++ b/boardvoting.go
@@ -4,8 +4,10 @@ import (
"context"
"crypto/tls"
"crypto/x509"
+ "encoding/base64"
"fmt"
"github.com/Masterminds/sprig"
+ "github.com/gorilla/sessions"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
"gopkg.in/yaml.v2"
@@ -20,17 +22,20 @@ import (
var logger *log.Logger
var config *Config
+var store *sessions.CookieStore
-func getTemplateFilenames(tmpl []string) (result []string) {
- result = make([]string, len(tmpl))
- for i := range tmpl {
- result[i] = fmt.Sprintf("templates/%s", tmpl[i])
+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, tmpl []string, context interface{}) {
- t := template.Must(template.New(tmpl[0]).Funcs(sprig.FuncMap()).ParseFiles(getTemplateFilenames(tmpl)...))
+func renderTemplate(w http.ResponseWriter, templates []string, context interface{}) {
+ t := template.Must(template.New(templates[0]).Funcs(sprig.FuncMap()).ParseFiles(getTemplateFilenames(templates)...))
if err := t.Execute(w, context); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
@@ -76,7 +81,7 @@ type motionParameters struct {
}
type motionListParameters struct {
- Page int64
+ Page int64
Flags struct {
Confirmed, Withdraw, Unvoted bool
}
@@ -85,7 +90,6 @@ type motionListParameters struct {
func parseMotionParameters(r *http.Request) motionParameters {
var m = motionParameters{}
m.ShowVotes, _ = strconv.ParseBool(r.URL.Query().Get("showvotes"))
- logger.Printf("parsed parameters: %+v\n", m)
return m
}
@@ -102,12 +106,16 @@ func parseMotionListParameters(r *http.Request) motionListParameters {
if r.Method == http.MethodPost {
m.Flags.Confirmed, _ = strconv.ParseBool(r.PostFormValue("confirm"))
}
- logger.Printf("parsed parameters: %+v\n", m)
return m
}
func motionListHandler(w http.ResponseWriter, r *http.Request) {
params := parseMotionListParameters(r)
+ session, err := store.Get(r, sessionCookieName)
+ if err != nil {
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
+ }
var templateContext struct {
Decisions []*DecisionForDisplay
@@ -115,12 +123,16 @@ func motionListHandler(w http.ResponseWriter, r *http.Request) {
Params *motionListParameters
PrevPage, NextPage int64
PageTitle string
+ Flashes interface{}
}
if voter, ok := r.Context().Value(ctxVoter).(*Voter); ok {
templateContext.Voter = voter
}
+ if flashes := session.Flashes(); len(flashes) > 0 {
+ templateContext.Flashes = flashes
+ }
+ session.Save(r, w)
templateContext.Params = &params
- var err error
if params.Flags.Unvoted && templateContext.Voter != nil {
if templateContext.Decisions, err = FindVotersUnvotedDecisionsForDisplayOnPage(
@@ -168,6 +180,7 @@ func motionHandler(w http.ResponseWriter, r *http.Request) {
Params *motionParameters
PrevPage, NextPage int64
PageTitle string
+ Flashes interface{}
}
voter, ok := getVoterFromRequest(r)
if ok {
@@ -304,16 +317,61 @@ func (h motionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
})
return
default:
- fmt.Fprintf(w, "No handler for '%s'", subURL)
+ http.NotFound(w, r)
return
}
}
func newMotionHandler(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintln(w, "New motion")
- voter, _ := getVoterFromRequest(r)
- fmt.Fprintf(w, "%+v\n", voter)
- // TODO: implement
+ voter, ok := getVoterFromRequest(r)
+ if !ok {
+ http.Error(w, http.StatusText(http.StatusPreconditionFailed), http.StatusPreconditionFailed)
+ }
+
+ templates := []string{"newmotion_form.html", "header.html", "footer.html"}
+ var templateContext struct {
+ Form NewDecisionForm
+ PageTitle string
+ Voter *Voter
+ Flashes interface{}
+ }
+ switch r.Method {
+ case http.MethodPost:
+ form := NewDecisionForm{
+ Title: r.FormValue("Title"),
+ Content: r.FormValue("Content"),
+ VoteType: r.FormValue("VoteType"),
+ Due: r.FormValue("Due"),
+ }
+
+ if valid, data := form.Validate(); !valid {
+ templateContext.Voter = voter
+ templateContext.Form = form
+ renderTemplate(w, templates, templateContext)
+ } else {
+ if err := CreateMotion(data, voter); err != nil {
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
+ }
+ session, err := store.Get(r, sessionCookieName)
+ if err != nil {
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
+ }
+ session.AddFlash("The motion has been proposed!")
+ session.Save(r, w)
+
+ http.Redirect(w, r, "/motions/", http.StatusTemporaryRedirect)
+ }
+
+ return
+ default:
+ templateContext.Voter = voter
+ templateContext.Form = NewDecisionForm{
+ VoteType: strconv.FormatInt(voteTypeMotion, 10),
+ }
+ renderTemplate(w, templates, templateContext)
+ }
}
type Config struct {
@@ -323,30 +381,33 @@ type Config struct {
ClientCACertificates string `yaml:"client_ca_certificates"`
ServerCert string `yaml:"server_certificate"`
ServerKey string `yaml:"server_key"`
+ CookieSecret string `yaml:"cookie_secret"`
}
-func main() {
+func init() {
logger = log.New(os.Stderr, "boardvoting: ", log.LstdFlags|log.LUTC|log.Lshortfile)
- var filename = "config.yaml"
- if len(os.Args) == 2 {
- filename = os.Args[1]
- }
-
- var err error
-
- var source []byte
-
- source, err = ioutil.ReadFile(filename)
+ source, err := ioutil.ReadFile("config.yaml")
if err != nil {
logger.Fatal(err)
}
- err = yaml.Unmarshal(source, &config)
+ if err := yaml.Unmarshal(source, &config); err != nil {
+ logger.Fatal(err)
+ }
+
+ cookieSecret, err := base64.StdEncoding.DecodeString(config.CookieSecret)
if err != nil {
logger.Fatal(err)
}
- logger.Printf("read configuration %v", config)
+ if len(cookieSecret) < 32 {
+ logger.Fatalln("Cookie secret is less than 32 bytes long")
+ }
+ store = sessions.NewCookieStore(cookieSecret)
+ logger.Println("read configuration")
+}
+func main() {
+ var err error
db, err = sqlx.Open("sqlite3", config.DatabaseFile)
if err != nil {
logger.Fatal(err)