Implement new motion form
- add session handler - add form decoder from go-playground - implement custom decoder for VoteTypemain
parent
47af34f1cd
commit
e1af6876c1
@ -0,0 +1,3 @@
|
|||||||
|
-- drop sessions table for server side session storage
|
||||||
|
DROP INDEX session_expiry_idx;
|
||||||
|
DROP TABLE sessions;
|
@ -0,0 +1,8 @@
|
|||||||
|
-- add sessions table for server side session storage
|
||||||
|
CREATE TABLE sessions
|
||||||
|
(
|
||||||
|
token char(43) PRIMARY KEY,
|
||||||
|
data BLOB NOT NULL,
|
||||||
|
expiry TIMESTAMP NOT NULL
|
||||||
|
);
|
||||||
|
CREATE INDEX session_expiry_idx ON sessions (expiry);
|
@ -0,0 +1,62 @@
|
|||||||
|
package validator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Validator struct {
|
||||||
|
FieldErrors map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) Valid() bool {
|
||||||
|
return len(v.FieldErrors) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) AddFieldError(key, message string) {
|
||||||
|
if v.FieldErrors == nil {
|
||||||
|
v.FieldErrors = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := v.FieldErrors[key]; !exists {
|
||||||
|
v.FieldErrors[key] = message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) CheckField(ok bool, key, message string) {
|
||||||
|
if !ok {
|
||||||
|
v.AddFieldError(key, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NotBlank(value string) bool {
|
||||||
|
return strings.TrimSpace(value) != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func MaxChars(value string, n int) bool {
|
||||||
|
return utf8.RuneCountInString(value) <= n
|
||||||
|
}
|
||||||
|
|
||||||
|
func MinChars(value string, n int) bool {
|
||||||
|
return utf8.RuneCountInString(value) >= n
|
||||||
|
}
|
||||||
|
|
||||||
|
func PermittedInt(value int, permittedValues ...int) bool {
|
||||||
|
for i := range permittedValues {
|
||||||
|
if value == permittedValues[i] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func PermittedStr(value string, permittedValues ...string) bool {
|
||||||
|
for i := range permittedValues {
|
||||||
|
if value == permittedValues[i] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
{{ define "title" }}CAcert Board Decisions: New motion{{ end }}
|
||||||
|
|
||||||
|
{{ define "main" }}
|
||||||
|
<div class="ui raised segment">
|
||||||
|
<form action="/newmotion/" method="post">
|
||||||
|
{{ csrfField .Request }}
|
||||||
|
<div class="ui form{{ if .Form.FieldErrors }} error{{ end }}">
|
||||||
|
<div class="three fields">
|
||||||
|
<div class="field">
|
||||||
|
<label>ID:</label>
|
||||||
|
(generated on submit)
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Proponent:</label>
|
||||||
|
{{ .Form.Voter.Name }}
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Proposed date/time:</label>
|
||||||
|
(auto filled to current date/time)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="required field{{ if .Form.FieldErrors.title }} error{{ end }}">
|
||||||
|
<label for="title">Title:</label>
|
||||||
|
<input id="title" name="title" type="text" value="{{ .Form.Title }}">
|
||||||
|
{{ if .Form.FieldErrors.title }}
|
||||||
|
<span class="ui small error text">{{ .Form.FieldErrors.title }}</span>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
<div class="required field{{ if .Form.FieldErrors.content }} error{{ end }}">
|
||||||
|
<label for="content">Text:</label>
|
||||||
|
<textarea id="content" name="content">{{ .Form.Content }}</textarea>
|
||||||
|
{{ if .Form.FieldErrors.content }}
|
||||||
|
<span class="ui small error text">{{ .Form.FieldErrors.content }}</span>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
<div class="two fields">
|
||||||
|
<div class="required field{{ if .Form.FieldErrors.type }} error{{ end }}">
|
||||||
|
<label for="type">Vote type:</label>
|
||||||
|
{{ $voteType := toString .Form.Type }}
|
||||||
|
<select id="type" name="type">
|
||||||
|
<option value="motion"
|
||||||
|
{{ if eq "motion" $voteType }}selected{{ end }}>
|
||||||
|
Motion
|
||||||
|
</option>
|
||||||
|
<option value="veto"
|
||||||
|
{{ if eq "veto" $voteType }}selected{{ end }}>
|
||||||
|
Veto
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
{{ if .Form.FieldErrors.type }}
|
||||||
|
<span class="ui small error text">{{ .Form.FieldErrors.type}}</span>
|
||||||
|
{{ end}}
|
||||||
|
</div>
|
||||||
|
<div class="required field{{ if .Form.FieldErrors.due }} error{{ end }}">
|
||||||
|
<label for="due">Due: (autofilled from chosen
|
||||||
|
option)</label>
|
||||||
|
<select id="due" name="due">
|
||||||
|
<option value="+3 days"{{ if eq "+3 days" .Form.Due }} selected{{ end }}>In 3 Days</option>
|
||||||
|
<option value="+7 days"{{ if eq "+7 days" .Form.Due }} selected{{ end }}>In 1 Week</option>
|
||||||
|
<option value="+14 days"{{ if eq "+14 days" .Form.Due }} selected{{ end }}>In 2 Weeks</option>
|
||||||
|
<option value="+28 days"{{ if eq "+28 days" .Form.Due }} selected{{ end }}>In 4 Weeks</option>
|
||||||
|
</select>
|
||||||
|
{{ if .Form.FieldErrors.due }}
|
||||||
|
<span class="ui small error text">{{ .Form.FieldErrors.due }}</span>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="ui button" type="submit">Propose</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
Loading…
Reference in New Issue