Zettelstore Contrib

Check-in [152d4d3bba]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Social: initial support of page-specific templates
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 152d4d3bba3436b3e989666f20602155fe9f6f6fd22d9fa70e4519d80b7826e5
User & Date: stern 2024-03-22 17:37:09
Context
2024-03-25
12:44
Social: move user-agent list to template-based site ... (check-in: 11335da40f user: stern tags: trunk)
2024-03-22
17:37
Social: initial support of page-specific templates ... (check-in: 152d4d3bba user: stern tags: trunk)
15:22
Social: refactor to improve code quality; always bind some symbol in rendering contexSocial: refactor to improve code quality; always bind some symbol in rendering contextt ... (check-in: c6c12e146f user: stern tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to social/go.mod.

1
2
3
4
5
module zettelstore.de/contrib/social

go 1.22

require zettelstore.de/sx.fossil v0.0.0-20240322135524-88a9890a8849




|
1
2
3
4
5
module zettelstore.de/contrib/social

go 1.22

require zettelstore.de/sx.fossil v0.0.0-20240322172300-7f216ac38787

Changes to social/go.sum.

1
2
zettelstore.de/sx.fossil v0.0.0-20240322135524-88a9890a8849 h1:fSf1uENjYEJpx/htJp5WPcrGA3URFfuWk5QRqYTjHdc=
zettelstore.de/sx.fossil v0.0.0-20240322135524-88a9890a8849/go.mod h1:/iGHxFXoo6GSV04PUkwaLuFrrCa5LMorxD73iLMAruI=
|
|
1
2
zettelstore.de/sx.fossil v0.0.0-20240322172300-7f216ac38787 h1:/0Bke+SqabK9tWy9tBykiCsOGr4Cwshp3Gc9WL3aMjA=
zettelstore.de/sx.fossil v0.0.0-20240322172300-7f216ac38787/go.mod h1:/iGHxFXoo6GSV04PUkwaLuFrrCa5LMorxD73iLMAruI=

Changes to social/web/wui/bindings.go.

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59


60
61
62
63
64
65
66
	return root, nil
}

var (
	specials = []*sxeval.Special{
		&sxbuiltins.QuoteS, &sxbuiltins.QuasiquoteS, // quote, quasiquote
		&sxbuiltins.UnquoteS, &sxbuiltins.UnquoteSplicingS, // unquote, unquote-splicing
		&sxbuiltins.DefDynS,   // defdyn
		&sxbuiltins.LetS,      // let
		&sxbuiltins.IfS,       // if
		&sxbuiltins.DefMacroS, // defmacro
		&sxbuiltins.BeginS,    // begin
	}
	builtins = []*sxeval.Builtin{
		&sxbuiltins.Equal,                // =
		&sxbuiltins.NullP,                // null?
		&sxbuiltins.Car, &sxbuiltins.Cdr, // car, cdr
		&sxbuiltins.Caar, &sxbuiltins.Cadr, // caar, cadr
		&sxbuiltins.Cadar,  // cadar


		&sxbuiltins.BoundP, // bound?
	}
)

