Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

page.gno

3.26 Kb · 122 lines
  1package wiki
  2
  3import (
  4	"time"
  5
  6	"gno.land/p/moul/ulist/v0"
  7)
  8
  9// Protection is a page's edit gate. The engine stores it and reports it; it
 10// never enforces it, because authority belongs to the realm that owns the
 11// wiki, not to a pure library. See Wiki.Edit.
 12type Protection uint8
 13
 14const (
 15	Open          Protection = iota // anyone the realm lets through
 16	SemiProtected                   // realm-defined trusted editors
 17	Locked                          // stewards only
 18)
 19
 20// String returns the protection level's display name.
 21func (p Protection) String() string {
 22	switch p {
 23	case SemiProtected:
 24		return "semi-protected"
 25	case Locked:
 26		return "locked"
 27	default:
 28		return "open"
 29	}
 30}
 31
 32// ParseProtection maps a user-supplied level name onto a Protection.
 33func ParseProtection(s string) (Protection, error) {
 34	switch s {
 35	case "open", "":
 36		return Open, nil
 37	case "semi", "semi-protected":
 38		return SemiProtected, nil
 39	case "locked":
 40		return Locked, nil
 41	}
 42	return Open, ErrBadProtection
 43}
 44
 45// Page is one title's history plus the indexes derived from its current
 46// revision. Revisions live in a ulist: append is O(1) and does not rewrite the
 47// existing entries, which matters when a popular page accumulates thousands of
 48// edits and every write would otherwise re-serialize the whole slice.
 49type Page struct {
 50	Title      Title
 51	Protection Protection
 52	Created    time.Time
 53	Blanked    bool // a steward blanked it; history is retained
 54
 55	revs *ulist.List // *Revision, oldest first
 56	head *Revision
 57
 58	redirect string   // target title, "" when this page is not a redirect
 59	links    []string // outgoing wikilink target keys of head
 60	cats     []string // category keys head belongs to
 61}
 62
 63// Head returns the current revision, or nil for a page with no revisions.
 64func (p *Page) Head() *Revision { return p.head }
 65
 66// NumRevisions returns how many revisions the page has.
 67func (p *Page) NumRevisions() int { return p.revs.Size() }
 68
 69// Redirect returns the title this page redirects to, or "" if it does not.
 70func (p *Page) Redirect() string { return p.redirect }
 71
 72// Body returns the current text and whether it is held on chain.
 73func (p *Page) Body() (string, bool) {
 74	if p.head == nil {
 75		return "", false
 76	}
 77	return p.head.Body()
 78}
 79
 80// Revision returns the revision with the given id, or nil.
 81func (p *Page) Revision(id uint64) *Revision {
 82	var found *Revision
 83	p.revs.Iterator(0, p.revs.Size()-1, func(_ int, v any) bool {
 84		r := v.(*Revision)
 85		if r.ID == id {
 86			found = r
 87			return true
 88		}
 89		return false
 90	})
 91	return found
 92}
 93
 94// History returns up to count revisions, newest first, skipping the newest
 95// offset of them.
 96func (p *Page) History(offset, count int) []*Revision {
 97	out := []*Revision{}
 98	if count <= 0 {
 99		return out
100	}
101	size := p.revs.Size()
102	// Walk backwards: index size-1 is the newest.
103	for i := size - 1 - offset; i >= 0 && len(out) < count; i-- {
104		out = append(out, p.revs.MustGet(i).(*Revision))
105	}
106	return out
107}
108
109// Contributors returns the distinct authors of the page, oldest edit first.
110func (p *Page) Contributors() []address {
111	seen := map[string]bool{}
112	out := []address{}
113	p.revs.Iterator(0, p.revs.Size()-1, func(_ int, v any) bool {
114		r := v.(*Revision)
115		if !seen[r.Author.String()] {
116			seen[r.Author.String()] = true
117			out = append(out, r.Author)
118		}
119		return false
120	})
121	return out
122}