iw/app/models/page.go

267 lines
8.3 KiB
Go

package models
import (
"time"
"github.com/revel/revel"
"github.com/mikkolehtisalo/revel/acl"
"regexp"
"html"
"strings"
"fmt"
//. "github.com/mikkolehtisalo/revel/common"
)
type Page struct {
Page_id string
Wiki_id string
Path string
Title string
Create_user string
Readacl string
Writeacl string
Adminacl string
Stopinheritation bool
Index int
Depth int
Status string
Modified time.Time
MatchedPermissions []string
Loaded bool `json:"loaded"`
}
// Sets modified time
// Updates depth automatically
func (p *Page) Save(save_activity bool) {
revel.TRACE.Printf("Page Save(): %+v", p)
db := get_db()
defer db.Close()
// Update depth
update_depth(p)
p.Modified = time.Now()
_, err := db.Exec("insert into pages(page_id, wiki_id, path, title, create_user, readacl, writeacl, adminacl, stopinheritation, index, depth, modified, status) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)",
p.Page_id, p.Wiki_id, p.Path, p.Title, p.Create_user, p.Readacl, p.Writeacl, p.Adminacl, p.Stopinheritation, p.Index, p.Depth, p.Modified, p.Status )
if err != nil {
revel.ERROR.Printf("Page Save(): failed with %+v", err)
}
if save_activity {
SaveActivity(p)
}
}
// Wiki id, one page id, path
// The pages in path must either exist in database or match given single id (in case target is not already in database)
func valid_path(w string, me string, p string) bool {
valid := true
split_path := strings.Split(p, "/")
for _, part := range split_path {
x := GetPage(w, part)
if part != me && x.Page_id != part {
valid = false
}
}
return valid
}
func (p *Page) Validate(v *revel.Validation) {
// Required fields
v.Required(p.Page_id)
v.Required(p.Wiki_id)
v.Required(p.Path)
v.Required(p.Title)
// Match against regexp patterns
v.Match(p.Wiki_id, regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")).Message("Wiki_id not UUID?")
v.Match(p.Page_id, regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")).Message("Page_id not UUID?")
// Escape HTML from the fields that might be rendered to users
p.Title = html.EscapeString(p.Title)
// Validate Path
v.Required(valid_path(p.Wiki_id, p.Page_id, p.Path)).Message(fmt.Sprintf("Path is probably invalid: %+v", p.Path))
}
func has_children(page Page) bool {
result := false
pages := Get_children(page)
if len(pages) > 0 {
result = true
}
return result
}
// Only the next level
func Get_children(page Page) []Page {
revel.TRACE.Printf("Page Get_children(): %+v", page)
pages := []Page{}
db := get_db()
defer db.Close()
path := page.Path + "/%"
db.Select(&pages, "select * from pages p1 where p1.status='ACTIVE' and not exists (select * from pages p2 where p1.wiki_id=p2.wiki_id and p1.page_id=p2.page_id and p2.modified>p1.modified) and p1.path like $1 and p1.depth=$2 order by index", path, page.Depth + 1)
return pages
}
// Used only from Wiki.Delete()
func DeletePages(wiki string, user string) {
revel.TRACE.Printf("Page DeletePages(): wiki %+v, user %+v", wiki, user)
pages := []Page{}
db := get_db()
defer db.Close()
db.Select(&pages, "select * from pages p1 where p1.status='ACTIVE' and p1.wiki_id=uuid_in($1) and not exists (select * from pages p2 where p1.wiki_id=p2.wiki_id and p1.page_id=p2.page_id and p2.modified>p1.modified)",
wiki)
for _, page := range pages {
DeletePage(wiki, page.Page_id, user, false)
}
}
func ListPages(wiki_id string, node string) []Page {
revel.TRACE.Printf("ListPages() wiki_id: %+v, node: %+v", wiki_id, node)
pages := []Page{}
db := get_db()
defer db.Close()
if len(node) > 0 {
// Get the parent, and find what's under it - if anything
p := GetPage(wiki_id, node)
path := p.Path + "/%"
depth := p.Depth + 1
db.Select(&pages, "select * from pages p1 where p1.status='ACTIVE' and not exists (select * from pages p2 where p1.wiki_id=p2.wiki_id and p1.page_id=p2.page_id and p2.modified>p1.modified) and p1.path like $1 and p1.depth=$2", path, depth)
} else {
db.Select(&pages, "select * from pages p1 where p1.status='ACTIVE' and p1.wiki_id=uuid_in($1) and p1.depth=0 and not exists (select * from pages p2 where p1.wiki_id=p2.wiki_id and p1.page_id=p2.page_id and p2.modified>p1.modified)", wiki_id)
}
// Update children status
for x, _ := range pages {
pages[x].Loaded = !has_children(pages[x])
revel.TRACE.Printf("Page %+v children: %+v", pages[x], has_children(pages[x]))
}
revel.TRACE.Printf("ListPages returning %+v", pages)
return pages
}
func DeletePage(wiki_id string, page_id string, user string, save_activity bool) {
revel.TRACE.Printf("DeletePage() wiki: %+v, page: %+v, user: %+v", wiki_id, page_id, user)
page := GetPage(wiki_id, page_id)
pages := Get_children(page)
// For looping
pages = append(pages, page)
for _, p := range pages {
if p.Wiki_id != "" {
p.Status = "DELETED"
p.Create_user = user
p.Save(save_activity)
// Delete ContentField too
cf := GetContent(p.Wiki_id, p.Page_id, nil)
cf.Status = "DELETED"
cf.Create_user = user
cf.Save(save_activity)
}
}
}
func GetPage(wiki_id string, page_id string) Page {
revel.TRACE.Printf("Page GetPage(): page: %+v wiki: %+v", page_id, wiki_id)
pages := []Page{}
page := Page{}
db := get_db()
defer db.Close()
db.Select(&pages, "select * from pages p1 where p1.wiki_id=uuid_in($1) and p1.page_id=uuid_in($2) and p1.status='ACTIVE' and not exists (select * from pages p2 where p1.wiki_id=p2.wiki_id and p1.page_id=p2.page_id and p2.modified>p1.modified)",
wiki_id, page_id)
if len(pages)>0 {
page = pages[0]
}
return page
}
func GetPageAllStatuses(wiki_id string, page_id string) Page {
revel.TRACE.Printf("Page GetPageAllStatuses(): page: %+v wiki: %+v", page_id, wiki_id)
pages := []Page{}
page := Page{}
db := get_db()
defer db.Close()
db.Select(&pages, "select * from pages p1 where p1.wiki_id=uuid_in($1) and p1.page_id=uuid_in($2) and not exists (select * from pages p2 where p1.wiki_id=p2.wiki_id and p1.page_id=p2.page_id and p2.modified>p1.modified)",
wiki_id, page_id)
if len(pages)>0 {
page = pages[0]
}
return page
}
func update_depth(page *Page) {
split := strings.Split(page.Path, "/")
page.Depth = len(split) - 1
}
// ACL stuff
// Build ACL entry for reference
func (p Page) BuildACLEntry(reference string) acl.ACLEntry {
revel.TRACE.Printf("BuildACLEntry() %+v", reference)
var entry acl.ACLEntry
if reference != ("page:"+p.Wiki_id+"/"+p.Page_id) {
if strings.Index(reference, "page") == 0 {
// We are not working on this copy, get from database
re := regexp.MustCompile("page:([^/]*)/(.*)")
m := re.FindStringSubmatch(reference)
wref := m[1]
pref := m[2]
pa := GetPage(wref, pref)
entry = entry_helper(pa.Readacl, pa.Writeacl, pa.Adminacl, reference, pa)
} else {
// This must be wiki!
re := regexp.MustCompile("wiki:(.*)")
ref := re.FindStringSubmatch(reference)[1]
wi := GetWiki(ref)
entry = entry_helper(wi.Readacl, wi.Writeacl, wi.Adminacl, reference, wi)
}
} else {
// It's exactly the originating item!
entry = entry_helper(p.Readacl, p.Writeacl, p.Adminacl, reference, p)
}
return entry
}
// Set the matched permissions to a variable
func (p Page) SetMatched(permissions []string) interface{} {
p.MatchedPermissions = permissions
return p
}
// Building parent information
func (p Page) BuildACLParent() string {
if p.Depth==0 {
return "wiki:"+p.Wiki_id
} else {
pslice := strings.Split(p.Path, "/")
return "page:"+p.Wiki_id + "/" + pslice[len(pslice)-2]
}
}
// Wiki+page ids...
func (p Page) BuildACLReference() string {
return "page:"+p.Wiki_id+"/"+p.Page_id
}
// Set on data
func (p Page) BuildACLInheritation() bool {
return !p.Stopinheritation
}