func (wui *WebUI) bindExtra(root *sxeval.Binding) error {
	err := root.BindBuiltin(&sxeval.Builtin{
		Name:     "make-url",







|











>
>







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
	return root, nil
}

var (
	specials = []*sxeval.Special{
		&sxbuiltins.QuoteS, &sxbuiltins.QuasiquoteS, // quote, quasiquote
		&sxbuiltins.UnquoteS, &sxbuiltins.UnquoteSplicingS, // unquote, unquote-splicing
		&sxbuiltins.DefunS, &sxbuiltins.DefDynS, // defun, defdyn
		&sxbuiltins.LetS,      // let
		&sxbuiltins.IfS,       // if
		&sxbuiltins.DefMacroS, // defmacro
		&sxbuiltins.BeginS,    // begin
	}
	builtins = []*sxeval.Builtin{
		&sxbuiltins.Equal,                // =
		&sxbuiltins.NullP,                // null?
		&sxbuiltins.Car, &sxbuiltins.Cdr, // car, cdr
		&sxbuiltins.Caar, &sxbuiltins.Cadr, // caar, cadr
		&sxbuiltins.Cadar,  // cadar
		&sxbuiltins.List,   // list
		&sxbuiltins.Map,    // map
		&sxbuiltins.BoundP, // bound?
	}
)

func (wui *WebUI) bindExtra(root *sxeval.Binding) error {
	err := root.BindBuiltin(&sxeval.Builtin{
		Name:     "make-url",
159
160
161
162
163
164
165


166

167
168
169
170
171
172
173
	lb.Add(symUL)
	lb.Add(makeNavItem(st, root, nil))
	buildNavLevel(st, &lb, ancestors[1:], root.Children())
	return lb.List()
}

func buildNavLevel(st *site.Site, lb *sx.ListBuilder, ancestors, children []*site.Node) {


	root := ancestors[0]

	for _, child := range children {
		if !child.IsVisible() {
			continue
		}
		lb.Add(makeNavItem(st, child, root))
		if child != root {
			continue







>
>
|
>







161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
	lb.Add(symUL)
	lb.Add(makeNavItem(st, root, nil))
	buildNavLevel(st, &lb, ancestors[1:], root.Children())
	return lb.List()
}

func buildNavLevel(st *site.Site, lb *sx.ListBuilder, ancestors, children []*site.Node) {
	var root *site.Node
	if len(ancestors) > 0 {
		root = ancestors[0]
	}
	for _, child := range children {
		if !child.IsVisible() {
			continue
		}
		lb.Add(makeNavItem(st, child, root))
		if child != root {
			continue
202
203
204
205
206
207
208

209
210
211
212
213
214
215
216
217
}

var (
	symA     = sx.MakeSymbol("a")
	symClass = sx.MakeSymbol("class")
	symHref  = sx.MakeSymbol("href")
	symLI    = sx.MakeSymbol("li")

	symUL    = sx.MakeSymbol("ul")
)

func makeSimpleLink(href, text sx.String) *sx.Pair {
	return sx.MakeList(
		symA,
		sx.MakeList(sxhtml.SymAttr, sx.Cons(symHref, href)),
		text)
}







>









207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
}

var (
	symA     = sx.MakeSymbol("a")
	symClass = sx.MakeSymbol("class")
	symHref  = sx.MakeSymbol("href")
	symLI    = sx.MakeSymbol("li")
	symP     = sx.MakeSymbol("p")
	symUL    = sx.MakeSymbol("ul")
)

func makeSimpleLink(href, text sx.String) *sx.Pair {
	return sx.MakeList(
		symA,
		sx.MakeList(sxhtml.SymAttr, sx.Cons(symHref, href)),
		text)
}

Changes to social/web/wui/layout.sxc.

24
25
26
27
28
29
30
31
32
33
34
  (title ,TITLE)
)
(body
  (header
    (h1 ,TITLE)
  )
  (main
    (div ,CONTENT)
  )
)))
)







|



24
25
26
27
28
29
30
31
32
33
34
  (title ,TITLE)
)
(body
  (header
    (h1 ,TITLE)
  )
  (main
    (div ,@CONTENT)
  )
)))
)

Changes to social/web/wui/template.go.

20
21
22
23
24
25
26
27
28
29
30
31
32
33

34









35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
	"net/http"

	"zettelstore.de/sx.fossil"
	"zettelstore.de/sx.fossil/sxeval"
	"zettelstore.de/sx.fossil/sxhtml"
)

