Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
Checking mergeability… Don't worry, you can still create the pull request.
  • 4 commits
  • 10 files changed
  • 0 commit comments
  • 1 contributor
View
2  README.md
@@ -24,7 +24,7 @@ Stuff already working
- Versioning of site state: any install/uninstall/configuration will create a newer version of site state, so you can revert to any previous version.
- Custom view editor: run any queries at any part of the page. The query builder generates excerpts, paging links etc.
- Any file is displayed as is, you dont even have to use any dynamic features of the CMS, you can simply copy-paste html pages and they will be displayed as if they were
-dynamic content. In the templates, PHP-like require functionality is available.
+dynamic content. Even in these html files, PHP-like require functionality is available.
Demo
=======
View
5 api/mod/mod.go
@@ -9,6 +9,7 @@ import (
"github.com/opesun/hypecms/modules/user"
"github.com/opesun/hypecms/modules/display_editor"
"github.com/opesun/hypecms/modules/template_editor"
+ "github.com/opesun/hypecms/modules/custom_actions"
)
func GetHook(modname string, method string) func(*context.Uni) error {
@@ -16,12 +17,12 @@ func GetHook(modname string, method string) func(*context.Uni) error {
switch modname {
case "content":
r = content.Hooks[method]
- case "tag":
-
case "user":
r = user.Hooks[method]
case "skeleton":
r = skeleton.Hooks[method]
+ case "custom_actions":
+ r = custom_actions.Hooks[method]
case "display_editor":
r = display_editor.Hooks[method]
case "template_editor":
View
98 model/patterns/patterns.go
@@ -0,0 +1,98 @@
+// Package pattern contains (should, lol) reusable database patterns.
+package patterns
+
+
+import(
+ "github.com/opesun/resolver"
+ "github.com/opesun/hypecms/model/basic"
+ "labix.org/v2/mgo/bson"
+ "labix.org/v2/mgo"
+ "fmt"
+)
+
+type m map[string]interface{}
+
+func ToIdWithCare(id interface{}) bson.ObjectId {
+ switch val := id.(type) {
+ case bson.ObjectId:
+ case string:
+ id = bson.ObjectIdHex(basic.StripId(val))
+ default:
+ panic(fmt.Sprintf("Can't create bson.ObjectId out of %T", val))
+ }
+ return id.(bson.ObjectId)
+}
+
+// Finds a doc by field-value equality.
+func FindEq(db *mgo.Database, coll, field string, value interface{}) (map[string]interface{}, error) {
+ if field == "_id" {
+ value = ToIdWithCare(value)
+ }
+ q := m{field: value}
+ var res interface{}
+ err := db.C(coll).Find(q).One(&res)
+ if err != nil { return nil, err }
+ if res == nil { return nil, fmt.Errorf("Can't find document at FindEq.") }
+ doc := basic.Convert(res.(bson.M)).(map[string]interface{})
+ return doc, nil
+}
+
+func FindChildren(db *mgo.Database, children_coll, parent_fk_field string, parent_id bson.ObjectId) ([]interface{}, error) {
+ q := m{parent_fk_field: parent_id}
+ var children []interface{}
+ err := db.C(children_coll).Find(q).All(&children)
+ if err != nil { return nil, err }
+ if children == nil { return nil, fmt.Errorf("Can't find children.") }
+ children = basic.Convert(children).([]interface{})
+ dont_query := map[string]interface{}{"password":0}
+ resolver.ResolveAll(db, children, dont_query)
+ return children, nil
+}
+
+// Finds a document in [parent_coll] collection based on [field] [value] equality, then queries
+// [children_coll] for documents which has the _id of that document in their parent_fk_field.
+// Returns children list only, no parent.
+func FindParentAndChildren(db *mgo.Database, parent_coll string, field string, value interface{}, children_coll, parent_fk_field string) ([]interface{}, error) {
+ parent, err := FindEq(db, parent_coll, field, value)
+ if err != nil { return nil, err }
+ return FindChildren(db, children_coll, parent_fk_field, parent["_id"].(bson.ObjectId))
+}
+
+func FieldStartsWith(db *mgo.Database, collname, fieldname, val string) ([]interface{}, error) {
+ var res []interface{}
+ q := m{fieldname: bson.RegEx{ "^" + val, "u"}}
+ err := db.C(collname).Find(q).All(&res)
+ if err != nil { return nil, err }
+ if res == nil { return nil, fmt.Errorf("Can't find %v starting with %v.", fieldname, val) }
+ res = basic.Convert(res).([]interface{})
+ return res, nil
+}
+
+// Takes a collection, a field and a value and pulls that value from all docs in the collection.
+// Caution, it does not take care about string id values, or even worse, non stripped string id values.
+func PullFromAll(db *mgo.Database, collname, fieldname string, value interface{}) error {
+ _, err := db.C(collname).UpdateAll(nil, m{"$pull":m{fieldname:value}})
+ return err
+}
+
+// Removes a doc by id, takes care about non stripped string ids.
+func DeleteById(db *mgo.Database, collname string, id interface{}) error {
+ id = ToIdWithCare(id)
+ q := m{"_id": id}
+ return db.C(collname).Remove(q)
+}
+
+func IncAll(db *mgo.Database, collname string, ids []bson.ObjectId, fieldname string, num int) error {
+ q := m{
+ "_id": m{
+ "$in": ids,
+ },
+ }
+ upd := m{
+ "$inc": m{
+ fieldname: num,
+ },
+ }
+ _, err := db.C(collname).UpdateAll(q, upd)
+ return err
+}
View
51 modules/content/model/tags.go
@@ -2,8 +2,7 @@ package content_model
import (
"github.com/opesun/hypecms/model/basic"
- "github.com/opesun/hypecms/model/scut"
- "github.com/opesun/resolver"
+ "github.com/opesun/hypecms/model/patterns"
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
"github.com/opesun/slugify"
@@ -49,14 +48,13 @@ func separateTags(db *mgo.Database, slug_sl []string) ([]bson.ObjectId, []string
}
return ret_ids, mToSSlice(contains)
}
+
func inc(db *mgo.Database, ids []bson.ObjectId) error {
- _, err := db.C(Tag_cname).UpdateAll(m{"_id": m{"$in":ids}}, m{"$inc":m{Count_fieldname:1 }})
- return err
+ return patterns.IncAll(db, Tag_cname, ids, Count_fieldname, 1)
}
func dec(db *mgo.Database, ids []bson.ObjectId) error {
- _, err := db.C(Tag_cname).UpdateAll(m{"_id": m{"$in":ids}}, m{"$inc":m{Count_fieldname:-1 }})
- return err
+ return patterns.IncAll(db, Tag_cname, ids, Count_fieldname, -1)
}
func insert(db *mgo.Database, tag_sl []string) []bson.ObjectId {
@@ -188,46 +186,21 @@ func PullTags(db *mgo.Database, content_id string, tag_ids []string) error {
return db.C(Cname).Update(q, upd)
}
+// Deletes a tag entirely.
func DeleteTag(db *mgo.Database, tag_id string) error {
- tag_idstr := basic.StripId(tag_id)
- tag_bsonid := bson.ObjectIdHex(tag_idstr)
- err := db.C(Tag_cname).Remove(m{"_id":tag_bsonid})
+ err := patterns.DeleteById(db, Tag_cname, tag_id)
if err != nil {return err}
- return PullTagFromAll(db, tag_bsonid)
+ return PullTagFromAll(db, tag_id)
}
-func PullTagFromAll(db *mgo.Database, tag_id bson.ObjectId) error {
- _, err := db.C(Tag_cname).UpdateAll(nil, m{"$pull":m{Tag_fieldname:tag_id}})
- return err
+func PullTagFromAll(db *mgo.Database, tag_id string) error {
+ return patterns.PullFromAll(db, Tag_cname, Tag_fieldname, patterns.ToIdWithCare(tag_id))
}
-func ListContentsByTag(db *mgo.Database, field, value string) ([]interface{}, error) {
- q := m{field: value}
- if field == "_id" {
- q[field] = bson.ObjectIdHex(basic.StripId(value))
- }
- var res interface{}
- err := db.C(Tag_cname).Find(q).One(&res)
- if err != nil { return nil, err }
- if res == nil { return nil, fmt.Errorf("Can't find tag by slug.") }
- tag := basic.Convert(res.(bson.M)).(map[string]interface{})
- q = m{Tag_fieldname: tag["_id"].(bson.ObjectId)}
- var contents []interface{}
- err = db.C(Cname).Find(q).All(&contents)
- if err != nil { return nil, err }
- if contents == nil { return nil, fmt.Errorf("Can't find contents.") }
- contents = basic.Convert(contents).([]interface{})
- dont_query := map[string]interface{}{"password":0}
- resolver.ResolveAll(db, contents, dont_query)
- scut.Strify(contents)
- return contents, nil
+func ListContentsByTag(db *mgo.Database, field string, value interface{}) ([]interface{}, error) {
+ return patterns.FindParentAndChildren(db, Tag_cname, field, value, Cname, Tag_fieldname)
}
func TagSearch(db *mgo.Database, tag_slug string) ([]interface{}, error) {
- var res []interface{}
- q := m{"slug": bson.RegEx{ "^" + tag_slug, "u"}}
- err := db.C(Tag_cname).Find(q).All(&res)
- if err != nil { return nil, err }
- if res == nil { return nil, fmt.Errorf("Can't find tags.") }
- return res, nil
+ return patterns.FieldStartsWith(db, Tag_cname, "slug", tag_slug)
}
View
60 modules/custom_actions/custom_actions.go
@@ -0,0 +1,60 @@
+package custom_actions
+
+import (
+ "github.com/opesun/hypecms/api/context"
+ ca_model "github.com/opesun/hypecms/modules/custom_actions/model"
+ "github.com/opesun/jsonp"
+ "labix.org/v2/mgo/bson"
+ "fmt"
+)
+
+type m map[string]interface{}
+
+var Hooks = map[string]func(*context.Uni) error {
+ "Back": Back,
+ "Install": Install,
+ "Uninstall": Uninstall,
+ "Test": Test,
+ "AD": AD,
+}
+
+func Back(uni *context.Uni) error {
+ action := uni.Dat["_action"].(string)
+ act, has := jsonp.GetM(uni.Opt, "Modules.custom_actions.actions." + action)
+ if !has { return fmt.Errorf("Can't find action %v in custom actions module.", action) }
+ return ca_model.RunAction(uni.Db, uni.Dat["_user"].(map[string]interface{}), act, map[string][]string(uni.Req.Form), action)
+}
+
+func Test(uni *context.Uni) error {
+ return nil
+}
+
+func AD(uni *context.Uni) error {
+ uni.Dat["_points"] = []string{"custom_action/index"}
+ // You can dispatch your different admin views here, based on url structure.
+ return nil
+}
+
+func Install(uni *context.Uni) error {
+ id := uni.Dat["_option_id"].(bson.ObjectId)
+ custom_action_options := m{
+ }
+ q := m{"_id": id}
+ upd := m{
+ "$set": m{
+ "Modules.custom_action": custom_action_options,
+ },
+ }
+ return uni.Db.C("options").Update(q, upd)
+}
+
+func Uninstall(uni *context.Uni) error {
+ id := uni.Dat["_option_id"].(bson.ObjectId)
+ q := m{"_id": id}
+ upd := m{
+ "$unset": m{
+ "Modules.custom_action": 1,
+ },
+ }
+ return uni.Db.C("options").Update(q, upd)
+}
View
81 modules/custom_actions/model/custom_actions.go
@@ -0,0 +1,81 @@
+package custom_actions_model
+
+import(
+ "labix.org/v2/mgo/bson"
+ "github.com/opesun/hypecms/model/patterns"
+ "github.com/opesun/extract"
+ "github.com/opesun/jsonp"
+ "labix.org/v2/mgo"
+ "fmt"
+)
+
+type m map[string]interface{}
+
+func isLegalOption(vote_options []string, input_vote string) bool {
+ in := false
+ for _, v := range vote_options {
+ if v == input_vote {
+ in = true
+ }
+ }
+ return in
+}
+
+func generateVoteQuery(doc_id, user_id bson.ObjectId, vote_options []string) map[string]interface{} {
+ q := map[string]interface{}{"_id": doc_id}
+ and := []interface{}{}
+ for _, v := range vote_options {
+ r := m{
+ v: m{
+ "$ne": user_id,
+ },
+ }
+ and = append(and, r)
+ }
+ q["$and"] = and
+ return q
+}
+
+// Vote options are mutually exclusive.
+// Example:
+// {
+// "type": "vote"
+// "c": "contents",
+// "vote_options": ["like", "dislike"]
+// }
+func Vote(db *mgo.Database, user, action map[string]interface{}, inp map[string][]string) error {
+ collection := action["c"].(string)
+ vote_options := jsonp.ToStringSlice(action["vote_options"].([]interface{}))
+ rules := map[string]interface{}{
+ "document_id": "must",
+ "vote_option": "must",
+ }
+ dat, err := extract.New(rules).Extract(inp)
+ if err != nil { return err }
+ input_vote := dat["vote_option"].(string)
+ if !isLegalOption(vote_options, input_vote) { return fmt.Errorf("Not a legal option.") }
+ doc_id := patterns.ToIdWithCare(dat["document_id"].(string))
+ user_id := user["_id"].(bson.ObjectId)
+ q := generateVoteQuery(doc_id, user_id, vote_options)
+ upd := m{
+ "$addToSet": m{
+ input_vote: user_id,
+ },
+ "$inc": m{
+ input_vote + "_count": 1,
+ },
+ }
+ return db.C(collection).Update(q, upd)
+}
+
+func RunAction(db *mgo.Database, user, action map[string]interface{}, inp map[string][]string, action_name string) error {
+ typ := action["type"].(string)
+ var r error
+ switch typ {
+ case "vote":
+ r = Vote(db, user, action, inp)
+ default:
+ r = fmt.Errorf("Unkown action %v at RunAction.", action_name)
+ }
+ return r
+}
View
13 modules/custom_actions/tpl/edit.tpl
@@ -0,0 +1,13 @@
+{{require admin/header.t}}
+{{require display_editor/sidebar.t}}
+<form action="/b/display_editor/save" method="post">
+ Name:<br/>
+ <input name="name" value="{{.point.name}}"><br/>
+ <input type="hidden" name="prev_name" value="{{.point.name}}"><br/>
+ <br/>
+ Queries:<br />
+ <textarea name="queries" rows="30" cols="75">{{.point.queries}}</textarea><br />
+ <br />
+ <input type="submit">
+</form>
+{{require admin/footer.t}}
View
4 modules/custom_actions/tpl/help.tpl
@@ -0,0 +1,4 @@
+{{require admin/header.t}}
+{{require custom_action/sidebar.t}}
+There will be some help here.
+{{require admin/footer.t}}
View
27 modules/custom_actions/tpl/search.tpl
@@ -0,0 +1,27 @@
+{{require admin/header.t}}
+{{require custom_action/sidebar.t}}
+
+{{if .has_points}}
+ Search:<br />
+ <form action="/admin/custom_action">
+ <input name="point-name" value="{{.search}}">
+ <input type="submit">
+ </form>
+ {{range .point_names}}
+ <a class="delete" href="/b/custom_action/delete?name={{.}}">-</a> <a href="/admin/custom_action/edit/{{.}}">{{.}}</a><br />
+ {{else}}
+ Nothing matches your search criteria.
+ {{end}}
+{{else}}
+ There are no display points yet.<br />
+{{end}}
+
+<br />
+<br />
+Create new:<br />
+<form action="/b/custom_action/new">
+ <input name="name">
+ <input type="submit">
+</form>
+
+{{require admin/footer.t}}
View
12 modules/custom_actions/tpl/sidebar.t
@@ -0,0 +1,12 @@
+<div id="left-sidebar">
+<ul>
+ <li>
+ <a href="/admin/custom_action">Search</a>
+ </li>
+ <li>
+ <a href="/admin/custom_action/help">Help</a>
+ </li>
+</ul>
+</div>
+
+<div id="inner-content">

No commit comments for this range

Something went wrong with that request. Please try again.