diff options
Diffstat (limited to 'boardvoting.go')
-rw-r--r-- | boardvoting.go | 117 |
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 = ¶ms - 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) |