func (wui *WebUI) renderTemplateStatus(w http.ResponseWriter, code int, rb *renderBinding) error {
	if err := rb.err; err != nil {
		return err
	}
	binding := rb.bind
	wui.logger.Debug("Render", "binding", binding.Bindings())
	env := sxeval.MakeExecutionEnvironment(binding)

	obj, err := env.Eval(sx.MakeList(sx.MakeSymbol("render-template"), sx.MakeSymbol("layout")))









	if err != nil {
		return err
	}
	wui.logger.Debug("Render", "sx", obj)
	gen := sxhtml.NewGenerator().SetNewline()
	var sb bytes.Buffer
	_, err = gen.WriteHTML(&sb, obj)
	if err != nil {
		return err
	}
	h := w.Header()
	h.Set("Content-Type", "text/html; charset=utf-8")
	w.WriteHeader(code)
	if _, err = w.Write(sb.Bytes()); err != nil {
		wui.logger.Error("Unable to write HTML", "error", err)
	}
	return nil
}

func (wui *WebUI) MakeTestHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		rb := wui.makeRenderBinding("test", r)
		rb.bindString("CONTENT", fmt.Sprintf("Some content, url is: %q", r.URL))
		if err := wui.renderTemplateStatus(w, 200, rb); err != nil {
			wui.handleError(w, "Render", err)
			return
		}
	}
}

func (wui *WebUI) handleError(w http.ResponseWriter, subsystem string, err error) {







|






>
|
>
>
>
>
>
>
>
>
>



|


















|
|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
	"net/http"

	"zettelstore.de/sx.fossil"
	"zettelstore.de/sx.fossil/sxeval"
	"zettelstore.de/sx.fossil/sxhtml"
)

func (wui *WebUI) renderTemplateStatus(w http.ResponseWriter, code int, templateSym *sx.Symbol, rb *renderBinding) error {
	if err := rb.err; err != nil {
		return err
	}
	binding := rb.bind
	wui.logger.Debug("Render", "binding", binding.Bindings())
	env := sxeval.MakeExecutionEnvironment(binding)
	if _, bound := env.Resolve(templateSym); bound {
		obj, err := env.Eval(sx.MakeList(sx.MakeSymbol("render-template"), templateSym))
		if err != nil {
			return err
		}
		wui.logger.Debug("Render", "content", obj)
		rb.bindObject("CONTENT", obj)
	} else if templateSym != nil {
		rb.bindObject("CONTENT", sx.MakeList(symP, sx.String(templateSym.GoString())))
	}
	obj, err := env.Eval(sx.MakeList(sx.MakeSymbol("render-template"), sx.MakeSymbol(nameLayout)))
	if err != nil {
		return err
	}
	wui.logger.Debug("Render", "sxhtml", obj)
	gen := sxhtml.NewGenerator().SetNewline()
	var sb bytes.Buffer
	_, err = gen.WriteHTML(&sb, obj)
	if err != nil {
		return err
	}
	h := w.Header()
	h.Set("Content-Type", "text/html; charset=utf-8")
	w.WriteHeader(code)
	if _, err = w.Write(sb.Bytes()); err != nil {
		wui.logger.Error("Unable to write HTML", "error", err)
	}
	return nil
}

func (wui *WebUI) MakeTestHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		rb := wui.makeRenderBinding("test", r)
		rb.bindObject("CONTENT", sx.MakeList(sx.String(fmt.Sprintf("Some content, url is: %q", r.URL))))
		if err := wui.renderTemplateStatus(w, 200, nil, rb); err != nil {
			wui.handleError(w, "Render", err)
			return
		}
	}
}

