Skip to content
page_vote.go 4.03 KiB
Newer Older
Lo^2's avatar
wip
Lo^2 committed
package page_vote

import (
Lo^2's avatar
Lo^2 committed
	"code.electrolab.fr/it/vote.electrolab.fr/app/server"
Lo^2's avatar
Lo^2 committed
	"code.electrolab.fr/it/vote.electrolab.fr/app/server/pages/common"
Lo^2's avatar
Lo^2 committed
	"code.electrolab.fr/it/vote.electrolab.fr/model"
Lo^2's avatar
Lo^2 committed
	"code.electrolab.fr/it/vote.electrolab.fr/service/db"
Lo^2's avatar
wip
Lo^2 committed
	"context"
Lo^2's avatar
Lo^2 committed
	"fmt"
	"github.com/lib/pq"
Lo^2's avatar
wip
Lo^2 committed
	"log"
	"net/http"
	"strings"
)

const (
	FORM_VOTER_PRIVATE_TOKEN = "private_token"
)

var (
Lo^2's avatar
Lo^2 committed
	template = common.MustParseTemplate(TEMPLATE_STR)
Lo^2's avatar
wip
Lo^2 committed
)

Lo^2's avatar
Lo^2 committed
func RegisterRoutes(mux *http.ServeMux, app *server.App) {
Lo^2's avatar
Lo^2 committed
	mux.Handle("/events/Electrolab-AG-2021/votes/", server.NewAppHandler(app, handlePage))
Lo^2's avatar
wip
Lo^2 committed
}

Lo^2's avatar
Lo^2 committed
func handlePage(app *server.App, ctx context.Context, w http.ResponseWriter, r *http.Request) {
Lo^2's avatar
wip
Lo^2 committed
	var (
Lo^2's avatar
Lo^2 committed
		err          error
Lo^2's avatar
wip
Lo^2 committed
		templateVars = new(TemplateVars)
	)

	pathParts := strings.Split(r.URL.Path, "/")
Lo^2's avatar
Lo^2 committed
	if len(pathParts) != 5 {
Lo^2's avatar
wip
Lo^2 committed
		w.WriteHeader(http.StatusNotFound)
		return
	}

Lo^2's avatar
Lo^2 committed
	templateVars.User, err = model.FetchUser(app.Context, app.Service.DB, r.FormValue("private_token"))
Lo^2's avatar
Lo^2 committed
	if err != nil {
Lo^2's avatar
Lo^2 committed
		// Query has an error
		log.Printf("Query error: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	if templateVars.User == nil {
Lo^2's avatar
Lo^2 committed
		// Token is not valid
		w.WriteHeader(http.StatusForbidden)
Lo^2's avatar
Lo^2 committed
		fmt.Fprintf(w, "Invalid token")
Lo^2's avatar
Lo^2 committed
		return
	}

Lo^2's avatar
Lo^2 committed
	questionName := pathParts[4]
Lo^2's avatar
Lo^2 committed
	templateVars.Question, err = model.FetchQuestion(app.Context, app.Service.DB, templateVars.User.Uuid, questionName)
Lo^2's avatar
wip
Lo^2 committed
	if err != nil {
Lo^2's avatar
Lo^2 committed
		// Query has an error
		log.Printf("Query error 1: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	if templateVars.Question == nil {
		// Question is not found
		// w.Header().Set("Location", "/votes")
		w.WriteHeader(http.StatusNotFound)
		return
Lo^2's avatar
wip
Lo^2 committed
	}

Lo^2's avatar
Lo^2 committed
	templateVars.PageTitle = "Vote Électrolab - " + templateVars.Question.Title

Lo^2's avatar
Lo^2 committed
	if r.Method == http.MethodPost {
Lo^2's avatar
Lo^2 committed
		switch r.FormValue("action") {
		case "openclose":
			if !templateVars.User.Admin {
				w.WriteHeader(http.StatusForbidden)
				fmt.Fprintf(w, "User is not admin")
				return
			}
			err := doOpenClose(ctx, app.Service.DB, templateVars.Question.Name, r.FormValue("open") == "true")
			if err != nil {
				templateVars.Error = err.Error()
			} else {
				// Vote is a success, redirect to the page as GET
				w.Header().Set("Location", r.URL.String())
				w.WriteHeader(http.StatusMovedPermanently)
				return
			}
		case "vote":
			err := doVote(ctx, app.Service.DB, templateVars.Question.Name, templateVars.User.PrivateToken, r.Form["answer"])
			if err != nil {
				templateVars.VoteError = err.Error()
			} else {
				// Vote is a success, redirect to the page as GET
				w.Header().Set("Location", r.URL.String())
				w.WriteHeader(http.StatusMovedPermanently)
				return
			}
Lo^2's avatar
Lo^2 committed
		}
	}

Lo^2's avatar
Lo^2 committed
	if templateVars.Question.VoteOpen || templateVars.Question.VoteCount == 0 {
Lo^2's avatar
Lo^2 committed
		model.ShuffleAnswers(templateVars.Question.Answers)
Lo^2's avatar
wip
Lo^2 committed
	}

Lo^2's avatar
Lo^2 committed
	err = template.Execute(w, templateVars)
Lo^2's avatar
wip
Lo^2 committed
	if err != nil {
		// Template has an error
		log.Printf("Template execution error: %s", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	return
}

Lo^2's avatar
Lo^2 committed
func doVote(ctx context.Context, db db.Service, questionName, userPrivateToken string, answers []string) error {
Lo^2's avatar
Lo^2 committed
	var voteErrorMsg string

Lo^2's avatar
Lo^2 committed
	err := db.QueryRowContext(ctx,
Lo^2's avatar
Lo^2 committed
		"SELECT COALESCE(vote($1, $2, $3), '')",
Lo^2's avatar
Lo^2 committed
		questionName,
		userPrivateToken,
		pq.StringArray(answers),
		// pq.StringArray(r.Form["answer"]),
Lo^2's avatar
Lo^2 committed
	).Scan(&voteErrorMsg)

	if err != nil {
		return fmt.Errorf("SQL query error: %s", err.Error())
	}

	if len(voteErrorMsg) > 0 {
		return fmt.Errorf("%s", voteErrorMsg)
	}

	return nil
}
Lo^2's avatar
Lo^2 committed

func doOpenClose(ctx context.Context, db db.Service, questionName string, isOpen bool) error {
	q := `
		UPDATE questions
		SET vote_open = $2
		WHERE name = $1
	`

	result, err := db.ExecContext(ctx, q, questionName, isOpen)
	if err != nil {
		return fmt.Errorf("SQL query error: %s", err.Error())
	}
	rowsAffected, _ := result.RowsAffected()
	if rowsAffected == 0 {
		return fmt.Errorf("Question not found: %s", questionName)
	}
	if rowsAffected > 1 {
		return fmt.Errorf("Multiple question not found, should not happen: %s", questionName)
	}

	return nil
}