-
Notifications
You must be signed in to change notification settings - Fork 0
/
song.go
140 lines (120 loc) · 4.23 KB
/
song.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package content
import (
"fmt"
"log"
"strings"
"net/http"
"github.com/ponzu-cms/ponzu/management/editor"
"github.com/ponzu-cms/ponzu/system/admin/user"
"github.com/ponzu-cms/ponzu/system/api"
"github.com/ponzu-cms/ponzu/system/item"
)
type Song struct {
item.Item
Title string `json:"title"`
Artist string `json:"artist"`
Rating int `json:"rating"`
Opinion string `json:"opinion"`
SpotifyURL string `json:"spotify_url"`
}
// MarshalEditor writes a buffer of html to edit a Song within the CMS
// and implements editor.Editable
func (s *Song) MarshalEditor() ([]byte, error) {
view, err := editor.Form(s,
// Take note that the first argument to these Input-like functions
// is the string version of each Song field, and must follow
// this pattern for auto-decoding and auto-encoding reasons:
editor.Field{
View: editor.Input("Title", s, map[string]string{
"label": "Title",
"type": "text",
"placeholder": "Enter the Title here",
}),
},
editor.Field{
View: editor.Input("Artist", s, map[string]string{
"label": "Artist",
"type": "text",
"placeholder": "Enter the Artist here",
}),
},
editor.Field{
View: editor.Input("Rating", s, map[string]string{
"label": "Rating",
"type": "text",
"placeholder": "Enter the Rating here",
}),
},
editor.Field{
View: editor.Richtext("Opinion", s, map[string]string{
"label": "Opinion",
"placeholder": "Enter the Opinion here",
}),
},
editor.Field{
View: editor.Input("SpotifyURL", s, map[string]string{
"label": "SpotifyURL",
"type": "text",
"placeholder": "Enter the SpotifyURL here",
}),
},
)
if err != nil {
return nil, fmt.Errorf("Failed to render Song editor view: %s", err.Error())
}
return view, nil
}
func init() {
item.Types["Song"] = func() interface{} { return new(Song) }
}
// String defines the display name of a Song in the CMS list-view
func (s *Song) String() string { return s.Title }
// BeforeAPIUpdate is only called if the Song type implements api.Updateable
// It is called before Update, and returning an error will cancel the request
// causing the system to reject the data sent in the POST
func (s *Song) BeforeAPIUpdate(res http.ResponseWriter, req *http.Request) error {
// do initial user authentication here on the request, checking for a
// token or cookie, or that certain form fields are set and valid
// for example, this will check if the request was made by a CMS admin user:
if !user.IsValid(req) {
return api.ErrNoAuth
}
// you could then to data validation on the request post form, or do it in
// the Update method, which is called after BeforeAPIUpdate
return nil
}
// Update is called after BeforeAPIUpdate and is where you may influence the
// merge process. For example, maybe you don't want an empty string for the Title
// or Artist field to be accepted by the update request. Updates will always merge
// with existing values, but by default will accept zero value as an update if sent.
func (s *Song) Update(res http.ResponseWriter, req *http.Request) error {
addr := req.RemoteAddr
log.Println("Song update sent by:", addr, "id:", req.URL.Query().Get("id"))
// On update its fine if fields are missing, but we don't want
// title overwritten by a blank or empty string since that would
// break the display name. Artist is also required to be non-blank.
var required = map[string]interface{}{
"title": nil,
"artist": nil,
}
for k, _ := range req.PostForm {
blank := (strings.TrimSpace(req.PostFormValue(k)) == "")
if _, ok := required[k]; ok && blank {
log.Println("Removing blank value for:", k)
// We'll just remove the blank values.
// Alternately we could return an error to
// reject the post.
req.PostForm.Del(k)
}
}
return nil
}
// AfterAPIUpdate is called after Update, and is useful for logging or triggering
// notifications, etc. after the data is saved to the database, etc.
// The request has a context containing the databse 'target' affected by the
// request.
func (s *Song) AfterAPIUpdate(res http.ResponseWriter, req *http.Request) error {
addr := req.RemoteAddr
log.Println("Song updated by:", addr, "id:", req.URL.Query().Get("id"))
return nil
}