func (wui *WebUI) handleError(w http.ResponseWriter, subsystem string, err error) {

Changes to social/web/wui/useragent.go.

15
16
17
18
19
20
21

22
23
24
25
26

27
28










29
30
31
32
33
34
35
36
37
38
39
40
41
42
43









import (
	"bytes"
	"fmt"
	"net/http"

	"zettelstore.de/contrib/social/usecase"

)

// MakeGetAllUAHandler creates a new HTTP handler to display the list of found
// user agents.
func (*WebUI) MakeGetAllUAHandler(ucAllUA usecase.GetAllUserAgents) http.HandlerFunc {

	return func(w http.ResponseWriter, r *http.Request) {
		uasT, uasF := ucAllUA.Run(r.Context())











		var buf bytes.Buffer
		for _, ua := range uasT {
			fmt.Fprintln(&buf, ua)
		}
		if len(uasF) > 0 && len(uasT) > 0 {
			fmt.Fprintln(&buf, "---")
		}
		for _, ua := range uasF {
			fmt.Fprintln(&buf, ua)
		}
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write(buf.Bytes())
	}
}















>




|
>


>
>
>
>
>
>
>
>
>
>















>
>
>
>
>
>
>
>
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

import (
	"bytes"
	"fmt"
	"net/http"

	"zettelstore.de/contrib/social/usecase"
	"zettelstore.de/sx.fossil"
)

// MakeGetAllUAHandler creates a new HTTP handler to display the list of found
// user agents.
func (wui *WebUI) MakeGetAllUAHandler(ucAllUA usecase.GetAllUserAgents) http.HandlerFunc {
	symUserAgents := sx.MakeSymbol("user-agents")
	return func(w http.ResponseWriter, r *http.Request) {
		uasT, uasF := ucAllUA.Run(r.Context())

		if len(r.URL.Query()) == 0 {
			rb := wui.makeRenderBinding("user-agent", r)
			rb.bindObject("ALLOWED-AGENTS", stringsTosxList(uasT))
			rb.bindObject("BLOCKED-AGENTS", stringsTosxList(uasF))
			if err := wui.renderTemplateStatus(w, 200, symUserAgents, rb); err != nil {
				wui.handleError(w, "Render", err)
			}
			return
		}

		var buf bytes.Buffer
		for _, ua := range uasT {
			fmt.Fprintln(&buf, ua)
		}
		if len(uasF) > 0 && len(uasT) > 0 {
			fmt.Fprintln(&buf, "---")
		}
		for _, ua := range uasF {
			fmt.Fprintln(&buf, ua)
		}
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write(buf.Bytes())
	}
}

func stringsTosxList(sl []string) *sx.Pair {
	var lb sx.ListBuilder
	for _, s := range sl {
		lb.Add(sx.String(s))
	}
	return lb.List()
}

Changes to social/web/wui/wui.go.

93
94
95
96
97
98
99





100
101
102
103
104
105
106
}

type renderBinding struct {
	err  error
	bind *sxeval.Binding
}






func (rb *renderBinding) bindString(key, val string) {
	if rb.err == nil {
		rb.err = rb.bind.Bind(sx.MakeSymbol(key), sx.String(val))
	}
}

func (wui *WebUI) evalCode(env *sxeval.Environment) error {







>
>
>
>
>







93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
}

type renderBinding struct {
	err  error
	bind *sxeval.Binding
}

func (rb *renderBinding) bindObject(key string, obj sx.Object) {
	if rb.err == nil {
		rb.err = rb.bind.Bind(sx.MakeSymbol(key), obj)
	}
}
func (rb *renderBinding) bindString(key, val string) {
	if rb.err == nil {
		rb.err = rb.bind.Bind(sx.MakeSymbol(key), sx.String(val))
	}
}

func (wui *WebUI) evalCode(env *sxeval.Environment) error {
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Template names
const (
	nameLayout = "layout"
)

// compileAllTemplates compiles (parses, reworks) all needed templates.
func (wui *WebUI) compileAllTemplates(env *sxeval.Environment, dir string) error {
	for _, name := range []string{nameLayout} {
		if err := wui.evalTemplate(env, dir, name); err != nil {
			return err
		}
	}
	return nil
}








|







148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Template names
const (
	nameLayout = "layout"
)

// compileAllTemplates compiles (parses, reworks) all needed templates.
func (wui *WebUI) compileAllTemplates(env *sxeval.Environment, dir string) error {
	for _, name := range []string{nameLayout, "useragent"} {
		if err := wui.evalTemplate(env, dir, name); err != nil {
			return err
		}
	}
	return nil
}