Zettelstore Contrib

Check-in [11335da40f]
Login

Check-in [11335da40f]

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

Overview
Comment:Social: move user-agent list to template-based site
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 11335da40f65646198533768896f88cfa43480838ffb20844a90938ddb313f92
User & Date: stern 2024-03-25 12:44:48
Context
2024-03-25
17:53
Social: initial version of simple SxHTML content handler ... (check-in: 78d7ea06fa user: stern tags: trunk)
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)
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-20240322172300-7f216ac38787




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

go 1.22

require zettelstore.de/sx.fossil v0.0.0-20240325101530-b11ffb3e9c3d

Changes to social/go.sum.

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=
|
|
1
2
zettelstore.de/sx.fossil v0.0.0-20240325101530-b11ffb3e9c3d h1:UruOvkuWI6CQqBv4D6+3Q8ARjZIPsljdDkvP9mvBUcA=
zettelstore.de/sx.fossil v0.0.0-20240325101530-b11ffb3e9c3d/go.mod h1:/iGHxFXoo6GSV04PUkwaLuFrrCa5LMorxD73iLMAruI=

Changes to social/social.go.

83
84
85
86
87
88
89
90
91


92
93
94
95
96
97
		return err
	}

	ucGetAllUserAgents := usecase.NewGetAllUserAgents(uaColl)

	docRoot := webui.MakeDocumentHandler(cfg.DocumentRoot)
	h.HandleFunc("GET /", docRoot)
	h.HandleFunc("GET /.ua/{$}", webui.MakeGetAllUAHandler(ucGetAllUserAgents))
	testHandler := webui.MakeTestHandler()


	h.HandleFunc("GET /.t/{$}", testHandler)
	h.HandleFunc("GET /.test/{$}", testHandler)
	h.HandleFunc("GET /.test/test/{$}", testHandler)
	h.HandleFunc("GET /.test/test/toast/{$}", testHandler)
	return nil
}







<

>
>






83
84
85
86
87
88
89

90
91
92
93
94
95
96
97
98
		return err
	}

	ucGetAllUserAgents := usecase.NewGetAllUserAgents(uaColl)

	docRoot := webui.MakeDocumentHandler(cfg.DocumentRoot)
	h.HandleFunc("GET /", docRoot)

	testHandler := webui.MakeTestHandler()
	h.HandleFunc("GET /social/{$}", testHandler)
	h.HandleFunc("GET /social/ua/{$}", webui.MakeGetAllUAHandler(ucGetAllUserAgents))
	h.HandleFunc("GET /.t/{$}", testHandler)
	h.HandleFunc("GET /.test/{$}", testHandler)
	h.HandleFunc("GET /.test/test/{$}", testHandler)
	h.HandleFunc("GET /.test/test/toast/{$}", testHandler)
	return nil
}

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

52
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67
68
69
		&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",
		MinArity: 0,







|
>
|
|
|







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
		&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.LengthGreater, // length>
		&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",
		MinArity: 0,

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

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
	"fmt"
	"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()







>
>








|





|
>
>
>
>
>
>
>
>
>

>
>
>
>
>
|
>
>
>
>







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
64
65
66
67
68
69
	"fmt"
	"net/http"

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

const contentName = "CONTENT"

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 _, templateBound := env.Resolve(templateSym); templateBound {
		obj, err := env.Eval(sx.MakeList(sx.MakeSymbol("render-template"), templateSym))
		if err != nil {
			return err
		}
		wui.logger.Debug("Render", "content", obj)
		rb.bindObject(contentName, obj)

	} else if obj, contentBound := env.Resolve(sx.MakeSymbol(contentName)); contentBound && !sx.IsNil(obj) {
		if _, isList := sx.GetPair(obj); !isList {
			obj = sx.MakeList(symP, obj)
		}
		obj = sx.Cons(obj, sx.Nil())
		wui.logger.Debug("Render", "obj", obj)
		rb.bindObject(contentName, obj)

	} else if templateSym != nil {
		rb.bindObject(
			contentName,
			sx.MakeList(
				symP,
				sx.String("Template "),
				sx.String(templateSym.GoString()),
				sx.String(" not found."),
			))
	} else {
		rb.bindObject(contentName, sx.MakeList(symP, sx.String("No template given.")))
	}
	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()
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
	}
	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
		}
	}
}








|







80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
	}
	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(symP, 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
		}
	}
}

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

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
// 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))







|
>



|




|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>







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
// 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())

		q := r.URL.Query()
		if len(q) == 0 {
			rb := wui.makeRenderBinding("user-agent", r)
			rb.bindObject("ALLOWED-AGENTS", stringsTosxList(uasT))
			rb.bindObject("BLOCKED-AGENTS", stringsTosxList(uasF))
			if err := wui.renderTemplateStatus(w, http.StatusOK, symUserAgents, rb); err != nil {
				wui.handleError(w, "Render", err)
			}
			return
		}
		if q.Has("plain") {
			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())
			return
		}

		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
	}
}

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

Added social/web/wui/useragent.sxc.























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
;;;----------------------------------------------------------------------------
;;; Copyright (c) 2024-present Detlef Stern
;;;
;;; This file is part of Zettel Social.
;;;
;;; Zettel Social is licensed under the latest version of the EUPL (European
;;; Union Public License). Please see file LICENSE.txt for your rights and
;;; obligations under this license.
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2024-present Detlef Stern
;;;----------------------------------------------------------------------------

;;; Node "User Agents"

(defun string-item (s) `(li ,s))

(define-template user-agents
`(
  (h1 "User Agents")
  (ul ,@(map string-item ALLOWED-AGENTS))
  ,@(if (length> BLOCKED-AGENTS 0)
        `((h2 "Blockiert")
          (ul ,@(map string-item BLOCKED-AGENTS))))
  (a (@ (href "?plain")) "plain")
 )
)

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

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128










129
130
131
132
133
134
135
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 {
	for _, content := range [][]byte{contentPreludeSxc, contentTemplateSxc, contentLayoutSxc} {
		rdr := sxreader.MakeReader(bytes.NewReader(content))
		if err := wui.evalReader(env, rdr); err != nil {
			return err
		}
	}
	return nil
}

//go:embed prelude.sxc
var contentPreludeSxc []byte

//go:embed template.sxc
var contentTemplateSxc []byte

//go:embed layout.sxc
var contentLayoutSxc []byte











func (wui *WebUI) evalReader(env *sxeval.Environment, rdr *sxreader.Reader) error {
	for {
		obj, err := rdr.Read()
		if err != nil {
			if err == io.EOF {
				break







|
















>
>
>
>
>
>
>
>
>
>







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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 {
	for _, content := range staticSxc {
		rdr := sxreader.MakeReader(bytes.NewReader(content))
		if err := wui.evalReader(env, rdr); err != nil {
			return err
		}
	}
	return nil
}

//go:embed prelude.sxc
var contentPreludeSxc []byte

//go:embed template.sxc
var contentTemplateSxc []byte

//go:embed layout.sxc
var contentLayoutSxc []byte

//go:embed useragent.sxc
var contentUseragentSxc []byte

var staticSxc = [][]byte{
	contentPreludeSxc,
	contentTemplateSxc,
	contentLayoutSxc,
	contentUseragentSxc,
}

func (wui *WebUI) evalReader(env *sxeval.Environment, rdr *sxreader.Reader) error {
	for {
		obj, err := rdr.Read()
		if err != nil {
			if err == io.EOF {
				break
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
}








|







158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// 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
}