Skip to content

Commit

Permalink
Merge pull request #311 from ponzu-cms/ponzu-dev
Browse files Browse the repository at this point in the history
Merging ponzu-dev to master as v0.11.0
  • Loading branch information
olliephillips authored Aug 1, 2019
2 parents 3ef2dc0 + 4beb78e commit f0472b9
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 11 deletions.
2 changes: 1 addition & 1 deletion cmd/ponzu/ponzu.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"version": "0.10.1"
"version": "0.11.0"
}
29 changes: 27 additions & 2 deletions docs/src/Interfaces/Item.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,16 @@ func (p *Post) Omit(res http.ResponseWriter, req *http.Request) ([]string, error

### [item.Hookable](https://godoc.org/github.com/ponzu-cms/ponzu/system/item#Hookable)
Hookable provides lifecycle hooks into the http handlers which manage Save, Delete,
Approve, and Reject routines. All methods in its set take an
`http.ResponseWriter, *http.Request` and return an `error`.
Approve, Reject routines, and API response routines. All methods in its set take an
`http.ResponseWriter, *http.Request` and return an `error`. Hooks which relate to the API response, additionally take data of type `[]byte`, and may provide a return of the same type.

##### Method Set

```go
type Hookable interface {
BeforeAPIResponse(http.ResponseWriter, *http.Request, []byte) ([]byte, error)
AfterAPIResponse(http.ResponseWriter, *http.Request, []byte) error

BeforeAPICreate(http.ResponseWriter, *http.Request) error
AfterAPICreate(http.ResponseWriter, *http.Request) error

Expand Down Expand Up @@ -155,6 +158,28 @@ type Hookable interface {

##### Implementations

#### BeforeAPIResponse
BeforeAPIResponse is called before content is sent over the Ponzu API, and
provides an opportunity to modify the response data. If a non-nil `error` value
is returned, a 500 Internal Server Error is sent instead of the response.

```go
func (p *Post) BeforeAPIResponse(res http.ResponseWriter, req *http.Request, data []byte) ([]byte, error) {
return data, nil
}
```

#### AfterAPIResponse
AfterAPIResponse is called after content is sent over the Ponzu API, whether
modified or not. The sent response data is available to the hook. A non-nil
`error` return will simply generate a log message.

```go
func (p *Post) AfterAPIResponse(res http.ResponseWriter, req *http.Request, data []byte) error {
return nil
}
```

#### BeforeAPICreate
BeforeAPICreate is called before an item is created via a 3rd-party client. If a
non-nil `error` value is returned, the item will not be created/saved.
Expand Down
31 changes: 31 additions & 0 deletions system/admin/filesystem.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
package admin

import (
"encoding/json"
"net/http"
"os"
"path/filepath"
"strings"

"github.com/ponzu-cms/ponzu/system/db"
"github.com/ponzu-cms/ponzu/system/item"
)

func deleteUploadFromDisk(target string) error {
// get data on file
data, err := db.Upload(target)
if err != nil {
return err
}

// unmarshal data
upload := item.FileUpload{}
if err = json.Unmarshal(data, &upload); err != nil {
return err
}

// split and rebuild path in OS friendly way
// use path to delete the physical file from disk
pathSplit := strings.Split(strings.TrimPrefix(upload.Path, "/api/"), "/")
pathJoin := filepath.Join(pathSplit...)
err = os.Remove(pathJoin)
if err != nil {
return err
}

return nil
}

func restrict(dir http.Dir) justFilesFilesystem {
return justFilesFilesystem{dir}
}
Expand Down
13 changes: 12 additions & 1 deletion system/admin/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2200,7 +2200,18 @@ func deleteUploadHandler(res http.ResponseWriter, req *http.Request) {
return
}

err = db.DeleteUpload(t + ":" + id)
dbTarget := t + ":" + id

// delete from file system, if good, we continue to delete
// from database, if bad error 500
err = deleteUploadFromDisk(dbTarget)
if err != nil {
log.Println(err)
res.WriteHeader(http.StatusInternalServerError)
return
}

err = db.DeleteUpload(dbTarget)
if err != nil {
log.Println(err)
res.WriteHeader(http.StatusInternalServerError)
Expand Down
73 changes: 73 additions & 0 deletions system/api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,31 @@ func contentsHandler(res http.ResponseWriter, req *http.Request) {
return
}

// assert hookable
get := it()
hook, ok := get.(item.Hookable)
if !ok {
log.Println("[Response] error: Type", t, "does not implement item.Hookable or embed item.Item.")
res.WriteHeader(http.StatusBadRequest)
return
}

// hook before response
j, err = hook.BeforeAPIResponse(res, req, j)
if err != nil {
log.Println("[Response] error calling BeforeAPIResponse:", err)
res.WriteHeader(http.StatusInternalServerError)
return
}

sendData(res, req, j)

// hook after response
err = hook.AfterAPIResponse(res, req, j)
if err != nil {
log.Println("[Response] error calling AfterAPIResponse:", err)
return
}
}

func contentHandler(res http.ResponseWriter, req *http.Request) {
Expand Down Expand Up @@ -156,7 +180,31 @@ func contentHandler(res http.ResponseWriter, req *http.Request) {
return
}

// assert hookable
get := p
hook, ok := get.(item.Hookable)
if !ok {
log.Println("[Response] error: Type", t, "does not implement item.Hookable or embed item.Item.")
res.WriteHeader(http.StatusBadRequest)
return
}

// hook before response
j, err = hook.BeforeAPIResponse(res, req, j)
if err != nil {
log.Println("[Response] error calling BeforeAPIResponse:", err)
res.WriteHeader(http.StatusInternalServerError)
return
}

sendData(res, req, j)

// hook after response
err = hook.AfterAPIResponse(res, req, j)
if err != nil {
log.Println("[Response] error calling AfterAPIResponse:", err)
return
}
}

func contentHandlerBySlug(res http.ResponseWriter, req *http.Request) {
Expand Down Expand Up @@ -206,7 +254,32 @@ func contentHandlerBySlug(res http.ResponseWriter, req *http.Request) {
return
}

// assert hookable
get := p
hook, ok := get.(item.Hookable)
if !ok {
log.Println("[Response] error: Type", t, "does not implement item.Hookable or embed item.Item.")
res.WriteHeader(http.StatusBadRequest)
return
}

// hook before response
j, err = hook.BeforeAPIResponse(res, req, j)
if err != nil {
log.Println("[Response] error calling BeforeAPIResponse:", err)
res.WriteHeader(http.StatusInternalServerError)
return
}

sendData(res, req, j)

// hook after response
err = hook.AfterAPIResponse(res, req, j)
if err != nil {
log.Println("[Response] error calling AfterAPIResponse:", err)
return
}

}

func uploadsHandler(res http.ResponseWriter, req *http.Request) {
Expand Down
27 changes: 20 additions & 7 deletions system/item/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ func init() {
// We store the compiled regex as the key
// and assign the replacement as the map's value.
rxList = map[*regexp.Regexp][]byte{
regexp.MustCompile("`[-]+`"): []byte("-"),
regexp.MustCompile("[[:space:]]"): []byte("-"),
regexp.MustCompile("[[:blank:]]"): []byte(""),
regexp.MustCompile("`[^a-z0-9]`i"): []byte("-"),
regexp.MustCompile("[!/:-@[-`{-~]"): []byte(""),
regexp.MustCompile("/[^\x20-\x7F]/"): []byte(""),
regexp.MustCompile("`&(amp;)?#?[a-z0-9]+;`i"): []byte("-"),
regexp.MustCompile("`[-]+`"): []byte("-"),
regexp.MustCompile("[[:space:]]"): []byte("-"),
regexp.MustCompile("[[:blank:]]"): []byte(""),
regexp.MustCompile("`[^a-z0-9]`i"): []byte("-"),
regexp.MustCompile("[!/:-@[-`{-~]"): []byte(""),
regexp.MustCompile("/[^\x20-\x7F]/"): []byte(""),
regexp.MustCompile("`&(amp;)?#?[a-z0-9]+;`i"): []byte("-"),
regexp.MustCompile("`&([a-z])(acute|uml|circ|grave|ring|cedil|slash|tilde|caron|lig|quot|rsquo);`i"): []byte("\\1"),
}
}
Expand Down Expand Up @@ -65,6 +65,9 @@ type Sortable interface {
// to the different lifecycles/events a struct may encounter. Item implements
// Hookable with no-ops so our user can override only whichever ones necessary.
type Hookable interface {
BeforeAPIResponse(http.ResponseWriter, *http.Request, []byte) ([]byte, error)
AfterAPIResponse(http.ResponseWriter, *http.Request, []byte) error

BeforeAPICreate(http.ResponseWriter, *http.Request) error
AfterAPICreate(http.ResponseWriter, *http.Request) error

Expand Down Expand Up @@ -177,6 +180,16 @@ func (i Item) String() string {
return fmt.Sprintf("Item ID: %s", i.UniqueID())
}

// BeforeAPIResponse is a no-op to ensure structs which embed Item implement Hookable
func (i Item) BeforeAPIResponse(res http.ResponseWriter, req *http.Request, data []byte) ([]byte, error) {
return data, nil
}

// AfterAPIResponse is a no-op to ensure structs which embed Item implement Hookable
func (i Item) AfterAPIResponse(res http.ResponseWriter, req *http.Request, data []byte) error {
return nil
}

// BeforeAPICreate is a no-op to ensure structs which embed Item implement Hookable
func (i Item) BeforeAPICreate(res http.ResponseWriter, req *http.Request) error {
return nil
Expand Down

0 comments on commit f0472b9

Please sign in to comment.