github.com/bosssauce/ponzu@v0.11.1-0.20200102001432-9bc41b703131/examples/createable/content/song.go (about) 1 package content 2 3 import ( 4 "fmt" 5 "log" 6 7 "net/http" 8 9 "github.com/ponzu-cms/ponzu/management/editor" 10 "github.com/ponzu-cms/ponzu/system/admin/user" 11 "github.com/ponzu-cms/ponzu/system/api" 12 "github.com/ponzu-cms/ponzu/system/item" 13 ) 14 15 type Song struct { 16 item.Item 17 18 Title string `json:"title"` 19 Artist string `json:"artist"` 20 Rating int `json:"rating"` 21 Opinion string `json:"opinion"` 22 SpotifyURL string `json:"spotify_url"` 23 } 24 25 // MarshalEditor writes a buffer of html to edit a Song within the CMS 26 // and implements editor.Editable 27 func (s *Song) MarshalEditor() ([]byte, error) { 28 view, err := editor.Form(s, 29 // Take note that the first argument to these Input-like functions 30 // is the string version of each Song field, and must follow 31 // this pattern for auto-decoding and auto-encoding reasons: 32 editor.Field{ 33 View: editor.Input("Title", s, map[string]string{ 34 "label": "Title", 35 "type": "text", 36 "placeholder": "Enter the Title here", 37 }), 38 }, 39 editor.Field{ 40 View: editor.Input("Artist", s, map[string]string{ 41 "label": "Artist", 42 "type": "text", 43 "placeholder": "Enter the Artist here", 44 }), 45 }, 46 editor.Field{ 47 View: editor.Input("Rating", s, map[string]string{ 48 "label": "Rating", 49 "type": "text", 50 "placeholder": "Enter the Rating here", 51 }), 52 }, 53 editor.Field{ 54 View: editor.Richtext("Opinion", s, map[string]string{ 55 "label": "Opinion", 56 "placeholder": "Enter the Opinion here", 57 }), 58 }, 59 editor.Field{ 60 View: editor.Input("SpotifyURL", s, map[string]string{ 61 "label": "SpotifyURL", 62 "type": "text", 63 "placeholder": "Enter the SpotifyURL here", 64 }), 65 }, 66 ) 67 68 if err != nil { 69 return nil, fmt.Errorf("Failed to render Song editor view: %s", err.Error()) 70 } 71 72 return view, nil 73 } 74 75 func init() { 76 item.Types["Song"] = func() interface{} { return new(Song) } 77 } 78 79 // String defines the display name of a Song in the CMS list-view 80 func (s *Song) String() string { return s.Title } 81 82 // Create implements api.Createable, and allows external POST requests from clients 83 // to add content as long as the request contains the json tag names of the Song 84 // struct fields, and is multipart encoded 85 func (s *Song) Create(res http.ResponseWriter, req *http.Request) error { 86 // do form data validation for required fields 87 required := []string{ 88 "title", 89 "artist", 90 "rating", 91 "opinion", 92 "spotify_url", 93 } 94 95 for _, r := range required { 96 if req.PostFormValue(r) == "" { 97 err := fmt.Errorf("request missing required field: %s", r) 98 return err 99 } 100 } 101 102 return nil 103 } 104 105 // BeforeAPICreate is only called if the Song type implements api.Createable 106 // It is called before Create, and returning an error will cancel the request 107 // causing the system to reject the data sent in the POST 108 func (s *Song) BeforeAPICreate(res http.ResponseWriter, req *http.Request) error { 109 // do initial user authentication here on the request, checking for a 110 // token or cookie, or that certain form fields are set and valid 111 112 // for example, this will check if the request was made by a CMS admin user: 113 if !user.IsValid(req) { 114 return api.ErrNoAuth 115 } 116 117 // you could then to data validation on the request post form, or do it in 118 // the Create method, which is called after BeforeAPICreate 119 120 return nil 121 } 122 123 // AfterAPICreate is called after Create, and is useful for logging or triggering 124 // notifications, etc. after the data is saved to the database, etc. 125 // The request has a context containing the databse 'target' affected by the 126 // request. Ex. Song__pending:3 or Song:8 depending if Song implements api.Trustable 127 func (s *Song) AfterAPICreate(res http.ResponseWriter, req *http.Request) error { 128 addr := req.RemoteAddr 129 log.Println("Song sent by:", addr, "titled:", req.PostFormValue("title")) 130 131 return nil 132 } 133 134 // Approve implements editor.Mergeable, which enables content supplied by external 135 // clients to be approved and thus added to the public content API. Before content 136 // is approved, it is waiting in the Pending bucket, and can only be approved in 137 // the CMS if the Mergeable interface is satisfied. If not, you will not see this 138 // content show up in the CMS. 139 func (s *Song) Approve(res http.ResponseWriter, req *http.Request) error { 140 return nil 141 } 142 143 /* 144 NOTICE: if AutoApprove (seen below) is implemented, the Approve method above will have no 145 effect, except to add the Public / Pending toggle in the CMS UI. Though, no 146 Song content would be in Pending, since all externally submitting Song data 147 is immediately approved. 148 */ 149 150 // AutoApprove implements api.Trustable, and will automatically approve content 151 // that has been submitted by an external client via api.Createable. Be careful 152 // when using AutoApprove, because content will immediately be available through 153 // your public content API. If the Trustable interface is satisfied, the AfterApprove 154 // method is bypassed. The 155 func (s *Song) AutoApprove(res http.ResponseWriter, req *http.Request) error { 156 // Use AutoApprove to check for trust-specific headers or whitelisted IPs, 157 // etc. Remember, you will not be able to Approve or Reject content that 158 // is auto-approved. You could add a field to Song, i.e. 159 // AutoApproved bool `json:auto_approved` 160 // and set that data here, as it is called before the content is saved, but 161 // after the BeforeSave hook. 162 163 return nil 164 }