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