boards.gno
4.54 Kb · 156 lines
1package boards2
2
3import (
4 "gno.land/p/gnoland/boards/exts/permissions/v0"
5 "gno.land/p/gnoland/boards/v0"
6 "gno.land/p/moul/realmpath/v0"
7 "gno.land/p/moul/txlink/v0"
8 "gno.land/p/nt/bptree/v0"
9)
10
11const (
12 realmPkgPath = "gno.land/r/gnoland/boards2/v0"
13 gRealmPath = "/r/gnoland/boards2/v0"
14 initialOwner address = "g1skl80cuz8zq3lul9pgz5pc35l2pfzgxgfpsqkx" // GovDAO T1 multisig
15)
16
17var (
18 // RealmLink contains Boards2 realm link.
19 // It can be used to generate board TX links from other realms.
20 RealmLink = txlink.Realm(realmPkgPath)
21
22 // RequiredAccountAmount contains the required account amount for open board interactions.
23 // The amount requirement is not applied to members that were invited to an open board.
24 // Amount is defined as ugnot.
25 RequiredAccountAmount = int64(3_000_000_000)
26
27 // Notice contains an optional message that is displayed globally within the realm.
28 Notice string
29
30 // Help contains optional Markdown with Boards2 realm help.
31 Help string
32)
33
34// TODO: Refactor globals in favor of a cleaner pattern
35var (
36 gListedBoardsByID bptree.BPTree // string(id) -> *boards.Board
37 gInviteRequests bptree.BPTree // string(board id) -> *bptree.BPTree(address -> time.Time)
38 gBannedUsers bptree.BPTree // string(board id) -> *bptree.BPTree(address -> time.Time)
39 gLocked struct {
40 realm bool
41 realmMembers bool
42 }
43)
44
45var (
46 gBoards = boards.NewStorage()
47 gBoardsSequence = boards.NewIdentifierGenerator()
48 gPerms = initRealmPermissions(initialOwner)
49)
50
51// initRealmPermissions returns the default realm permissions.
52func initRealmPermissions(owners ...address) boards.Permissions {
53 perms := permissions.New(
54 permissions.UseSingleUserRole(),
55 permissions.WithSuperRole(RoleOwner),
56 )
57 perms.AddRole(RoleAdmin, PermissionBoardCreate)
58 for _, owner := range owners {
59 perms.SetUserRoles(owner, RoleOwner)
60 }
61
62 perms.ValidateFunc(PermissionBoardCreate, validateBasicBoardCreate)
63 perms.ValidateFunc(PermissionMemberInvite, validateBasicMemberInvite)
64 perms.ValidateFunc(PermissionRoleChange, validateBasicRoleChange)
65 return perms
66}
67
68func init() {
69 addBoard(gBoardsSequence.Next(), initialOwner, "OpenDiscussions", true, true)
70}
71
72// getInviteRequests returns invite requests for a board.
73func getInviteRequests(boardID boards.ID) (_ *bptree.BPTree, found bool) {
74 v := gInviteRequests.Get(boardID.Key())
75 if v == nil {
76 return nil, false
77 }
78 return v.(*bptree.BPTree), true
79}
80
81// getBannedUsers returns banned users within a board.
82func getBannedUsers(boardID boards.ID) (_ *bptree.BPTree, found bool) {
83 v := gBannedUsers.Get(boardID.Key())
84 if v == nil {
85 return nil, false
86 }
87 return v.(*bptree.BPTree), true
88}
89
90// mustGetBoardByName returns a board or panics when it's not found.
91func mustGetBoardByName(name string) *boards.Board {
92 board, found := gBoards.GetByName(name)
93 if !found {
94 panic("board does not exist with name: " + name)
95 }
96 return board
97}
98
99// mustGetBoard returns a board or panics when it's not found.
100func mustGetBoard(id boards.ID) *boards.Board {
101 board, found := gBoards.Get(id)
102 if !found {
103 panic("board does not exist with ID: " + id.String())
104 }
105 return board
106}
107
108// getThread returns a board thread.
109func getThread(board *boards.Board, threadID boards.ID) (*boards.Post, bool) {
110 thread, found := board.Threads.Get(threadID)
111 if !found {
112 // When thread is not found search it within hidden threads
113 meta := board.Meta.(*BoardMeta)
114 thread, found = meta.HiddenThreads.Get(threadID)
115 }
116 return thread, found
117}
118
119// getReply returns a thread comment or reply.
120func getReply(thread *boards.Post, replyID boards.ID) (*boards.Post, bool) {
121 meta := thread.Meta.(*ThreadMeta)
122 return meta.AllReplies.Get(replyID)
123}
124
125// mustGetThread returns a thread or panics when it's not found.
126func mustGetThread(board *boards.Board, threadID boards.ID) *boards.Post {
127 thread, found := getThread(board, threadID)
128 if !found {
129 panic("thread does not exist with ID: " + threadID.String())
130 }
131 return thread
132}
133
134// mustGetReply returns a reply or panics when it's not found.
135func mustGetReply(thread *boards.Post, replyID boards.ID) *boards.Post {
136 reply, found := getReply(thread, replyID)
137 if !found {
138 panic("reply does not exist with ID: " + replyID.String())
139 }
140 return reply
141}
142
143func mustGetPermissions(bid boards.ID) boards.Permissions {
144 if bid != 0 {
145 board := mustGetBoard(bid)
146 return board.Permissions
147 }
148 return gPerms
149}
150
151func parseRealmPath(path string) *realmpath.Request {
152 // Make sure request is using current realm path so paths can be parsed during Render
153 r := realmpath.Parse(path)
154 r.Realm = string(RealmLink)
155 return r
156}