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  }