summaryrefslogtreecommitdiff
path: root/system/admin
diff options
context:
space:
mode:
authorSteve <nilslice@gmail.com>2017-01-12 16:19:00 -0800
committerGitHub <noreply@github.com>2017-01-12 16:19:00 -0800
commit21b9f301c5e1305c3be4e260161d2495bca059de (patch)
tree3be88e282bb9801b5b0354f89c5fe127996b0b6a /system/admin
parentad96d5fa100615ada7ae8f06e75088f9297122fd (diff)
[addons] Expanding basic addon framework (#29)
Diffstat (limited to 'system/admin')
-rw-r--r--system/admin/admin.go3
-rw-r--r--system/admin/config/config.go12
-rw-r--r--system/admin/handlers.go584
-rw-r--r--system/admin/server.go3
-rw-r--r--system/admin/static/dashboard/css/admin.css17
-rw-r--r--system/admin/upload/upload.go2
6 files changed, 594 insertions, 27 deletions
diff --git a/system/admin/admin.go b/system/admin/admin.go
index e3ae2d6..9c4cfdd 100644
--- a/system/admin/admin.go
+++ b/system/admin/admin.go
@@ -64,7 +64,8 @@ var mainAdminHTML = `
<div class="card-title">System</div>
<div class="row collection-item">
<li><a class="col s12" href="/admin/configure"><i class="tiny left material-icons">settings</i>Configuration</a></li>
- <li><a class="col s12" href="/admin/configure/users"><i class="tiny left material-icons">supervisor_account</i>Users</a></li>
+ <li><a class="col s12" href="/admin/configure/users"><i class="tiny left material-icons">supervisor_account</i>Admin Users</a></li>
+ <li><a class="col s12" href="/admin/addons"><i class="tiny left material-icons">settings_input_svideo</i>Addons</a></li>
</div>
</ul>
</div>
diff --git a/system/admin/config/config.go b/system/admin/config/config.go
index 2bc80c6..7b57dc0 100644
--- a/system/admin/config/config.go
+++ b/system/admin/config/config.go
@@ -8,7 +8,6 @@ import (
// Config represents the confirgurable options of the system
type Config struct {
item.Item
- editor editor.Editor
Name string `json:"name"`
Domain string `json:"domain"`
@@ -23,9 +22,6 @@ type Config struct {
// String partially implements item.Identifiable and overrides Item's String()
func (c *Config) String() string { return c.Name }
-// Editor partially implements editor.Editable
-func (c *Config) Editor() *editor.Editor { return &c.editor }
-
// MarshalEditor writes a buffer of html to edit a Post and partially implements editor.Editable
func (c *Config) MarshalEditor() ([]byte, error) {
view, err := editor.Form(c,
@@ -90,7 +86,13 @@ func (c *Config) MarshalEditor() ([]byte, error) {
return nil, err
}
- open := []byte(`<div class="card"><form action="/admin/configure" method="post">`)
+ open := []byte(`
+ <div class="card">
+ <div class="card-content">
+ <div class="card-title">System Configuration</div>
+ </div>
+ <form action="/admin/configure" method="post">
+ `)
close := []byte(`</form></div>`)
script := []byte(`
<script>
diff --git a/system/admin/handlers.go b/system/admin/handlers.go
index e7dabfe..c39fee4 100644
--- a/system/admin/handlers.go
+++ b/system/admin/handlers.go
@@ -2,6 +2,7 @@ package admin
import (
"bytes"
+ "context"
"encoding/base64"
"encoding/json"
"fmt"
@@ -13,12 +14,14 @@ import (
"github.com/ponzu-cms/ponzu/management/editor"
"github.com/ponzu-cms/ponzu/management/manager"
+ "github.com/ponzu-cms/ponzu/system/addon"
"github.com/ponzu-cms/ponzu/system/admin/config"
"github.com/ponzu-cms/ponzu/system/admin/upload"
"github.com/ponzu-cms/ponzu/system/admin/user"
"github.com/ponzu-cms/ponzu/system/api"
"github.com/ponzu-cms/ponzu/system/db"
"github.com/ponzu-cms/ponzu/system/item"
+ "github.com/tidwall/gjson"
"github.com/gorilla/schema"
emailer "github.com/nilslice/email"
@@ -263,6 +266,17 @@ func configUsersEditHandler(res http.ResponseWriter, req *http.Request) {
switch req.Method {
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
+ }
// check if user to be edited is current user
j, err := db.CurrentUser(req)
@@ -385,6 +399,17 @@ func configUsersDeleteHandler(res http.ResponseWriter, req *http.Request) {
switch req.Method {
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
+ }
// do not allow current user to delete themselves
j, err := db.CurrentUser(req)
@@ -567,8 +592,9 @@ func forgotPasswordHandler(res http.ResponseWriter, req *http.Request) {
case http.MethodPost:
err := req.ParseMultipartForm(1024 * 1024 * 4) // maxMemory 4MB
if err != nil {
- res.WriteHeader(http.StatusBadRequest)
- errView, err := Error400()
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
if err != nil {
return
}
@@ -939,12 +965,37 @@ func contentsHandler(res http.ResponseWriter, req *http.Request) {
log.Println("Error unmarshal json into", t, err, string(posts[i]))
post := `<li class="col s12">Error decoding data. Possible file corruption.</li>`
- b.Write([]byte(post))
+ _, err := b.Write([]byte(post))
+ if err != nil {
+ log.Println(err)
+
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ }
+
+ res.Write(errView)
+ return
+ }
+
continue
}
post := adminPostListItem(p, t, status)
- b.Write(post)
+ _, err = b.Write(post)
+ if err != nil {
+ log.Println(err)
+
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ }
+
+ res.Write(errView)
+ return
+ }
}
case "pending":
@@ -964,12 +1015,36 @@ func contentsHandler(res http.ResponseWriter, req *http.Request) {
log.Println("Error unmarshal json into", t, err, string(posts[i]))
post := `<li class="col s12">Error decoding data. Possible file corruption.</li>`
- b.Write([]byte(post))
+ _, err := b.Write([]byte(post))
+ if err != nil {
+ log.Println(err)
+
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ }
+
+ res.Write(errView)
+ return
+ }
continue
}
post := adminPostListItem(p, t, status)
- b.Write(post)
+ _, err = b.Write(post)
+ if err != nil {
+ log.Println(err)
+
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ }
+
+ res.Write(errView)
+ return
+ }
}
}
@@ -982,18 +1057,54 @@ func contentsHandler(res http.ResponseWriter, req *http.Request) {
log.Println("Error unmarshal json into", t, err, string(posts[i]))
post := `<li class="col s12">Error decoding data. Possible file corruption.</li>`
- b.Write([]byte(post))
+ _, err := b.Write([]byte(post))
+ if err != nil {
+ log.Println(err)
+
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ }
+
+ res.Write(errView)
+ return
+ }
continue
}
post := adminPostListItem(p, t, status)
- b.Write(post)
+ _, err = b.Write(post)
+ if err != nil {
+ log.Println(err)
+
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ }
+
+ res.Write(errView)
+ return
+ }
}
}
html += `<ul class="posts row">`
- b.Write([]byte(`</ul>`))
+ _, err = b.Write([]byte(`</ul>`))
+ if err != nil {
+ log.Println(err)
+
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ }
+
+ res.Write(errView)
+ return
+ }
statusDisabled := "disabled"
prevStatus := ""
@@ -1042,7 +1153,19 @@ func contentsHandler(res http.ResponseWriter, req *http.Request) {
`
}
- b.Write([]byte(pagination + `</div></div>`))
+ _, err = b.Write([]byte(pagination + `</div></div>`))
+ if err != nil {
+ log.Println(err)
+
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ }
+
+ res.Write(errView)
+ return
+ }
script := `
<script>
@@ -1269,6 +1392,10 @@ func approveContentHandler(res http.ResponseWriter, req *http.Request) {
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(req)
if err != nil {
log.Println("Error running AfterSave hook in approveContentHandler for:", t, err)
@@ -1306,7 +1433,7 @@ func editHandler(res http.ResponseWriter, req *http.Request) {
contentType, ok := item.Types[t]
if !ok {
- fmt.Fprintf(res, item.ErrTypeNotRegistered, t)
+ fmt.Fprintf(res, item.ErrTypeNotRegistered.Error(), t)
return
}
post := contentType()
@@ -1358,6 +1485,7 @@ func editHandler(res http.ResponseWriter, req *http.Request) {
log.Println("Content type", t, "doesn't implement item.Identifiable")
return
}
+
item.SetItemID(-1)
}
@@ -1388,8 +1516,8 @@ func editHandler(res http.ResponseWriter, req *http.Request) {
err := req.ParseMultipartForm(1024 * 1024 * 4) // maxMemory 4MB
if err != nil {
log.Println(err)
- res.WriteHeader(http.StatusBadRequest)
- errView, err := Error405()
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
if err != nil {
return
}
@@ -1452,11 +1580,12 @@ func editHandler(res http.ResponseWriter, req *http.Request) {
req.PostForm.Del(discardKey)
}
+ pt := t
if strings.Contains(t, "__") {
- t = strings.Split(t, "__")[0]
+ pt = strings.Split(t, "__")[0]
}
- p, ok := item.Types[t]
+ p, ok := item.Types[pt]
if !ok {
log.Println("Type", t, "is not a content type. Cannot edit or save.")
res.WriteHeader(http.StatusBadRequest)
@@ -1472,7 +1601,7 @@ func editHandler(res http.ResponseWriter, req *http.Request) {
post := p()
hook, ok := post.(item.Hookable)
if !ok {
- log.Println("Type", t, "does not implement item.Hookable or embed item.Item.")
+ log.Println("Type", pt, "does not implement item.Hookable or embed item.Item.")
res.WriteHeader(http.StatusBadRequest)
errView, err := Error400()
if err != nil {
@@ -1509,6 +1638,10 @@ func editHandler(res http.ResponseWriter, req *http.Request) {
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(req)
if err != nil {
log.Println(err)
@@ -1526,7 +1659,7 @@ func editHandler(res http.ResponseWriter, req *http.Request) {
host := req.URL.Host
path := req.URL.Path
sid := fmt.Sprintf("%d", id)
- redir := scheme + host + path + "?type=" + t + "&id=" + sid
+ redir := scheme + host + path + "?type=" + pt + "&id=" + sid
if req.URL.Query().Get("status") == "pending" {
redir += "&status=pending"
@@ -1549,6 +1682,12 @@ func deleteHandler(res http.ResponseWriter, req *http.Request) {
if err != nil {
log.Println(err)
res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
return
}
@@ -1729,15 +1868,51 @@ func searchHandler(res http.ResponseWriter, req *http.Request) {
log.Println("Error unmarshal search result json into", t, err, posts[i])
post := `<li class="col s12">Error decoding data. Possible file corruption.</li>`
- b.Write([]byte(post))
+ _, err = b.Write([]byte(post))
+ if err != nil {
+ log.Println(err)
+
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ }
+
+ res.Write(errView)
+ return
+ }
continue
}
post := adminPostListItem(p, t, status)
- b.Write([]byte(post))
+ _, err = b.Write([]byte(post))
+ if err != nil {
+ log.Println(err)
+
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ }
+
+ res.Write(errView)
+ return
+ }
}
- b.Write([]byte(`</ul></div></div>`))
+ _, err := b.WriteString(`</ul></div></div>`)
+ if err != nil {
+ log.Println(err)
+
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ }
+
+ res.Write(errView)
+ return
+ }
btn := `<div class="col s3"><a href="/admin/edit?type=` + t + `" class="btn new-post waves-effect waves-light">New ` + t + `</a></div></div>`
html = html + b.String() + btn
@@ -1752,3 +1927,372 @@ func searchHandler(res http.ResponseWriter, req *http.Request) {
res.Header().Set("Content-Type", "text/html")
res.Write(adminView)
}
+
+func addonsHandler(res http.ResponseWriter, req *http.Request) {
+ switch req.Method {
+ case http.MethodGet:
+ all := db.AddonAll()
+ list := &bytes.Buffer{}
+
+ for i := range all {
+ v := adminAddonListItem(all[i])
+ _, err := list.Write(v)
+ if err != nil {
+ log.Println("Error writing bytes to addon list view:", err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+ }
+
+ html := &bytes.Buffer{}
+ open := `<div class="col s9 card">
+ <div class="card-content">
+ <div class="row">
+ <div class="card-title col s7">Addons</div>
+ </div>
+ <ul class="posts row">`
+
+ _, err := html.WriteString(open)
+ if err != nil {
+ log.Println("Error writing open html to addon html view:", err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ _, err = html.Write(list.Bytes())
+ if err != nil {
+ log.Println("Error writing list bytes to addon html view:", err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ _, err = html.WriteString(`</ul></div></div>`)
+ if err != nil {
+ log.Println("Error writing close html to addon html view:", err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ if html.Len() == 0 {
+ _, err := html.WriteString(`<p>No addons available.</p>`)
+ if err != nil {
+ log.Println("Error writing default addon html to admin view:", err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+ }
+
+ view, err := Admin(html.Bytes())
+ if err != nil {
+ log.Println("Error writing addon html to admin view:", err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ log.Println(err)
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ res.Write(view)
+
+ 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
+ }
+
+ id := req.PostFormValue("id")
+ action := strings.ToLower(req.PostFormValue("action"))
+
+ _, err = db.Addon(id)
+ if err == db.ErrNoAddonExists {
+ log.Println(err)
+ res.WriteHeader(http.StatusNotFound)
+ errView, err := Error404()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ switch action {
+ case "enable":
+ err := addon.Enable(id)
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+ case "disable":
+ err := addon.Disable(id)
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+ default:
+ res.WriteHeader(http.StatusBadRequest)
+ errView, err := Error400()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ http.Redirect(res, req, req.URL.String(), http.StatusFound)
+
+ default:
+ res.WriteHeader(http.StatusBadRequest)
+ errView, err := Error400()
+ if err != nil {
+ log.Println(err)
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+}
+
+func addonHandler(res http.ResponseWriter, req *http.Request) {
+ switch req.Method {
+ case http.MethodGet:
+ id := req.FormValue("id")
+
+ data, err := db.Addon(id)
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ _, ok := addon.Types[id]
+ if !ok {
+ log.Println("Addon: ", id, "is not found in addon.Types map")
+ res.WriteHeader(http.StatusNotFound)
+ errView, err := Error404()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ m, err := addon.Manage(data, id)
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ addonView, err := Admin(m)
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ res.Header().Set("Content-Type", "text/html")
+ res.Write(addonView)
+
+ case http.MethodPost:
+ // save req.Form
+ 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
+ }
+
+ name := req.FormValue("addon_name")
+ id := req.FormValue("addon_reverse_dns")
+
+ at, ok := addon.Types[id]
+ if !ok {
+ log.Println("Error: addon", name, "has no record in addon.Types map at", id)
+ res.WriteHeader(http.StatusBadRequest)
+ errView, err := Error400()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ // if Hookable, call BeforeSave prior to saving
+ h, ok := at().(item.Hookable)
+ if ok {
+ err := h.BeforeSave(req)
+ if err != nil {
+ log.Println(err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+ }
+
+ err = db.SetAddon(req.Form, at())
+ if err != nil {
+ log.Println("Error saving addon:", name, err)
+ res.WriteHeader(http.StatusInternalServerError)
+ errView, err := Error500()
+ if err != nil {
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+
+ http.Redirect(res, req, "/admin/addon?id="+id, http.StatusFound)
+
+ default:
+ res.WriteHeader(http.StatusBadRequest)
+ errView, err := Error405()
+ if err != nil {
+ log.Println(err)
+ return
+ }
+
+ res.Write(errView)
+ return
+ }
+}
+
+func adminAddonListItem(data []byte) []byte {
+ id := gjson.GetBytes(data, "addon_reverse_dns").String()
+ status := gjson.GetBytes(data, "addon_status").String()
+ name := gjson.GetBytes(data, "addon_name").String()
+ author := gjson.GetBytes(data, "addon_author").String()
+ authorURL := gjson.GetBytes(data, "addon_author_url").String()
+ version := gjson.GetBytes(data, "addon_version").String()
+
+ var action string
+ var buttonClass string
+ if status != addon.StatusEnabled {
+ action = "Enable"
+ buttonClass = "green"
+ } else {
+ action = "Disable"
+ buttonClass = "red"
+ }
+
+ a := `
+ <li class="col s12">
+ <div class="row">
+ <div class="col s9">
+ <a class="addon-name" href="/admin/addon?id=` + id + `" alt="Configure '` + name + `'">` + name + `</a>
+ <span class="addon-meta addon-author">by: <a href="` + authorURL + `">` + author + `</a></span>
+ <span class="addon-meta addon-version">version: ` + version + `</span>
+ </div>
+
+ <div class="col s3">
+ <form enctype="multipart/form-data" class="quick-` + strings.ToLower(action) + `-addon __ponzu right" action="/admin/addons" method="post">
+ <button class="btn waves-effect waves-effect-light ` + buttonClass + `">` + action + `</button>
+ <input type="hidden" name="id" value="` + id + `" />
+ <input type="hidden" name="action" value="` + action + `" />
+ </form>
+ </div>
+ </div>
+ </li>`
+
+ return []byte(a)
+}
diff --git a/system/admin/server.go b/system/admin/server.go
index 155892a..f2bf244 100644
--- a/system/admin/server.go
+++ b/system/admin/server.go
@@ -23,6 +23,9 @@ func Run() {
http.HandleFunc("/admin/recover", forgotPasswordHandler)
http.HandleFunc("/admin/recover/key", recoveryKeyHandler)
+ http.HandleFunc("/admin/addons", user.Auth(addonsHandler))
+ http.HandleFunc("/admin/addon", user.Auth(addonHandler))
+
http.HandleFunc("/admin/configure", user.Auth(configHandler))
http.HandleFunc("/admin/configure/users", user.Auth(configUsersHandler))
http.HandleFunc("/admin/configure/users/edit", user.Auth(configUsersEditHandler))
diff --git a/system/admin/static/dashboard/css/admin.css b/system/admin/static/dashboard/css/admin.css
index ce533a4..a977afb 100644
--- a/system/admin/static/dashboard/css/admin.css
+++ b/system/admin/static/dashboard/css/admin.css
@@ -191,6 +191,23 @@ li:hover .quick-delete-post, li:hover .delete-user {
top: -10px;
}
+tr.default-fields, tr.editor-fields {
+ margin-top: 20px;
+ margin-bottom: 20px;
+}
+
+.addon-meta a {
+ color: #7e7e7e;
+ text-decoration: underline;
+ font-style: italic;
+}
+
+.addon-meta {
+ display: block;
+ color: #9e9e9e;
+ font-size: 12px;
+}
+
/* OVERRIDE Bootstrap + Materialize conflicts */
.iso-texteditor.input-field label {
color: #9e9e9e;
diff --git a/system/admin/upload/upload.go b/system/admin/upload/upload.go
index 323f371..486f55c 100644
--- a/system/admin/upload/upload.go
+++ b/system/admin/upload/upload.go
@@ -14,7 +14,7 @@ import (
func StoreFiles(req *http.Request) (map[string]string, error) {
err := req.ParseMultipartForm(1024 * 1024 * 4) // maxMemory 4MB
if err != nil {
- return nil, fmt.Errorf("%s", err)
+ return nil, err
}
ts := req.FormValue("timestamp") // timestamp in milliseconds since unix epoch