forked from ponzu-cms/ponzu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
song.go
164 lines (141 loc) · 5.28 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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package content
import (
"fmt"
"log"
"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 }
// Create implements api.Createable, and allows external POST requests from clients
// to add content as long as the request contains the json tag names of the Song
// struct fields, and is multipart encoded
func (s *Song) Create(res http.ResponseWriter, req *http.Request) error {
// do form data validation for required fields
required := []string{
"title",
"artist",
"rating",
"opinion",
"spotify_url",
}
for _, r := range required {
if req.PostFormValue(r) == "" {
err := fmt.Errorf("request missing required field: %s", r)
return err
}
}
return nil
}
// BeforeAPICreate is only called if the Song type implements api.Createable
// It is called before Create, and returning an error will cancel the request
// causing the system to reject the data sent in the POST
func (s *Song) BeforeAPICreate(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 Create method, which is called after BeforeAPICreate
return nil
}
// AfterAPICreate is called after Create, 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. Ex. Song__pending:3 or Song:8 depending if Song implements api.Trustable
func (s *Song) AfterAPICreate(res http.ResponseWriter, req *http.Request) error {
addr := req.RemoteAddr
log.Println("Song sent by:", addr, "titled:", req.PostFormValue("title"))
return nil
}
// Approve implements editor.Mergeable, which enables content supplied by external
// clients to be approved and thus added to the public content API. Before content
// is approved, it is waiting in the Pending bucket, and can only be approved in
// the CMS if the Mergeable interface is satisfied. If not, you will not see this
// content show up in the CMS.
func (s *Song) Approve(res http.ResponseWriter, req *http.Request) error {
return nil
}
/*
NOTICE: if AutoApprove (seen below) is implemented, the Approve method above will have no
effect, except to add the Public / Pending toggle in the CMS UI. Though, no
Song content would be in Pending, since all externally submitting Song data
is immediately approved.
*/
// AutoApprove implements api.Trustable, and will automatically approve content
// that has been submitted by an external client via api.Createable. Be careful
// when using AutoApprove, because content will immediately be available through
// your public content API. If the Trustable interface is satisfied, the AfterApprove
// method is bypassed. The
func (s *Song) AutoApprove(res http.ResponseWriter, req *http.Request) error {
// Use AutoApprove to check for trust-specific headers or whitelisted IPs,
// etc. Remember, you will not be able to Approve or Reject content that
// is auto-approved. You could add a field to Song, i.e.
// AutoApproved bool `json:auto_approved`
// and set that data here, as it is called before the content is saved, but
// after the BeforeSave hook.
return nil
}