app.gno
3.08 Kb · 112 lines
1package home
2
3import (
4 "chain"
5 "chain/runtime/unsafe"
6 "strings"
7
8 "gno.land/r/nym-alexiscolin000/gnoradio/catalog/v1"
9 "gno.land/r/nym-alexiscolin000/gnoradio/data"
10)
11
12// The site's two settings live in the data realm, "home/config", so a later
13// home release keeps them: "app" is the GnoRadio app (dApp) that pages link
14// to, "" hides the links; "contact" is an extra way to reach the moderator,
15// shown with the notice path, "" hides the line (the on-chain Report needs
16// none). Only the catalog admin sets them: home has no admin of its own.
17const (
18 cConfig = "home/config"
19 maxAppURL = 100
20 maxContact = 120
21)
22
23// init makes home/config while this realm is the home writer (release 1 at
24// deploy); a later release finds it made.
25func init(cur realm) {
26 if data.Writer("home") == cur.PkgPath() {
27 data.Make(cross(cur), cConfig)
28 }
29}
30
31// Ready tells the data realm that this release is on chain (see
32// data.Propose). Anyone may call it.
33func Ready(cur realm) {
34 noPayment()
35 data.Ready(cross(cur))
36}
37
38func noPayment() {
39 if len(unsafe.OriginSend()) != 0 {
40 panic("home: this action does not take coins")
41 }
42}
43
44func onlyCatalogAdmin(cur realm) {
45 if !cur.Previous().IsUserCall() || cur.Previous().Address() != catalog.Admin() {
46 panic("home: catalog admin only")
47 }
48 noPayment()
49}
50
51// SetAppURL sets the app link shown on every page: an https URL of at most
52// 100 characters from [A-Za-z0-9-._~/:%], or "" to remove it. Catalog admin only.
53func SetAppURL(cur realm, url string) {
54 onlyCatalogAdmin(cur)
55 if url != "" && !validAppURL(url) {
56 panic("home: app URL must be https://, at most 100 characters of A-Z a-z 0-9 - . _ ~ / : %")
57 }
58 data.Set(cross(cur), cConfig, "app", url)
59 chain.Emit("AppURLSet", "url", url)
60}
61
62// SetContact sets the contact line shown next to the notice path (a web form,
63// a postal address…): one line of at most 120 characters, or "" to remove it.
64// Catalog admin only.
65func SetContact(cur realm, line string) {
66 onlyCatalogAdmin(cur)
67 line = strings.TrimSpace(line)
68 if len(line) > maxContact || strings.ContainsAny(line, "\r\n") {
69 panic("home: the contact is one line of at most 120 characters")
70 }
71 data.Set(cross(cur), cConfig, "contact", line)
72 chain.Emit("ContactSet", "contact", line)
73}
74
75func validAppURL(u string) bool {
76 if !strings.HasPrefix(u, "https://") || len(u) <= len("https://") || len(u) > maxAppURL {
77 return false
78 }
79 for _, c := range u {
80 switch {
81 case c >= 'a' && c <= 'z', c >= 'A' && c <= 'Z', c >= '0' && c <= '9':
82 case strings.ContainsRune("-._~/:%", c):
83 default:
84 return false
85 }
86 }
87 return true
88}
89
90// appURL and contact read the settings.
91// appURL is checked again when read: a later release may write it.
92func appURL() string {
93 if v, _ := data.Get(cConfig, "app"); validAppURL(v) {
94 return v
95 }
96 return ""
97}
98
99func contact() string {
100 v, _ := data.Get(cConfig, "contact")
101 return v
102}
103
104// inApp links an app route (e.g. "track/12", "" for the player), or returns
105// "" when no app URL is set.
106func inApp(route, label string) string {
107 u := appURL()
108 if u == "" {
109 return ""
110 }
111 return "[" + label + "](" + strings.TrimRight(u, "/") + "/" + route + ")"
112}