Implement direct voting

debian
Jan Dittberner 7 years ago committed by Jan Dittberner
parent 2cac50ee86
commit 8d1f18e16d

@ -53,6 +53,7 @@ const (
ctxVoter
ctxDecision
ctxVote
ctxAuthenticatedCert
)
func authenticateRequest(w http.ResponseWriter, r *http.Request, handler func(http.ResponseWriter, *http.Request)) {
@ -66,7 +67,9 @@ func authenticateRequest(w http.ResponseWriter, r *http.Request, handler func(ht
return
}
if voter != nil {
handler(w, r.WithContext(context.WithValue(r.Context(), ctxVoter, voter)))
requestContext := context.WithValue(r.Context(), ctxVoter, voter)
requestContext = context.WithValue(requestContext, ctxAuthenticatedCert, cert)
handler(w, r.WithContext(requestContext))
return
}
}
@ -475,12 +478,12 @@ func (h motionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
type voteHandler struct {
type directVoteHandler struct {
FlashMessageAction
authenticationRequiredHandler
}
func (h *voteHandler) Handle(w http.ResponseWriter, r *http.Request) {
func (h *directVoteHandler) Handle(w http.ResponseWriter, r *http.Request) {
decision, ok := getDecisionFromRequest(r)
if !ok {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -496,10 +499,37 @@ func (h *voteHandler) Handle(w http.ResponseWriter, r *http.Request) {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
fmt.Fprintln(w, "to be implemented")
fmt.Fprintln(w, "Decision:", decision)
fmt.Fprintln(w, "Voter:", voter)
fmt.Fprintln(w, "Vote:", vote)
switch r.Method {
case http.MethodPost:
voteResult := &Vote{
VoterId: voter.Id, Vote: vote, DecisionId: decision.Id, Voted: time.Now().UTC(),
Notes: fmt.Sprintf("Direct Vote\n\n%s", getPEMClientCert(r))}
if err := voteResult.Save(); err != nil {
logger.Println("ERROR", "Problem saving vote:", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
NotifyMailChannel <- NewNotificationDirectVote(&decision.Decision, voter, voteResult)
if err := h.AddFlash(w, r, "Your vote has been registered."); err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/motions/", http.StatusMovedPermanently)
default:
templates := []string{"direct_vote_form.html", "header.html", "footer.html", "motion_fragments.html"}
var templateContext struct {
Decision *DecisionForDisplay
VoteChoice VoteChoice
PageTitle string
Flashes interface{}
}
templateContext.Decision = decision
templateContext.VoteChoice = vote
renderTemplate(w, templates, templateContext)
}
}
type proxyVoteHandler struct {
@ -509,7 +539,8 @@ type proxyVoteHandler struct {
func getPEMClientCert(r *http.Request) string {
clientCertPEM := bytes.NewBufferString("")
pem.Encode(clientCertPEM, &pem.Block{Type: "CERTIFICATE", Bytes: r.TLS.PeerCertificates[0].Raw})
authenticatedCertificate := r.Context().Value(ctxAuthenticatedCert).(*x509.Certificate)
pem.Encode(clientCertPEM, &pem.Block{Type: "CERTIFICATE", Bytes: authenticatedCertificate.Raw})
return clientCertPEM.String()
}
@ -604,13 +635,17 @@ func (h *decisionVoteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
})
case strings.HasPrefix(r.URL.Path, "/vote/"):
parts := strings.Split(r.URL.Path[len("/vote/"):], "/")
if len(parts) != 2 {
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
motionTag := parts[0]
voteValue, ok := VoteValues[parts[1]]
if !ok {
http.NotFound(w, r)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
handler := &voteHandler{}
handler := &directVoteHandler{}
authenticateRequest(
w, r.WithContext(context.WithValue(r.Context(), ctxNeedsAuth, true)),
func(w http.ResponseWriter, r *http.Request) {

@ -113,7 +113,7 @@ type NotificationCreateMotion struct {
}
func (n *NotificationCreateMotion) GetData() interface{} {
voteURL := fmt.Sprintf("%s/vote", config.BaseURL)
voteURL := fmt.Sprintf("%s/vote/%s", config.BaseURL, n.decision.Tag)
unvotedURL := fmt.Sprintf("%s/motions/?unvoted=1", config.BaseURL)
return struct {
*Decision
@ -146,7 +146,7 @@ func NewNotificationUpdateMotion(decision Decision, voter Voter) *NotificationUp
}
func (n *NotificationUpdateMotion) GetData() interface{} {
voteURL := fmt.Sprintf("%s/vote", config.BaseURL)
voteURL := fmt.Sprintf("%s/vote/%s", config.BaseURL, n.decision.Tag)
unvotedURL := fmt.Sprintf("%s/motions/?unvoted=1", config.BaseURL)
return struct {
*Decision
@ -250,3 +250,30 @@ func (n *NotificationProxyVote) GetTemplate() string { return "proxy_vote_mail.t
func (n *NotificationProxyVote) GetSubject() string {
return fmt.Sprintf("Re: %s - %s", n.decision.Tag, n.decision.Title)
}
type NotificationDirectVote struct {
voteNotificationBase
decisionReplyBase
voter Voter
vote Vote
}
func NewNotificationDirectVote(decision *Decision, voter *Voter, vote *Vote) *NotificationDirectVote {
notification := &NotificationDirectVote{voter: *voter, vote: *vote}
notification.decision = *decision
return notification
}
func (n *NotificationDirectVote) GetData() interface{} {
return struct {
Vote VoteChoice
Voter string
Decision *Decision
}{n.vote.Vote, n.voter.Name, &n.decision}
}
func (n *NotificationDirectVote) GetTemplate() string { return "direct_vote_mail.txt" }
func (n *NotificationDirectVote) GetSubject() string {
return fmt.Sprintf("Re: %s - %s", n.decision.Tag, n.decision.Title)
}

@ -0,0 +1,22 @@
{{ template "header" . }}
<a href="/motions/">Show all votes</a>
<table class="list">
<thead>
<tr>
<th>Status</th>
<th>Motion</th>
</tr>
</thead>
<tbody>
<tr>
{{ with .Decision }}
{{ template "motion_fragment" .}}
{{ end}}
</tr>
</tbody>
</table>
<form action="/vote/{{ .Decision.Tag }}/{{ .VoteChoice }}" method="post">
<input type="submit" value="Vote {{ .VoteChoice }}"/>
</form>
{{ template "footer" . }}

@ -0,0 +1,10 @@
Dear Board,
{{ .Voter }} has just voted {{ .Vote }} on motion {{ .Decision.Tag }}.
Motion:
{{ .Decision.Title }}
{{ .Decision.Content }}
Kind regards,
the vote system
Loading…
Cancel
Save