Implement motion editing
[cacert-boardvoting.git] / boardvoting.go
index 367179f..efa9ecc 100644 (file)
@@ -47,8 +47,8 @@ type contextKey int
 
 const (
        ctxNeedsAuth contextKey = iota
-       ctxVoter     contextKey = iota
-       ctxDecision  contextKey = iota
+       ctxVoter
+       ctxDecision
 )
 
 func authenticateRequest(w http.ResponseWriter, r *http.Request, handler func(http.ResponseWriter, *http.Request)) {
@@ -260,82 +260,83 @@ type editMotionAction struct {
        authenticationRequiredHandler
 }
 
-func (editMotionAction) Handle(w http.ResponseWriter, r *http.Request) {
-       decision, ok := getDecisionFromRequest(r)
-       if !ok || decision.Status != voteStatusPending {
+func newMotionHandler(w http.ResponseWriter, r *http.Request) {
+       voter, ok := getVoterFromRequest(r)
+       if !ok {
                http.Error(w, http.StatusText(http.StatusPreconditionFailed), http.StatusPreconditionFailed)
-               return
        }
-       fmt.Fprintln(w, "Edit motion", decision.Tag)
-       // TODO: implement
-}
-
-type motionsHandler struct{}
 
-func (h motionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-       if err := db.Ping(); err != nil {
-               logger.Fatal(err)
+       templates := []string{"create_motion_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"),
+               }
 
-       subURL := r.URL.Path
-
-       var motionActionMap = map[string]motionActionHandler{
-               "withdraw": withDrawMotionAction{},
-               "edit":     editMotionAction{},
-       }
+               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)
 
-       switch {
-       case subURL == "":
-               authenticateRequest(w, r.WithContext(context.WithValue(r.Context(), ctxNeedsAuth, false)), motionListHandler)
-               return
-       case strings.Count(subURL, "/") == 1:
-               parts := strings.Split(subURL, "/")
-               logger.Printf("handle %v\n", parts)
-               motionTag := parts[0]
-               action, ok := motionActionMap[parts[1]]
-               if !ok {
-                       http.NotFound(w, r)
-                       return
+                       http.Redirect(w, r, "/motions/", http.StatusMovedPermanently)
                }
-               authenticateRequest(
-                       w, r.WithContext(context.WithValue(r.Context(), ctxNeedsAuth, action.NeedsAuth())),
-                       func(w http.ResponseWriter, r *http.Request) {
-                               singleDecisionHandler(w, r, motionTag, action.Handle)
-                       })
-               logger.Printf("motion: %s, action: %s\n", motionTag, action)
-               return
-       case strings.Count(subURL, "/") == 0:
-               authenticateRequest(w, r.WithContext(context.WithValue(r.Context(), ctxNeedsAuth, false)),
-                       func(w http.ResponseWriter, r *http.Request) {
-                               singleDecisionHandler(w, r, subURL, motionHandler)
-                       })
+
                return
        default:
-               http.NotFound(w, r)
-               return
+               templateContext.Voter = voter
+               templateContext.Form = NewDecisionForm{
+                       VoteType: strconv.FormatInt(voteTypeMotion, 10),
+               }
+               renderTemplate(w, templates, templateContext)
        }
 }
 
-func newMotionHandler(w http.ResponseWriter, r *http.Request) {
+func (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)
+               return
+       }
        voter, ok := getVoterFromRequest(r)
        if !ok {
                http.Error(w, http.StatusText(http.StatusPreconditionFailed), http.StatusPreconditionFailed)
        }
-
-       templates := []string{"newmotion_form.html", "header.html", "footer.html"}
+       templates := []string{"edit_motion_form.html", "header.html", "footer.html"}
        var templateContext struct {
-               Form      NewDecisionForm
+               Form      EditDecisionForm
                PageTitle string
                Voter     *Voter
                Flashes   interface{}
        }
        switch r.Method {
        case http.MethodPost:
-               form := NewDecisionForm{
+               form := EditDecisionForm{
                        Title:    r.FormValue("Title"),
                        Content:  r.FormValue("Content"),
                        VoteType: r.FormValue("VoteType"),
                        Due:      r.FormValue("Due"),
+                       Decision: &decision.Decision,
                }
 
                if valid, data := form.Validate(); !valid {
@@ -343,7 +344,7 @@ func newMotionHandler(w http.ResponseWriter, r *http.Request) {
                        templateContext.Form = form
                        renderTemplate(w, templates, templateContext)
                } else {
-                       if err := CreateMotion(data, voter); err != nil {
+                       if err := UpdateMotion(data, voter); err != nil {
                                http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
                                return
                        }
@@ -352,7 +353,7 @@ func newMotionHandler(w http.ResponseWriter, r *http.Request) {
                                http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
                                return
                        }
-                       session.AddFlash("The motion has been proposed!")
+                       session.AddFlash("The motion has been modified!")
                        session.Save(r, w)
 
                        http.Redirect(w, r, "/motions/", http.StatusMovedPermanently)
@@ -361,13 +362,60 @@ func newMotionHandler(w http.ResponseWriter, r *http.Request) {
                return
        default:
                templateContext.Voter = voter
-               templateContext.Form = NewDecisionForm{
-                       VoteType: strconv.FormatInt(voteTypeMotion, 10),
+               templateContext.Form = EditDecisionForm{
+                       Title:    decision.Title,
+                       Content:  decision.Content,
+                       VoteType: fmt.Sprintf("%d", decision.VoteType),
+                       Decision: &decision.Decision,
                }
                renderTemplate(w, templates, templateContext)
        }
 }
 
+type motionsHandler struct{}
+
+func (h motionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+       if err := db.Ping(); err != nil {
+               logger.Fatal(err)
+       }
+
+       subURL := r.URL.Path
+
+       var motionActionMap = map[string]motionActionHandler{
+               "withdraw": withDrawMotionAction{},
+               "edit":     editMotionAction{},
+       }
+
+       switch {
+       case subURL == "":
+               authenticateRequest(w, r.WithContext(context.WithValue(r.Context(), ctxNeedsAuth, false)), motionListHandler)
+               return
+       case strings.Count(subURL, "/") == 1:
+               parts := strings.Split(subURL, "/")
+               motionTag := parts[0]
+               action, ok := motionActionMap[parts[1]]
+               if !ok {
+                       http.NotFound(w, r)
+                       return
+               }
+               authenticateRequest(
+                       w, r.WithContext(context.WithValue(r.Context(), ctxNeedsAuth, action.NeedsAuth())),
+                       func(w http.ResponseWriter, r *http.Request) {
+                               singleDecisionHandler(w, r, motionTag, action.Handle)
+                       })
+               return
+       case strings.Count(subURL, "/") == 0:
+               authenticateRequest(w, r.WithContext(context.WithValue(r.Context(), ctxNeedsAuth, false)),
+                       func(w http.ResponseWriter, r *http.Request) {
+                               singleDecisionHandler(w, r, subURL, motionHandler)
+                       })
+               return
+       default:
+               http.NotFound(w, r)
+               return
+       }
+}
+
 type Config struct {
        BoardMailAddress     string `yaml:"board_mail_address"`
        NoticeSenderAddress  string `yaml:"notice_sender_address"`
@@ -377,6 +425,10 @@ type Config struct {
        ServerKey            string `yaml:"server_key"`
        CookieSecret         string `yaml:"cookie_secret"`
        BaseURL              string `yaml:"base_url"`
+       MailServer           struct {
+               Host string `yaml:"host"`
+               Port int `yaml:"port"`
+       } `yaml:"mail_server"`
 }
 
 func init() {