summaryrefslogtreecommitdiff
path: root/boardvoting.go
diff options
context:
space:
mode:
Diffstat (limited to 'boardvoting.go')
-rw-r--r--boardvoting.go117
1 files changed, 82 insertions, 35 deletions
diff --git a/boardvoting.go b/boardvoting.go
index efa9ecc..187a867 100644
--- a/boardvoting.go
+++ b/boardvoting.go
@@ -18,6 +18,7 @@ import (
"os"
"strconv"
"strings"
+ "time"
)
var logger *log.Logger
@@ -216,10 +217,6 @@ func (authenticationRequiredHandler) NeedsAuth() bool {
return true
}
-type withDrawMotionAction struct {
- authenticationRequiredHandler
-}
-
func getVoterFromRequest(r *http.Request) (voter *Voter, ok bool) {
voter, ok = r.Context().Value(ctxVoter).(*Voter)
return
@@ -230,37 +227,71 @@ func getDecisionFromRequest(r *http.Request) (decision *DecisionForDisplay, ok b
return
}
-func (withDrawMotionAction) Handle(w http.ResponseWriter, r *http.Request) {
- voter, voter_ok := getVoterFromRequest(r)
- decision, decision_ok := getDecisionFromRequest(r)
+type FlashMessageAction struct{}
- if !voter_ok || !decision_ok || decision.Status != voteStatusPending {
+func (a *FlashMessageAction) AddFlash(w http.ResponseWriter, r *http.Request, message interface{}, tags ...string) (err error) {
+ session, err := store.Get(r, sessionCookieName)
+ if err != nil {
+ logger.Println("ERROR getting session:", err)
+ return
+ }
+ session.AddFlash(message, tags...)
+ session.Save(r, w)
+ if err != nil {
+ logger.Println("ERROR saving session:", err)
+ return
+ }
+ return
+}
+
+type withDrawMotionAction struct {
+ FlashMessageAction
+ authenticationRequiredHandler
+}
+
+func (a *withDrawMotionAction) Handle(w http.ResponseWriter, r *http.Request) {
+ decision, ok := getDecisionFromRequest(r)
+ if !ok || decision.Status != voteStatusPending {
+ http.Error(w, http.StatusText(http.StatusPreconditionFailed), http.StatusPreconditionFailed)
+ return
+ }
+ voter, ok := getVoterFromRequest(r)
+ if !ok {
http.Error(w, http.StatusText(http.StatusPreconditionFailed), http.StatusPreconditionFailed)
return
}
+ templates := []string{"withdraw_motion_form.html", "header.html", "footer.html", "motion_fragments.html"}
+ var templateContext struct {
+ PageTitle string
+ Decision *DecisionForDisplay
+ Flashes interface{}
+ }
switch r.Method {
case http.MethodPost:
- if confirm, err := strconv.ParseBool(r.PostFormValue("confirm")); err != nil {
- log.Println("could not parse confirm parameter:", err)
- http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
- } else if confirm {
- WithdrawMotion(&decision.Decision, voter)
- } else {
- http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
+ decision.Status = voteStatusWithdrawn
+ decision.Modified = time.Now().UTC()
+ if err := WithdrawMotion(&decision.Decision, voter); err != nil {
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
+ }
+ if err := a.AddFlash(w, r, fmt.Sprintf("Motion %s has been withdrawn!", decision.Tag)); err != nil {
+ http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+ return
}
http.Redirect(w, r, "/motions/", http.StatusTemporaryRedirect)
return
default:
- fmt.Fprintln(w, "Withdraw motion", decision.Tag)
+ templateContext.Decision = decision
+ renderTemplate(w, templates, templateContext)
}
}
-type editMotionAction struct {
- authenticationRequiredHandler
+type newMotionHandler struct {
+ FlashMessageAction
}
-func newMotionHandler(w http.ResponseWriter, r *http.Request) {
+func (h *newMotionHandler) Handle(w http.ResponseWriter, r *http.Request) {
voter, ok := getVoterFromRequest(r)
if !ok {
http.Error(w, http.StatusText(http.StatusPreconditionFailed), http.StatusPreconditionFailed)
@@ -287,17 +318,15 @@ func newMotionHandler(w http.ResponseWriter, r *http.Request) {
templateContext.Form = form
renderTemplate(w, templates, templateContext)
} else {
+ data.Proposed = time.Now().UTC()
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 {
+ if err := h.AddFlash(w, r, "The motion has been proposed!"); 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.StatusMovedPermanently)
}
@@ -312,7 +341,12 @@ func newMotionHandler(w http.ResponseWriter, r *http.Request) {
}
}
-func (editMotionAction) Handle(w http.ResponseWriter, r *http.Request) {
+type editMotionAction struct {
+ FlashMessageAction
+ authenticationRequiredHandler
+}
+
+func (a editMotionAction) Handle(w http.ResponseWriter, r *http.Request) {
decision, ok := getDecisionFromRequest(r)
if !ok || decision.Status != voteStatusPending {
http.Error(w, http.StatusText(http.StatusPreconditionFailed), http.StatusPreconditionFailed)
@@ -321,6 +355,7 @@ func (editMotionAction) Handle(w http.ResponseWriter, r *http.Request) {
voter, ok := getVoterFromRequest(r)
if !ok {
http.Error(w, http.StatusText(http.StatusPreconditionFailed), http.StatusPreconditionFailed)
+ return
}
templates := []string{"edit_motion_form.html", "header.html", "footer.html"}
var templateContext struct {
@@ -344,21 +379,18 @@ func (editMotionAction) Handle(w http.ResponseWriter, r *http.Request) {
templateContext.Form = form
renderTemplate(w, templates, templateContext)
} else {
+ data.Modified = time.Now().UTC()
if err := UpdateMotion(data, voter); err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- session, err := store.Get(r, sessionCookieName)
- if err != nil {
+ if err := a.AddFlash(w, r, "The motion has been modified!"); err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- session.AddFlash("The motion has been modified!")
- session.Save(r, w)
http.Redirect(w, r, "/motions/", http.StatusMovedPermanently)
}
-
return
default:
templateContext.Voter = voter
@@ -382,14 +414,20 @@ func (h motionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
subURL := r.URL.Path
var motionActionMap = map[string]motionActionHandler{
- "withdraw": withDrawMotionAction{},
- "edit": editMotionAction{},
+ "withdraw": &withDrawMotionAction{},
+ "edit": &editMotionAction{},
}
switch {
case subURL == "":
authenticateRequest(w, r.WithContext(context.WithValue(r.Context(), ctxNeedsAuth, false)), motionListHandler)
return
+ case subURL == "/newmotion/":
+ handler := &newMotionHandler{}
+ authenticateRequest(
+ w, r.WithContext(context.WithValue(r.Context(), ctxNeedsAuth, true)),
+ handler.Handle)
+ return
case strings.Count(subURL, "/") == 1:
parts := strings.Split(subURL, "/")
motionTag := parts[0]
@@ -427,7 +465,7 @@ type Config struct {
BaseURL string `yaml:"base_url"`
MailServer struct {
Host string `yaml:"host"`
- Port int `yaml:"port"`
+ Port int `yaml:"port"`
} `yaml:"mail_server"`
}
@@ -465,9 +503,7 @@ func main() {
defer db.Close()
http.Handle("/motions/", http.StripPrefix("/motions/", motionsHandler{}))
- http.HandleFunc("/newmotion/", func(w http.ResponseWriter, r *http.Request) {
- authenticateRequest(w, r.WithContext(context.WithValue(r.Context(), ctxNeedsAuth, true)), newMotionHandler)
- })
+ http.Handle("/newmotion/", motionsHandler{})
http.Handle("/static/", http.FileServer(http.Dir(".")))
http.Handle("/", http.RedirectHandler("/motions/", http.StatusMovedPermanently))
@@ -495,7 +531,18 @@ func main() {
logger.Printf("Launching application on https://localhost%s/\n", server.Addr)
+ errs := make(chan error, 1)
+ go func() {
+ if err := http.ListenAndServe(":8080", http.RedirectHandler(config.BaseURL, http.StatusMovedPermanently)); err != nil {
+ errs <- err
+ }
+ close(errs)
+ }()
+
if err = server.ListenAndServeTLS(config.ServerCert, config.ServerKey); err != nil {
logger.Fatal("ListenAndServerTLS: ", err)
}
+ if err := <-errs; err != nil {
+ logger.Fatal("ListenAndServe: ", err)
+ }
}