diff options
Diffstat (limited to 'system/addon/addon.go')
-rw-r--r-- | system/addon/addon.go | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/system/addon/addon.go b/system/addon/addon.go new file mode 100644 index 0000000..51be9dc --- /dev/null +++ b/system/addon/addon.go @@ -0,0 +1,234 @@ +package addon + +import ( + "encoding/json" + "fmt" + "net/url" + "strings" + + "github.com/ponzu-cms/ponzu/system/db" + "github.com/ponzu-cms/ponzu/system/item" + + "github.com/tidwall/sjson" +) + +var ( + // Types is a record of addons, like content types, of addon_reverse_dns:interface{} + Types = make(map[string]func() interface{}) +) + +const ( + // StatusEnabled defines string status for Addon enabled state + StatusEnabled = "enabled" + // StatusDisabled defines string status for Addon disabled state + StatusDisabled = "disabled" +) + +// Meta contains the basic information about the addon +type Meta struct { + PonzuAddonName string `json:"addon_name"` + PonzuAddonAuthor string `json:"addon_author"` + PonzuAddonAuthorURL string `json:"addon_author_url"` + PonzuAddonVersion string `json:"addon_version"` + PonzuAddonReverseDNS string `json:"addon_reverse_dns"` + PonzuAddonStatus string `json:"addon_status"` +} + +// Addon contains information about a provided addon to the system +type Addon struct { + item.Item + Meta +} + +// Register constructs a new addon and registers it with the system. Meta is a +// addon.Meta and fn is a closure returning a pointer to your own addon type +func Register(m Meta, fn func() interface{}) Addon { + // get or create the reverse DNS identifier + if m.PonzuAddonReverseDNS == "" { + revDNS, err := reverseDNS(m) + if err != nil { + panic(err) + } + + m.PonzuAddonReverseDNS = revDNS + } + + Types[m.PonzuAddonReverseDNS] = fn + + a := Addon{Meta: m} + + err := register(a) + if err != nil { + panic(err) + } + + return a +} + +// register sets up the system to use the Addon by: +// 1. Validating the Addon struct +// 2. Saving it to the __addons bucket in DB with id/key = addon_reverse_dns +func register(a Addon) error { + if a.PonzuAddonName == "" { + return fmt.Errorf(`Addon must have valid Meta struct embedded: missing %s field.`, "PonzuAddonName") + } + if a.PonzuAddonAuthor == "" { + return fmt.Errorf(`Addon must have valid Meta struct embedded: missing %s field.`, "PonzuAddonAuthor") + } + if a.PonzuAddonAuthorURL == "" { + return fmt.Errorf(`Addon must have valid Meta struct embedded: missing %s field.`, "PonzuAddonAuthorURL") + } + if a.PonzuAddonVersion == "" { + return fmt.Errorf(`Addon must have valid Meta struct embedded: missing %s field.`, "PonzuAddonVersion") + } + + if _, ok := Types[a.PonzuAddonReverseDNS]; !ok { + return fmt.Errorf(`Addon "%s" has no record in the addons.Types map`, a.PonzuAddonName) + } + + // check if addon is already registered in db as addon_reverse_dns + if db.AddonExists(a.PonzuAddonReverseDNS) { + return nil + } + + // convert a.Item into usable data, Item{} => []byte(json) => map[string]interface{} + kv := make(map[string]interface{}) + + data, err := json.Marshal(a.Item) + if err != nil { + return err + } + + err = json.Unmarshal(data, &kv) + if err != nil { + return err + } + + // save new addon to db + vals := make(url.Values) + for k, v := range kv { + vals.Set(k, fmt.Sprintf("%v", v)) + } + + vals.Set("addon_name", a.PonzuAddonName) + vals.Set("addon_author", a.PonzuAddonAuthor) + vals.Set("addon_author_url", a.PonzuAddonAuthorURL) + vals.Set("addon_version", a.PonzuAddonVersion) + vals.Set("addon_reverse_dns", a.PonzuAddonReverseDNS) + vals.Set("addon_status", StatusDisabled) + + // db.SetAddon is like SetContent, but rather than the key being an int64 ID, + // we need it to be a string based on the addon_reverse_dns + kind, ok := Types[a.PonzuAddonReverseDNS] + if !ok { + return fmt.Errorf("Error: no addon to set with id: %s", a.PonzuAddonReverseDNS) + } + + err = db.SetAddon(vals, kind()) + if err != nil { + return err + } + + return nil +} + +// Deregister removes an addon from the system. `key` is the addon_reverse_dns +func Deregister(key string) error { + err := db.DeleteAddon(key) + if err != nil { + return err + } + + delete(Types, key) + return nil +} + +// Enable sets the addon status to `enabled`. `key` is the addon_reverse_dns +func Enable(key string) error { + err := setStatus(key, StatusEnabled) + if err != nil { + return err + } + + return nil +} + +// Disable sets the addon status to `disabled`. `key` is the addon_reverse_dns +func Disable(key string) error { + err := setStatus(key, StatusDisabled) + if err != nil { + return err + } + + return nil +} + +func setStatus(key, status string) error { + a, err := db.Addon(key) + if err != nil { + return err + } + + a, err = sjson.SetBytes(a, "addon_status", status) + if err != nil { + return err + } + + kind, ok := Types[key] + if !ok { + return fmt.Errorf("Error: no addon to set with id: %s", key) + } + + // convert json => map[string]interface{} => url.Values + var kv map[string]interface{} + err = json.Unmarshal(a, &kv) + if err != nil { + return err + } + + vals := make(url.Values) + for k, v := range kv { + switch v.(type) { + case []string: + s := v.([]string) + for i := range s { + if i == 0 { + vals.Set(k, s[i]) + } + + vals.Add(k, s[i]) + } + default: + vals.Set(k, fmt.Sprintf("%v", v)) + } + } + + err = db.SetAddon(vals, kind()) + if err != nil { + return err + } + + return nil +} + +func reverseDNS(meta Meta) (string, error) { + u, err := url.Parse(meta.PonzuAddonAuthorURL) + if err != nil { + return "", nil + } + + if u.Host == "" { + return "", fmt.Errorf(`Error parsing Addon Author URL: %s. Ensure URL is formatted as "scheme://hostname/path?query" (path & query optional)`, meta.PonzuAddonAuthorURL) + } + + name := strings.Replace(meta.PonzuAddonName, " ", "", -1) + + // reverse the host name parts, split on '.', ex. bosssauce.it => it.bosssauce + parts := strings.Split(u.Host, ".") + strap := make([]string, 0, len(parts)) + for i := len(parts) - 1; i >= 0; i-- { + strap = append(strap, parts[i]) + } + + return strings.Join(append(strap, name), "."), nil +} |