summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Manuel <nilslice@gmail.com>2017-04-29 14:39:35 -0700
committerSteve Manuel <nilslice@gmail.com>2017-04-29 14:39:35 -0700
commita48ed8dc7f7ddb3ddbc8ea54ad0b0d2feb7c87f0 (patch)
treea9b29cab1d50909b9aae7653d92651a0815f71f7
parent7ecfc3ac37f3a00522dd486f4b7e7e7c1850de47 (diff)
fmt bytes to readable and update setupload logic to allow edits
-rw-r--r--management/editor/elements.go2
-rw-r--r--system/admin/handlers.go245
-rw-r--r--system/admin/server.go1
-rw-r--r--system/admin/upload/upload.go2
-rw-r--r--system/db/upload.go92
-rw-r--r--system/item/upload.go (renamed from system/item/file.go)50
6 files changed, 368 insertions, 24 deletions
diff --git a/management/editor/elements.go b/management/editor/elements.go
index b179960..2dfab40 100644
--- a/management/editor/elements.go
+++ b/management/editor/elements.go
@@ -276,7 +276,7 @@ func Richtext(fieldName string, p interface{}, attrs map[string]string) []byte {
data.append("file", files[0]);
$.ajax({
data: data,
- type: 'POST',
+ type: 'PUT',
url: '/admin/edit/upload',
cache: false,
contentType: false,
diff --git a/system/admin/handlers.go b/system/admin/handlers.go
index 0433b76..7780151 100644
--- a/system/admin/handlers.go
+++ b/system/admin/handlers.go
@@ -2057,6 +2057,250 @@ func deleteHandler(res http.ResponseWriter, req *http.Request) {
}
func editUploadHandler(res http.ResponseWriter, req *http.Request) {
+ switch req.Method {
+ case http.MethodGet:
+ q := req.URL.Query()
+ i := q.Get("id")
+ t := "__uploads"
+
+ post := &item.FileUpload{}
+
+ if i != "" {
+ data, err := db.Upload(t + ":" + i)
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ if len(data) < 1 || data == nil {
+ res.WriteHeader(http.StatusNotFound)
+ errView, err := Error404()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ err = json.Unmarshal(data, post)
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+ } else {
+ it, ok := interface{}(post).(item.Identifiable)
+ if !ok {
+ log.Println("Content type", t, "doesn't implement item.Identifiable")
+ return
+ }
+
+ it.SetItemID(-1)
+ }
+
+ m, err := manager.Manage(interface{}(post).(editor.Editable), t)
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ adminView, err := Admin(m)
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ res.Header().Set("Content-Type", "text/html")
+ res.Write(adminView)
+
+ case http.MethodPost:
+ err := req.ParseMultipartForm(1024 * 1024 * 4) // maxMemory 4MB
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ cid := req.FormValue("id")
+ t := req.FormValue("type")
+ pt := "__uploads"
+ ts := req.FormValue("timestamp")
+ up := req.FormValue("updated")
+
+ // create a timestamp if one was not set
+ if ts == "" {
+ ts = fmt.Sprintf("%d", int64(time.Nanosecond)*time.Now().UTC().UnixNano()/int64(time.Millisecond))
+ req.PostForm.Set("timestamp", ts)
+ }
+
+ if up == "" {
+ req.PostForm.Set("updated", ts)
+ }
+
+ urlPaths, err := upload.StoreFiles(req)
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ for name, urlPath := range urlPaths {
+ req.PostForm.Set(name, urlPath)
+ }
+
+ // check for any multi-value fields (ex. checkbox fields)
+ // and correctly format for db storage. Essentially, we need
+ // fieldX.0: value1, fieldX.1: value2 => fieldX: []string{value1, value2}
+ fieldOrderValue := make(map[string]map[string][]string)
+ ordVal := make(map[string][]string)
+ for k, v := range req.PostForm {
+ if strings.Contains(k, ".") {
+ fo := strings.Split(k, ".")
+
+ // put the order and the field value into map
+ field := string(fo[0])
+ order := string(fo[1])
+ fieldOrderValue[field] = ordVal
+
+ // orderValue is 0:[?type=Thing&id=1]
+ orderValue := fieldOrderValue[field]
+ orderValue[order] = v
+ fieldOrderValue[field] = orderValue
+
+ // discard the post form value with name.N
+ req.PostForm.Del(k)
+ }
+
+ }
+
+ // add/set the key & value to the post form in order
+ for f, ov := range fieldOrderValue {
+ for i := 0; i < len(ov); i++ {
+ position := fmt.Sprintf("%d", i)
+ fieldValue := ov[position]
+
+ if req.PostForm.Get(f) == "" {
+ for i, fv := range fieldValue {
+ if i == 0 {
+ req.PostForm.Set(f, fv)
+ } else {
+ req.PostForm.Add(f, fv)
+ }
+ }
+ } else {
+ for _, fv := range fieldValue {
+ req.PostForm.Add(f, fv)
+ }
+ }
+ }
+ }
+
+ post := interface{}(&item.FileUpload{})
+ hook, ok := post.(item.Hookable)
+ if !ok {
+ log.Println("Type", pt, "does not implement item.Hookable or embed item.Item.")
+ res.WriteHeader(http.StatusBadRequest)
+ errView, err := Error400()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ err = hook.BeforeSave(res, req)
+ if err != nil {
+ log.Println("Error running BeforeSave method in editHandler for:", t, err)
+ return
+ }
+
+ id, err := db.SetUpload(t+":"+cid, req.PostForm)
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ // set the target in the context so user can get saved value from db in hook
+ ctx := context.WithValue(req.Context(), "target", fmt.Sprintf("%s:%d", t, id))
+ req = req.WithContext(ctx)
+
+ err = hook.AfterSave(res, req)
+ if err != nil {
+ log.Println("Error running AfterSave method in editHandler for:", t, err)
+ return
+ }
+
+ scheme := req.URL.Scheme
+ host := req.URL.Host
+ path := req.URL.Path
+ sid := fmt.Sprintf("%d", id)
+ redir := scheme + host + path + "?type=" + pt + "&id=" + sid
+
+ if req.URL.Query().Get("status") == "pending" {
+ redir += "&status=pending"
+ }
+
+ http.Redirect(res, req, redir, http.StatusFound)
+ case http.MethodPut:
+ urlPaths, err := upload.StoreFiles(req)
+ if err != nil {
+ log.Println("Couldn't store file uploads.", err)
+ res.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ res.Header().Set("Content-Type", "application/json")
+ res.Write([]byte(`{"data": [{"url": "` + urlPaths["file"] + `"}]}`))
+ default:
+ res.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+}
+
+/*
+func editUploadHandler(res http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
res.WriteHeader(http.StatusMethodNotAllowed)
return
@@ -2072,6 +2316,7 @@ func editUploadHandler(res http.ResponseWriter, req *http.Request) {
res.Header().Set("Content-Type", "application/json")
res.Write([]byte(`{"data": [{"url": "` + urlPaths["file"] + `"}]}`))
}
+*/
func searchHandler(res http.ResponseWriter, req *http.Request) {
q := req.URL.Query()
diff --git a/system/admin/server.go b/system/admin/server.go
index 94aca78..3ee44f9 100644
--- a/system/admin/server.go
+++ b/system/admin/server.go
@@ -33,6 +33,7 @@ func Run() {
http.HandleFunc("/admin/configure/users/delete", user.Auth(configUsersDeleteHandler))
http.HandleFunc("/admin/uploads", user.Auth(uploadContentsHandler))
+ // http.HandleFunc("/admin/uploads/search", user.Auth(uploadSearchHandler))
http.HandleFunc("/admin/contents", user.Auth(contentsHandler))
http.HandleFunc("/admin/contents/search", user.Auth(searchHandler))
diff --git a/system/admin/upload/upload.go b/system/admin/upload/upload.go
index 35fb8f9..a0568e4 100644
--- a/system/admin/upload/upload.go
+++ b/system/admin/upload/upload.go
@@ -115,7 +115,7 @@ func storeFileInfo(size int64, filename, urlPath string, fds []*multipart.FileHe
"content_length": []string{fmt.Sprintf("%d", size)},
}
- err := db.SetUpload(data)
+ _, err := db.SetUpload("__uploads:-1", data)
if err != nil {
log.Println("Error saving file upload record to database:", err)
}
diff --git a/system/db/upload.go b/system/db/upload.go
index cef4595..b05f505 100644
--- a/system/db/upload.go
+++ b/system/db/upload.go
@@ -10,41 +10,64 @@ import (
"github.com/ponzu-cms/ponzu/system/item"
+ "strconv"
+
"github.com/boltdb/bolt"
"github.com/gorilla/schema"
uuid "github.com/satori/go.uuid"
)
// SetUpload stores information about files uploaded to the system
-func SetUpload(data url.Values) error {
- // set new UUID for upload
- data.Set("uuid", uuid.NewV4().String())
+func SetUpload(target string, data url.Values) (int, error) {
+ parts := strings.Split(target, ":")
+ if parts[0] != "__uploads" {
+ return 0, fmt.Errorf("cannot call SetUpload with target type: %s", parts[0])
+ }
+ pid := parts[1]
- // create slug based on filename and timestamp/updated fields
- slug := data.Get("name")
- slug, err := checkSlugForDuplicate(slug)
- if err != nil {
- return err
+ if data.Get("uuid") == "" {
+ // set new UUID for upload
+ data.Set("uuid", uuid.NewV4().String())
+ }
+
+ if data.Get("slug") == "" {
+ // create slug based on filename and timestamp/updated fields
+ slug := data.Get("name")
+ slug, err := checkSlugForDuplicate(slug)
+ if err != nil {
+ return 0, err
+ }
+ data.Set("slug", slug)
}
- data.Set("slug", slug)
ts := fmt.Sprintf("%d", time.Now().Unix()*1000)
- data.Set("timestamp", ts)
+ if data.Get("timestamp") == "" {
+ data.Set("timestamp", ts)
+ }
+
data.Set("updated", ts)
// store in database
- err = store.Update(func(tx *bolt.Tx) error {
+ var id int64
+ err := store.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucketIfNotExists([]byte("__uploads"))
if err != nil {
return err
}
- // get sequential ID for item
- id, err := b.NextSequence()
- if err != nil {
- return err
+ if pid == "-1" {
+ // get sequential ID for item
+ id, err := b.NextSequence()
+ if err != nil {
+ return err
+ }
+ data.Set("id", fmt.Sprintf("%d", id))
+ } else {
+ id, err = strconv.ParseInt(pid, 10, 64)
+ if err != nil {
+ return err
+ }
}
- data.Set("id", fmt.Sprintf("%d", id))
file := &item.FileUpload{}
dec := schema.NewDecoder()
@@ -78,12 +101,14 @@ func SetUpload(data url.Values) error {
if err != nil {
return err
}
- // -
return nil
})
+ if err != nil {
+ return 0, err
+ }
- return err
+ return int(id), nil
}
// Upload returns the value for an upload by its target (__uploads:{id})
@@ -136,3 +161,34 @@ func UploadBySlug(slug string) ([]byte, error) {
return val.Bytes(), err
}
+
+// func replaceUpload(id string, data url.Values) error {
+// // Unmarsal the existing values
+// s := t()
+// err := json.Unmarshal(existingContent, &s)
+// if err != nil {
+// log.Println("Error decoding json while updating", ns, ":", err)
+// return j, err
+// }
+
+// // Don't allow the Item fields to be updated from form values
+// data.Del("id")
+// data.Del("uuid")
+// data.Del("slug")
+
+// dec := schema.NewDecoder()
+// dec.SetAliasTag("json") // allows simpler struct tagging when creating a content type
+// dec.IgnoreUnknownKeys(true) // will skip over form values submitted, but not in struct
+// err = dec.Decode(s, data)
+// if err != nil {
+// return j, err
+// }
+
+// j, err = json.Marshal(s)
+// if err != nil {
+// return j, err
+// }
+
+// return j, nil
+// return nil
+// }
diff --git a/system/item/file.go b/system/item/upload.go
index 3b33b04..9253fcc 100644
--- a/system/item/file.go
+++ b/system/item/upload.go
@@ -23,20 +23,32 @@ func (f *FileUpload) String() string { return f.Name }
func (f *FileUpload) MarshalEditor() ([]byte, error) {
view, err := editor.Form(f,
editor.Field{
- View: []byte(`
+ View: func() []byte {
+ if f.Path == "" {
+ return nil
+ }
+
+ return []byte(`
<div class="input-field col s12">
- <label class="active">{{ .Name }}</label>
+ <label class="active">` + f.Name + `</label>
<!-- Add your custom editor field view here. -->
<h4>` + f.Name + `</h4>
<img class="preview" src="` + f.Path + `"/>
<p>File information:</p>
<ul>
- <li>Content-Length: ` + fmt.Sprintf("%d", f.ContentLength) + `</li>
+ <li>Content-Length: ` + fmt.Sprintf("%s", FmtBytes(float64(f.ContentLength))) + `</li>
<li>Content-Type: ` + f.ContentType + `</li>
</ul>
</div>
- `),
+ `)
+ }(),
+ },
+ editor.Field{
+ View: editor.File("Path", f, map[string]string{
+ "label": "File Upload",
+ "placeholder": "Upload the file here",
+ }),
},
)
if err != nil {
@@ -94,3 +106,33 @@ func (f *FileUpload) Push() []string {
"path",
}
}
+
+// FmtBytes converts the numeric byte size value to the appropriate magnitude
+// size in KB, MB, GB, TB, PB, or EB.
+func FmtBytes(size float64) string {
+ unit := float64(1024)
+ BYTE := unit
+ KBYTE := BYTE * unit
+ MBYTE := KBYTE * unit
+ GBYTE := MBYTE * unit
+ TBYTE := GBYTE * unit
+ PBYTE := TBYTE * unit
+
+ switch {
+ case size < BYTE:
+ return fmt.Sprintf("%0.f B", size)
+ case size < KBYTE:
+ return fmt.Sprintf("%.1f KB", size/BYTE)
+ case size < MBYTE:
+ return fmt.Sprintf("%.1f MB", size/KBYTE)
+ case size < GBYTE:
+ return fmt.Sprintf("%.1f GB", size/MBYTE)
+ case size < TBYTE:
+ return fmt.Sprintf("%.1f TB", size/GBYTE)
+ case size < PBYTE:
+ return fmt.Sprintf("%.1f PB", size/TBYTE)
+ default:
+ return fmt.Sprintf("%0.f B", size)
+ }
+
+}