Zettelstore

Check-in Differences
Login

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

Difference From v0.1 To trunk

2021-11-27
17:03
Cleanup code to satisfy some linter ... (Leaf check-in: cbf9e73a85 user: stern tags: trunk)
17:00
If syntax is none, only evaluated explicitly stated metadata, not inherited metadata ... (check-in: 21a8dd536a user: stern tags: trunk)
2021-11-13
17:09
Version 0.1.1 ... (check-in: 8d9dc27ae1 user: stern tags: trunk, release, v0.1.1)
2021-11-11
16:37
Version 0.1 ... (check-in: 7c6fd9bcda user: stern tags: trunk, release, v0.1)
14:56
Do not abort release build when unparam finds optional problems ... (check-in: 29e086118f user: stern tags: trunk)

Changes to VERSION.

1
0.1
|
1
0.2.0-dev

Changes to ast/ast.go.

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
28
29

30
31
32
33
34
35
36
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package ast provides the abstract syntax tree.
package ast

import (
	"net/url"

	"zettelstore.de/z/domain"
	"zettelstore.de/z/domain/id"
	"zettelstore.de/z/domain/meta"
)

// ZettelNode is the root node of the abstract syntax tree.
// It is *not* part of the visitor pattern.
type ZettelNode struct {
	Meta    *meta.Meta     // Original metadata
	Content domain.Content // Original content
	Zid     id.Zid         // Zettel identification.
	InhMeta *meta.Meta     // Metadata of the zettel, with inherited values.
	Ast     *BlockListNode // Zettel abstract syntax tree is a sequence of block nodes.

}

// Node is the interface, all nodes must implement.
type Node interface {
	WalkChildren(v Visitor)
}











|


















>







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
28
29
30
31
32
33
34
35
36
37
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package ast provides the abstract syntax tree for parsed zettel content.
package ast

import (
	"net/url"

	"zettelstore.de/z/domain"
	"zettelstore.de/z/domain/id"
	"zettelstore.de/z/domain/meta"
)

// ZettelNode is the root node of the abstract syntax tree.
// It is *not* part of the visitor pattern.
type ZettelNode struct {
	Meta    *meta.Meta     // Original metadata
	Content domain.Content // Original content
	Zid     id.Zid         // Zettel identification.
	InhMeta *meta.Meta     // Metadata of the zettel, with inherited values.
	Ast     *BlockListNode // Zettel abstract syntax tree is a sequence of block nodes.
	Syntax  string         // Syntax / parser that produced the Ast
}

// Node is the interface, all nodes must implement.
type Node interface {
	WalkChildren(v Visitor)
}

Changes to ast/attr.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package ast provides the abstract syntax tree.
package ast

import (
	"strings"
)

// Attributes store additional information about some node types.
type Attributes struct {
	Attrs map[string]string
}

// IsEmpty returns true if there are no attributes.










<


|
<
<







1
2
3
4
5
6
7
8
9
10

11
12
13


14
15
16
17
18
19
20
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package ast

import "strings"



// Attributes store additional information about some node types.
type Attributes struct {
	Attrs map[string]string
}

// IsEmpty returns true if there are no attributes.

Changes to ast/attr_test.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package ast provides the abstract syntax tree.
package ast_test

import (
	"testing"

	"zettelstore.de/z/ast"
)










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package ast_test

import (
	"testing"

	"zettelstore.de/z/ast"
)

Changes to ast/block.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package ast provides the abstract syntax tree.
package ast

// Definition of Block nodes.

// BlockListNode is a list of BlockNodes.
type BlockListNode struct {
	List []BlockNode










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package ast

// Definition of Block nodes.

// BlockListNode is a list of BlockNodes.
type BlockListNode struct {
	List []BlockNode

Changes to ast/inline.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package ast provides the abstract syntax tree.
package ast

// Definitions of inline nodes.

// InlineListNode is a list of BlockNodes.
type InlineListNode struct {
	List []InlineNode










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package ast

// Definitions of inline nodes.

// InlineListNode is a list of BlockNodes.
type InlineListNode struct {
	List []InlineNode
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
			inl = append(inl, &SpaceNode{Lexeme: " "})
		}
		inl = append(inl, &TextNode{Text: word})
	}
	return &InlineListNode{List: inl}
}

// WalkChildren walks down to the descriptions.
func (iln *InlineListNode) WalkChildren(v Visitor) {
	for _, bn := range iln.List {
		Walk(v, bn)
	}
}

// IsEmpty returns true if the list has no elements.







|







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
			inl = append(inl, &SpaceNode{Lexeme: " "})
		}
		inl = append(inl, &TextNode{Text: word})
	}
	return &InlineListNode{List: inl}
}

// WalkChildren walks down to the list.
func (iln *InlineListNode) WalkChildren(v Visitor) {
	for _, bn := range iln.List {
		Walk(v, bn)
	}
}

// IsEmpty returns true if the list has no elements.
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
}

// FormatKind specifies the format that is applied to the inline nodes.
type FormatKind uint8

// Constants for FormatCode
const (
	_                    FormatKind = iota
	FormatEmph                      // Emphasized text.
	FormatStrong                    // Strongly emphasized text.
	FormatInsert                    // Inserted text.
	FormatDelete                    // Deleted text.
	FormatSuper                     // Superscripted text.
	FormatSub                       // SubscriptedText.
	FormatQuote                     // Quoted text.
	FormatQuotation                 // Quotation text.
	FormatSmall                     // Smaller text.
	FormatSpan                      // Generic inline container.
	FormatMonospace                 // Monospaced text.
	FormatEmphDeprecated            // Deprecated kind of emphasized text.
)

func (*FormatNode) inlineNode() { /* Just a marker */ }

// WalkChildren walks to the formatted text.
func (fn *FormatNode) WalkChildren(v Visitor) {
	Walk(v, fn.Inlines)







|
|
|
|
|
|
|
|
|
<
|
|
<







196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211

212
213

214
215
216
217
218
219
220
}

// FormatKind specifies the format that is applied to the inline nodes.
type FormatKind uint8

// Constants for FormatCode
const (
	_               FormatKind = iota
	FormatEmph                 // Emphasized text.
	FormatStrong               // Strongly emphasized text.
	FormatInsert               // Inserted text.
	FormatDelete               // Deleted text.
	FormatSuper                // Superscripted text.
	FormatSub                  // SubscriptedText.
	FormatQuote                // Quoted text.
	FormatQuotation            // Quotation text.

	FormatSpan                 // Generic inline container.
	FormatMonospace            // Monospaced text.

)

func (*FormatNode) inlineNode() { /* Just a marker */ }

// WalkChildren walks to the formatted text.
func (fn *FormatNode) WalkChildren(v Visitor) {
	Walk(v, fn.Inlines)

Changes to ast/material.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package ast provides the abstract syntax tree.
package ast

// MaterialNode references the various types of zettel material.
type MaterialNode interface {
	Node
	materialNode()
}










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package ast

// MaterialNode references the various types of zettel material.
type MaterialNode interface {
	Node
	materialNode()
}

Changes to ast/ref.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package ast provides the abstract syntax tree.
package ast

import (
	"net/url"

	"zettelstore.de/z/domain/id"
)










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package ast

import (
	"net/url"

	"zettelstore.de/z/domain/id"
)

Changes to ast/ref_test.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package ast_test provides the tests for the abstract syntax tree.
package ast_test

import (
	"testing"

	"zettelstore.de/z/ast"
)










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package ast_test

import (
	"testing"

	"zettelstore.de/z/ast"
)

Changes to ast/walk.go.

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
28
29
30
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package ast provides the abstract syntax tree.
package ast

// Visitor is a visitor for walking the AST.
type Visitor interface {
	Visit(node Node) Visitor
}

// Walk traverses the AST.
func Walk(v Visitor, node Node) {
	if v = v.Visit(node); v == nil {
		return
	}






	node.WalkChildren(v)
	v.Visit(nil)
}

// WalkItemSlice traverses an item slice.
func WalkItemSlice(v Visitor, ins ItemSlice) {
	for _, in := range ins {










<












>
>
>
>
>
>







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
28
29
30
31
32
33
34
35
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package ast

// Visitor is a visitor for walking the AST.
type Visitor interface {
	Visit(node Node) Visitor
}

// Walk traverses the AST.
func Walk(v Visitor, node Node) {
	if v = v.Visit(node); v == nil {
		return
	}

	// Implementation note:
	// It is much faster to use interface dispatching than to use a switch statement.
	// On my "cpu: Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz", a switch statement
	// implementation tooks approx 940-980 ns/op. Interface dispatching is in the
	// range of 900-930 ns/op.
	node.WalkChildren(v)
	v.Visit(nil)
}

// WalkItemSlice traverses an item slice.
func WalkItemSlice(v Visitor, ins ItemSlice) {
	for _, in := range ins {

Added ast/walk_test.go.































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
76
77
78
79
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

package ast_test

import (
	"testing"

	"zettelstore.de/z/ast"
)

func BenchmarkWalk(b *testing.B) {
	root := &ast.BlockListNode{
		List: []ast.BlockNode{
			&ast.HeadingNode{
				Inlines: ast.CreateInlineListNodeFromWords("A", "Simple", "Heading"),
			},
			&ast.ParaNode{
				Inlines: ast.CreateInlineListNodeFromWords("This", "is", "the", "introduction."),
			},
			&ast.NestedListNode{
				Kind: ast.NestedListUnordered,
				Items: []ast.ItemSlice{
					[]ast.ItemNode{
						&ast.ParaNode{
							Inlines: ast.CreateInlineListNodeFromWords("Item", "1"),
						},
					},
					[]ast.ItemNode{
						&ast.ParaNode{
							Inlines: ast.CreateInlineListNodeFromWords("Item", "2"),
						},
					},
				},
			},
			&ast.ParaNode{
				Inlines: ast.CreateInlineListNodeFromWords("This", "is", "some", "intermediate", "text."),
			},
			&ast.ParaNode{
				Inlines: ast.CreateInlineListNode(
					&ast.FormatNode{
						Kind: ast.FormatEmph,
						Attrs: &ast.Attributes{
							Attrs: map[string]string{
								"":      "class",
								"color": "green",
							},
						},
						Inlines: ast.CreateInlineListNodeFromWords("This", "is", "some", "emphasized", "text."),
					},
					&ast.SpaceNode{Lexeme: " "},
					&ast.LinkNode{
						Ref: &ast.Reference{
							Value: "http://zettelstore.de",
						},
						Inlines: ast.CreateInlineListNodeFromWords("URL", "text."),
						OnlyRef: false,
					},
				),
			},
		},
	}
	v := benchVisitor{}
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		ast.Walk(&v, root)
	}
}

type benchVisitor struct{}

func (bv *benchVisitor) Visit(ast.Node) ast.Visitor { return bv }

Changes to auth/impl/impl.go.

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
}

// IsReadonly returns true, if the systems is configured to run in read-only-mode.
func (a *myAuth) IsReadonly() bool { return a.readonly }

const reqHash = jwt.HS512

// ErrNoUser signals that the meta data has no role value 'user'.
var ErrNoUser = errors.New("auth: meta is no user")

// ErrNoIdent signals that the 'ident' key is missing.
var ErrNoIdent = errors.New("auth: missing ident")

// ErrOtherKind signals that the token was defined for another token kind.
var ErrOtherKind = errors.New("auth: wrong token kind")

// ErrNoZid signals that the 'zid' key is missing.
var ErrNoZid = errors.New("auth: missing zettel id")

// GetToken returns a token to be used for authentification.
func (a *myAuth) GetToken(ident *meta.Meta, d time.Duration, kind auth.TokenKind) ([]byte, error) {
	if role, ok := ident.Get(api.KeyRole); !ok || role != api.ValueRoleUser {
		return nil, ErrNoUser
	}
	subject, ok := ident.Get(api.KeyUserID)
	if !ok || subject == "" {
		return nil, ErrNoIdent
	}

	now := time.Now().Round(time.Second)
	claims := jwt.Claims{







<
<
<











<
<
<







66
67
68
69
70
71
72



73
74
75
76
77
78
79
80
81
82
83



84
85
86
87
88
89
90
}

// IsReadonly returns true, if the systems is configured to run in read-only-mode.
func (a *myAuth) IsReadonly() bool { return a.readonly }

const reqHash = jwt.HS512




// ErrNoIdent signals that the 'ident' key is missing.
var ErrNoIdent = errors.New("auth: missing ident")

// ErrOtherKind signals that the token was defined for another token kind.
var ErrOtherKind = errors.New("auth: wrong token kind")

// ErrNoZid signals that the 'zid' key is missing.
var ErrNoZid = errors.New("auth: missing zettel id")

// GetToken returns a token to be used for authentification.
func (a *myAuth) GetToken(ident *meta.Meta, d time.Duration, kind auth.TokenKind) ([]byte, error) {



	subject, ok := ident.Get(api.KeyUserID)
	if !ok || subject == "" {
		return nil, ErrNoIdent
	}

	now := time.Now().Round(time.Second)
	claims := jwt.Claims{

Changes to auth/policy/anon.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package policy provides some interfaces and implementation for authorization policies.
package policy

import (
	"zettelstore.de/z/auth"
	"zettelstore.de/z/config"
	"zettelstore.de/z/domain/meta"
)










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package policy

import (
	"zettelstore.de/z/auth"
	"zettelstore.de/z/config"
	"zettelstore.de/z/domain/meta"
)

Changes to auth/policy/box.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package policy provides some interfaces and implementation for authorizsation policies.
package policy

import (
	"context"

	"zettelstore.de/z/auth"
	"zettelstore.de/z/box"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package policy

import (
	"context"

	"zettelstore.de/z/auth"
	"zettelstore.de/z/box"

Changes to auth/policy/default.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package policy provides some interfaces and implementation for authorizsation policies.
package policy

import (
	"zettelstore.de/c/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/domain/meta"
)










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package policy

import (
	"zettelstore.de/c/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/domain/meta"
)

Changes to auth/policy/owner.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package policy provides some interfaces and implementation for authorizsation policies.
package policy

import (
	"zettelstore.de/c/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/config"
	"zettelstore.de/z/domain/meta"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package policy

import (
	"zettelstore.de/c/api"
	"zettelstore.de/z/auth"
	"zettelstore.de/z/config"
	"zettelstore.de/z/domain/meta"
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
	return o.userIsOwner(user) || o.userCanCreate(user, newMeta)
}

func (o *ownerPolicy) userCanCreate(user, newMeta *meta.Meta) bool {
	if o.manager.GetUserRole(user) == meta.UserRoleReader {
		return false
	}
	if role, ok := newMeta.Get(api.KeyRole); ok && role == api.ValueRoleUser {
		return false
	}
	return true
}

func (o *ownerPolicy) CanRead(user, m *meta.Meta) bool {
	// No need to call o.pre.CanRead(user, meta), because it will always return true.







|







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
	return o.userIsOwner(user) || o.userCanCreate(user, newMeta)
}

func (o *ownerPolicy) userCanCreate(user, newMeta *meta.Meta) bool {
	if o.manager.GetUserRole(user) == meta.UserRoleReader {
		return false
	}
	if _, ok := newMeta.Get(api.KeyUserID); ok {
		return false
	}
	return true
}

func (o *ownerPolicy) CanRead(user, m *meta.Meta) bool {
	// No need to call o.pre.CanRead(user, meta), because it will always return true.
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
		return false
	case meta.VisibilityPublic:
		return true
	}
	if user == nil {
		return false
	}
	if role, ok := m.Get(api.KeyRole); ok && role == api.ValueRoleUser {
		// Only the user can read its own zettel
		return user.Zid == m.Zid
	}
	switch o.manager.GetUserRole(user) {
	case meta.UserRoleReader, meta.UserRoleWriter, meta.UserRoleOwner:
		return true
	case meta.UserRoleCreator:







|







56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
		return false
	case meta.VisibilityPublic:
		return true
	}
	if user == nil {
		return false
	}
	if _, ok := m.Get(api.KeyUserID); ok {
		// Only the user can read its own zettel
		return user.Zid == m.Zid
	}
	switch o.manager.GetUserRole(user) {
	case meta.UserRoleReader, meta.UserRoleWriter, meta.UserRoleOwner:
		return true
	case meta.UserRoleCreator:
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
	}
	if o.userIsOwner(user) {
		return true
	}
	if !o.userCanRead(user, oldMeta, vis) {
		return false
	}
	if role, ok := oldMeta.Get(api.KeyRole); ok && role == api.ValueRoleUser {
		// Here we know, that user.Zid == newMeta.Zid (because of userCanRead) and
		// user.Zid == newMeta.Zid (because oldMeta.Zid == newMeta.Zid)
		for _, key := range noChangeUser {
			if oldMeta.GetDefault(key, "") != newMeta.GetDefault(key, "") {
				return false
			}
		}







|







91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
	}
	if o.userIsOwner(user) {
		return true
	}
	if !o.userCanRead(user, oldMeta, vis) {
		return false
	}
	if _, ok := oldMeta.Get(api.KeyUserID); ok {
		// Here we know, that user.Zid == newMeta.Zid (because of userCanRead) and
		// user.Zid == newMeta.Zid (because oldMeta.Zid == newMeta.Zid)
		for _, key := range noChangeUser {
			if oldMeta.GetDefault(key, "") != newMeta.GetDefault(key, "") {
				return false
			}
		}

Changes to auth/policy/policy_test.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package policy provides some interfaces and implementation for authorizsation policies.
package policy

import (
	"fmt"
	"testing"

	"zettelstore.de/c/api"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package policy

import (
	"fmt"
	"testing"

	"zettelstore.de/c/api"
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
	userZid    = id.Zid(1025)
)

func newAnon() *meta.Meta { return nil }
func newCreator() *meta.Meta {
	user := meta.New(creatorZid)
	user.Set(api.KeyTitle, "Creator")
	user.Set(api.KeyRole, api.ValueRoleUser)
	user.Set(api.KeyUserRole, api.ValueUserRoleCreator)
	return user
}
func newReader() *meta.Meta {
	user := meta.New(readerZid)
	user.Set(api.KeyTitle, "Reader")
	user.Set(api.KeyRole, api.ValueRoleUser)
	user.Set(api.KeyUserRole, api.ValueUserRoleReader)
	return user
}
func newWriter() *meta.Meta {
	user := meta.New(writerZid)
	user.Set(api.KeyTitle, "Writer")
	user.Set(api.KeyRole, api.ValueRoleUser)
	user.Set(api.KeyUserRole, api.ValueUserRoleWriter)
	return user
}
func newOwner() *meta.Meta {
	user := meta.New(ownerZid)
	user.Set(api.KeyTitle, "Owner")
	user.Set(api.KeyRole, api.ValueRoleUser)
	user.Set(api.KeyUserRole, api.ValueUserRoleOwner)
	return user
}
func newOwner2() *meta.Meta {
	user := meta.New(owner2Zid)
	user.Set(api.KeyTitle, "Owner 2")
	user.Set(api.KeyRole, api.ValueRoleUser)
	user.Set(api.KeyUserRole, api.ValueUserRoleOwner)
	return user
}
func newZettel() *meta.Meta {
	m := meta.New(zettelZid)
	m.Set(api.KeyTitle, "Any Zettel")
	return m







|






|






|






|






|







575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
	userZid    = id.Zid(1025)
)

func newAnon() *meta.Meta { return nil }
func newCreator() *meta.Meta {
	user := meta.New(creatorZid)
	user.Set(api.KeyTitle, "Creator")
	user.Set(api.KeyUserID, "ceator")
	user.Set(api.KeyUserRole, api.ValueUserRoleCreator)
	return user
}
func newReader() *meta.Meta {
	user := meta.New(readerZid)
	user.Set(api.KeyTitle, "Reader")
	user.Set(api.KeyUserID, "reader")
	user.Set(api.KeyUserRole, api.ValueUserRoleReader)
	return user
}
func newWriter() *meta.Meta {
	user := meta.New(writerZid)
	user.Set(api.KeyTitle, "Writer")
	user.Set(api.KeyUserID, "writer")
	user.Set(api.KeyUserRole, api.ValueUserRoleWriter)
	return user
}
func newOwner() *meta.Meta {
	user := meta.New(ownerZid)
	user.Set(api.KeyTitle, "Owner")
	user.Set(api.KeyUserID, "owner")
	user.Set(api.KeyUserRole, api.ValueUserRoleOwner)
	return user
}
func newOwner2() *meta.Meta {
	user := meta.New(owner2Zid)
	user.Set(api.KeyTitle, "Owner 2")
	user.Set(api.KeyUserID, "owner-2")
	user.Set(api.KeyUserRole, api.ValueUserRoleOwner)
	return user
}
func newZettel() *meta.Meta {
	m := meta.New(zettelZid)
	m.Set(api.KeyTitle, "Any Zettel")
	return m
676
677
678
679
680
681
682
683
684
685
	m.Set(api.KeyTitle, "Owner r/o Zettel")
	m.Set(api.KeyReadOnly, api.ValueUserRoleOwner)
	return m
}
func newUserZettel() *meta.Meta {
	m := meta.New(userZid)
	m.Set(api.KeyTitle, "Any User")
	m.Set(api.KeyRole, api.ValueRoleUser)
	return m
}







|


675
676
677
678
679
680
681
682
683
684
	m.Set(api.KeyTitle, "Owner r/o Zettel")
	m.Set(api.KeyReadOnly, api.ValueUserRoleOwner)
	return m
}
func newUserZettel() *meta.Meta {
	m := meta.New(userZid)
	m.Set(api.KeyTitle, "Any User")
	m.Set(api.KeyUserID, "any")
	return m
}

Changes to auth/policy/readonly.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package policy provides some interfaces and implementation for authorization policies.
package policy

import "zettelstore.de/z/domain/meta"

type roPolicy struct{}

func (p *roPolicy) CanCreate(user, newMeta *meta.Meta) bool         { return false }
func (p *roPolicy) CanRead(user, m *meta.Meta) bool                 { return true }
func (p *roPolicy) CanWrite(user, oldMeta, newMeta *meta.Meta) bool { return false }
func (p *roPolicy) CanRename(user, m *meta.Meta) bool               { return false }
func (p *roPolicy) CanDelete(user, m *meta.Meta) bool               { return false }










<






|
|
|
|
|
1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18
19
20
21
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package policy

import "zettelstore.de/z/domain/meta"

type roPolicy struct{}

func (*roPolicy) CanCreate(_, _ *meta.Meta) bool   { return false }
func (*roPolicy) CanRead(_, _ *meta.Meta) bool     { return true }
func (*roPolicy) CanWrite(_, _, _ *meta.Meta) bool { return false }
func (*roPolicy) CanRename(_, _ *meta.Meta) bool   { return false }
func (*roPolicy) CanDelete(_, _ *meta.Meta) bool   { return false }

Changes to box/compbox/compbox.go.

42
43
44
45
46
47
48

49
50
51
52
53
54
55
	content func(*meta.Meta) []byte
}{
	id.MustParse(api.ZidVersion):              {genVersionBuildM, genVersionBuildC},
	id.MustParse(api.ZidHost):                 {genVersionHostM, genVersionHostC},
	id.MustParse(api.ZidOperatingSystem):      {genVersionOSM, genVersionOSC},
	id.MustParse(api.ZidBoxManager):           {genManagerM, genManagerC},
	id.MustParse(api.ZidMetadataKey):          {genKeysM, genKeysC},

	id.MustParse(api.ZidStartupConfiguration): {genConfigZettelM, genConfigZettelC},
}

// Get returns the one program box.
func getCompBox(boxNumber int, mf box.Enricher) box.ManagedBox {
	return &compBox{number: boxNumber, enricher: mf}
}







>







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
	content func(*meta.Meta) []byte
}{
	id.MustParse(api.ZidVersion):              {genVersionBuildM, genVersionBuildC},
	id.MustParse(api.ZidHost):                 {genVersionHostM, genVersionHostC},
	id.MustParse(api.ZidOperatingSystem):      {genVersionOSM, genVersionOSC},
	id.MustParse(api.ZidBoxManager):           {genManagerM, genManagerC},
	id.MustParse(api.ZidMetadataKey):          {genKeysM, genKeysC},
	id.MustParse(api.ZidParser):               {genParserM, genParserC},
	id.MustParse(api.ZidStartupConfiguration): {genConfigZettelM, genConfigZettelC},
}

// Get returns the one program box.
func getCompBox(boxNumber int, mf box.Enricher) box.ManagedBox {
	return &compBox{number: boxNumber, enricher: mf}
}

Changes to box/compbox/config.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package compbox provides zettel that have computed content.
package compbox

import (
	"bytes"

	"zettelstore.de/c/api"
	"zettelstore.de/z/domain/id"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package compbox

import (
	"bytes"

	"zettelstore.de/c/api"
	"zettelstore.de/z/domain/id"

Changes to box/compbox/keys.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package compbox provides zettel that have computed content.
package compbox

import (
	"bytes"
	"fmt"

	"zettelstore.de/c/api"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package compbox

import (
	"bytes"
	"fmt"

	"zettelstore.de/c/api"

Changes to box/compbox/manager.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package compbox provides zettel that have computed content.
package compbox

import (
	"bytes"
	"fmt"

	"zettelstore.de/c/api"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package compbox

import (
	"bytes"
	"fmt"

	"zettelstore.de/c/api"

Added box/compbox/parser.go.



































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

package compbox

import (
	"bytes"
	"fmt"
	"sort"
	"strings"

	"zettelstore.de/c/api"
	"zettelstore.de/z/domain/id"
	"zettelstore.de/z/domain/meta"
	"zettelstore.de/z/parser"
)

func genParserM(zid id.Zid) *meta.Meta {
	m := meta.New(zid)
	m.Set(api.KeyTitle, "Zettelstore Supported Parser")
	m.Set(api.KeyVisibility, api.ValueVisibilityLogin)
	return m
}

func genParserC(*meta.Meta) []byte {
	var buf bytes.Buffer
	buf.WriteString("|=Syntax<|=Alt. Value(s):|=Text Parser?:|=Image Format?:\n")
	syntaxes := parser.GetSyntaxes()
	sort.Strings(syntaxes)
	for _, syntax := range syntaxes {
		info := parser.Get(syntax)
		if info.Name != syntax {
			continue
		}
		altNames := info.AltNames
		sort.Strings(altNames)
		fmt.Fprintf(
			&buf, "|%v|%v|%v|%v\n",
			syntax, strings.Join(altNames, ", "), info.IsTextParser, info.IsImageFormat)
	}
	return buf.Bytes()
}

Changes to box/compbox/version.go.

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
28
29
30
31
32
33
34
35
36
37
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package compbox provides zettel that have computed content.
package compbox

import (
	"zettelstore.de/c/api"
	"zettelstore.de/z/domain/id"
	"zettelstore.de/z/domain/meta"
	"zettelstore.de/z/kernel"
)

func getVersionMeta(zid id.Zid, title string) *meta.Meta {
	m := meta.New(zid)
	m.Set(api.KeyTitle, title)
	m.Set(api.KeyVisibility, api.ValueVisibilityExpert)
	return m
}

func genVersionBuildM(zid id.Zid) *meta.Meta {
	m := getVersionMeta(zid, "Zettelstore Version")
	m.Set(api.KeyVisibility, api.ValueVisibilityPublic)
	return m
}
func genVersionBuildC(*meta.Meta) []byte {
	return []byte(kernel.Main.GetConfig(kernel.CoreService, kernel.CoreVersion).(string))
}

func genVersionHostM(zid id.Zid) *meta.Meta {










<


















|







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
28
29
30
31
32
33
34
35
36
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package compbox

import (
	"zettelstore.de/c/api"
	"zettelstore.de/z/domain/id"
	"zettelstore.de/z/domain/meta"
	"zettelstore.de/z/kernel"
)

func getVersionMeta(zid id.Zid, title string) *meta.Meta {
	m := meta.New(zid)
	m.Set(api.KeyTitle, title)
	m.Set(api.KeyVisibility, api.ValueVisibilityExpert)
	return m
}

func genVersionBuildM(zid id.Zid) *meta.Meta {
	m := getVersionMeta(zid, "Zettelstore Version")
	m.Set(api.KeyVisibility, api.ValueVisibilityLogin)
	return m
}
func genVersionBuildC(*meta.Meta) []byte {
	return []byte(kernel.Main.GetConfig(kernel.CoreService, kernel.CoreVersion).(string))
}

func genVersionHostM(zid id.Zid) *meta.Meta {

Changes to box/constbox/constbox.go.

141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
	id.MustParse(api.ZidAuthors): {
		constHeader{
			api.KeyTitle:      "Zettelstore Contributors",
			api.KeyRole:       api.ValueRoleConfiguration,
			api.KeySyntax:     api.ValueSyntaxZmk,
			api.KeyLang:       api.ValueLangEN,
			api.KeyReadOnly:   api.ValueTrue,
			api.KeyVisibility: api.ValueVisibilityPublic,
		},
		domain.NewContent(contentContributors)},
	id.MustParse(api.ZidDependencies): {
		constHeader{
			api.KeyTitle:      "Zettelstore Dependencies",
			api.KeyRole:       api.ValueRoleConfiguration,
			api.KeySyntax:     api.ValueSyntaxZmk,
			api.KeyLang:       api.ValueLangEN,
			api.KeyReadOnly:   api.ValueTrue,
			api.KeyVisibility: api.ValueVisibilityPublic,
		},
		domain.NewContent(contentDependencies)},
	id.BaseTemplateZid: {
		constHeader{
			api.KeyTitle:      "Zettelstore Base HTML Template",
			api.KeyRole:       api.ValueRoleConfiguration,
			api.KeySyntax:     syntaxTemplate,







|









|







141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
	id.MustParse(api.ZidAuthors): {
		constHeader{
			api.KeyTitle:      "Zettelstore Contributors",
			api.KeyRole:       api.ValueRoleConfiguration,
			api.KeySyntax:     api.ValueSyntaxZmk,
			api.KeyLang:       api.ValueLangEN,
			api.KeyReadOnly:   api.ValueTrue,
			api.KeyVisibility: api.ValueVisibilityLogin,
		},
		domain.NewContent(contentContributors)},
	id.MustParse(api.ZidDependencies): {
		constHeader{
			api.KeyTitle:      "Zettelstore Dependencies",
			api.KeyRole:       api.ValueRoleConfiguration,
			api.KeySyntax:     api.ValueSyntaxZmk,
			api.KeyLang:       api.ValueLangEN,
			api.KeyReadOnly:   api.ValueTrue,
			api.KeyVisibility: api.ValueVisibilityLogin,
		},
		domain.NewContent(contentDependencies)},
	id.BaseTemplateZid: {
		constHeader{
			api.KeyTitle:      "Zettelstore Base HTML Template",
			api.KeyRole:       api.ValueRoleConfiguration,
			api.KeySyntax:     syntaxTemplate,
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
			api.KeySyntax:     api.ValueSyntaxZmk,
			api.KeyVisibility: api.ValueVisibilityCreator,
		},
		domain.NewContent(nil)},
	id.MustParse(api.ZidTemplateNewUser): {
		constHeader{
			api.KeyTitle:                       "New User",
			api.KeyRole:                        api.ValueRoleUser,
			api.KeySyntax:                      api.ValueSyntaxNone,
			meta.NewPrefix + api.KeyCredential: "",
			meta.NewPrefix + api.KeyUserID:     "",
			meta.NewPrefix + api.KeyUserRole:   api.ValueUserRoleReader,
			api.KeyVisibility:                  api.ValueVisibilityOwner,
		},
		domain.NewContent(nil)},







|







308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
			api.KeySyntax:     api.ValueSyntaxZmk,
			api.KeyVisibility: api.ValueVisibilityCreator,
		},
		domain.NewContent(nil)},
	id.MustParse(api.ZidTemplateNewUser): {
		constHeader{
			api.KeyTitle:                       "New User",
			api.KeyRole:                        api.ValueRoleConfiguration,
			api.KeySyntax:                      api.ValueSyntaxNone,
			meta.NewPrefix + api.KeyCredential: "",
			meta.NewPrefix + api.KeyUserID:     "",
			meta.NewPrefix + api.KeyUserRole:   api.ValueUserRoleReader,
			api.KeyVisibility:                  api.ValueVisibilityOwner,
		},
		domain.NewContent(nil)},

Changes to box/constbox/dependencies.zettel.

1
2
3
4
5
6
7
8
9
10
11
Zettelstore is made with the help of other software and other artifacts.
Thank you very much!

This zettel lists all of them, together with their license.

=== Go runtime and associated libraries
; License
: BSD 3-Clause "New" or "Revised" License
```
Copyright (c) 2009 The Go Authors. All rights reserved.




|







1
2
3
4
5
6
7
8
9
10
11
Zettelstore is made with the help of other software and other artifacts.
Thank you very much!

This zettel lists all of them, together with their licenses.

=== Go runtime and associated libraries
; License
: BSD 3-Clause "New" or "Revised" License
```
Copyright (c) 2009 The Go Authors. All rights reserved.

Changes to box/constbox/info.mustache.

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
28
29
30
31
32


33







34
35
36
37
38
39
40
<article>
<header>
<h1>Information for Zettel {{Zid}}</h1>
<a href="{{{WebURL}}}">Web</a>
&#183; <a href="{{{ContextURL}}}">Context</a>
{{#CanWrite}} &#183; <a href="{{{EditURL}}}">Edit</a>{{/CanWrite}}
{{#CanFolge}} &#183; <a href="{{{FolgeURL}}}">Folge</a>{{/CanFolge}}
{{#CanCopy}} &#183; <a href="{{{CopyURL}}}">Copy</a>{{/CanCopy}}
{{#CanRename}}&#183; <a href="{{{RenameURL}}}">Rename</a>{{/CanRename}}
{{#CanDelete}}&#183; <a href="{{{DeleteURL}}}">Delete</a>{{/CanDelete}}
</header>
<h2>Interpreted Metadata</h2>
<table>{{#MetaData}}<tr><td>{{Key}}</td><td>{{{Value}}}</td></tr>{{/MetaData}}</table>
{{#HasLinks}}
<h2>References</h2>
{{#HasLocLinks}}
<h3>Local</h3>
<ul>
{{#LocLinks}}
{{#Valid}}<li><a href="{{{Zid}}}">{{Zid}}</a></li>{{/Valid}}
{{^Valid}}<li>{{Zid}}</li>{{/Valid}}
{{/LocLinks}}
</ul>
{{/HasLocLinks}}
{{#HasExtLinks}}
<h3>External</h3>
<ul>
{{#ExtLinks}}
<li><a href="{{{.}}}"{{{ExtNewWindow}}}>{{.}}</a></li>
{{/ExtLinks}}
</ul>
{{/HasExtLinks}}


{{/HasLinks}}







<h2>Parts and encodings</h2>
<table>
{{#EvalMatrix}}
<tr>
<th>{{Header}}</th>
{{#Elements}}<td><a href="{{{URL}}}">{{Text}}</td>
{{/Elements}}













<


















>
>
|
>
>
>
>
>
>
>







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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<article>
<header>
<h1>Information for Zettel {{Zid}}</h1>
<a href="{{{WebURL}}}">Web</a>
&#183; <a href="{{{ContextURL}}}">Context</a>
{{#CanWrite}} &#183; <a href="{{{EditURL}}}">Edit</a>{{/CanWrite}}
{{#CanFolge}} &#183; <a href="{{{FolgeURL}}}">Folge</a>{{/CanFolge}}
{{#CanCopy}} &#183; <a href="{{{CopyURL}}}">Copy</a>{{/CanCopy}}
{{#CanRename}}&#183; <a href="{{{RenameURL}}}">Rename</a>{{/CanRename}}
{{#CanDelete}}&#183; <a href="{{{DeleteURL}}}">Delete</a>{{/CanDelete}}
</header>
<h2>Interpreted Metadata</h2>
<table>{{#MetaData}}<tr><td>{{Key}}</td><td>{{{Value}}}</td></tr>{{/MetaData}}</table>

<h2>References</h2>
{{#HasLocLinks}}
<h3>Local</h3>
<ul>
{{#LocLinks}}
{{#Valid}}<li><a href="{{{Zid}}}">{{Zid}}</a></li>{{/Valid}}
{{^Valid}}<li>{{Zid}}</li>{{/Valid}}
{{/LocLinks}}
</ul>
{{/HasLocLinks}}
{{#HasExtLinks}}
<h3>External</h3>
<ul>
{{#ExtLinks}}
<li><a href="{{{.}}}"{{{ExtNewWindow}}}>{{.}}</a></li>
{{/ExtLinks}}
</ul>
{{/HasExtLinks}}
<h3>Unlinked</h3>
<ul>
{{#UnLinks}}
<li><a href="{{{URL}}}">{{{Text}}}</a></li>
{{/UnLinks}}
</ul>
<form>
<label for="phrase">Search Phrase</label>
<input class="zs-input" type="text" id="phrase" name="{{QueryKeyPhrase}}" placeholder="Phrase.." value="{{UnLinksPhrase}}">
</form>
<h2>Parts and encodings</h2>
<table>
{{#EvalMatrix}}
<tr>
<th>{{Header}}</th>
{{#Elements}}<td><a href="{{{URL}}}">{{Text}}</td>
{{/Elements}}

Changes to box/constbox/zettel.mustache.

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{{/FolgeLinks}}
</ul>
</details>
</nav>
{{/HasFolgeLinks}}
{{#HasBackLinks}}
<nav>
<details>
<summary>Additional links to this zettel</summary>
<ul>
{{#BackLinks}}
<li><a href="{{{URL}}}">{{Text}}</a></li>
{{/BackLinks}}
</ul>
</details>
</nav>
{{/HasBackLinks}}







|
|








25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{{/FolgeLinks}}
</ul>
</details>
</nav>
{{/HasFolgeLinks}}
{{#HasBackLinks}}
<nav>
<details open>
<summary>Incoming</summary>
<ul>
{{#BackLinks}}
<li><a href="{{{URL}}}">{{Text}}</a></li>
{{/BackLinks}}
</ul>
</details>
</nav>
{{/HasBackLinks}}

Changes to box/dirbox/makedir.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package dirbox provides a directory-based zettel box.
package dirbox

import (
	"zettelstore.de/z/box/dirbox/notifydir"
	"zettelstore.de/z/box/dirbox/simpledir"
	"zettelstore.de/z/kernel"
)










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package dirbox

import (
	"zettelstore.de/z/box/dirbox/notifydir"
	"zettelstore.de/z/box/dirbox/simpledir"
	"zettelstore.de/z/kernel"
)

Changes to box/dirbox/notifydir/service.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package notifydir manages the notified directory part of a dirstore.
package notifydir

import (
	"log"
	"time"

	"zettelstore.de/z/box"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package notifydir

import (
	"log"
	"time"

	"zettelstore.de/z/box"

Changes to box/dirbox/notifydir/watch.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package notifydir manages the notified directory part of a dirstore.
package notifydir

import (
	"os"
	"path/filepath"
	"regexp"
	"time"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package notifydir

import (
	"os"
	"path/filepath"
	"regexp"
	"time"

Changes to box/dirbox/notifydir/watch_test.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package notifydir manages the notified directory part of a dirstore.
package notifydir

import "testing"

func sameStringSlices(sl1, sl2 []string) bool {
	if len(sl1) != len(sl2) {
		return false










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package notifydir

import "testing"

func sameStringSlices(sl1, sl2 []string) bool {
	if len(sl1) != len(sl2) {
		return false

Changes to box/dirbox/service.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package dirbox provides a directory-based zettel box.
package dirbox

import (
	"os"

	"zettelstore.de/z/box/dirbox/directory"
	"zettelstore.de/z/box/filebox"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package dirbox

import (
	"os"

	"zettelstore.de/z/box/dirbox/directory"
	"zettelstore.de/z/box/filebox"

Changes to box/filebox/zipbox.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package filebox provides boxes that are stored in a file.
package filebox

import (
	"archive/zip"
	"context"
	"io"
	"regexp"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package filebox

import (
	"archive/zip"
	"context"
	"io"
	"regexp"

Changes to box/helper.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package box provides a generic interface to zettel boxes.
package box

import (
	"time"

	"zettelstore.de/z/domain/id"
)










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package box

import (
	"time"

	"zettelstore.de/z/domain/id"
)

Changes to box/manager/anteroom.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package manager coordinates the various boxes and indexes of a Zettelstore.
package manager

import (
	"sync"

	"zettelstore.de/z/domain/id"
)










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package manager

import (
	"sync"

	"zettelstore.de/z/domain/id"
)

Changes to box/manager/anteroom_test.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package manager coordinates the various boxes and indexes of a Zettelstore.
package manager

import (
	"testing"

	"zettelstore.de/z/domain/id"
)










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package manager

import (
	"testing"

	"zettelstore.de/z/domain/id"
)

Changes to box/manager/box.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package manager coordinates the various boxes and indexes of a Zettelstore.
package manager

import (
	"bytes"
	"context"
	"errors"











<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package manager

import (
	"bytes"
	"context"
	"errors"

Changes to box/manager/collect.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package manager coordinates the various boxes and indexes of a Zettelstore.
package manager

import (
	"strings"

	"zettelstore.de/z/ast"
	"zettelstore.de/z/box/manager/store"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package manager

import (
	"strings"

	"zettelstore.de/z/ast"
	"zettelstore.de/z/box/manager/store"

Changes to box/manager/enrich.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package manager coordinates the various boxes and indexes of a Zettelstore.
package manager

import (
	"context"
	"strconv"

	"zettelstore.de/c/api"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package manager

import (
	"context"
	"strconv"

	"zettelstore.de/c/api"

Changes to box/manager/indexer.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package manager coordinates the various boxes and indexes of a Zettelstore.
package manager

import (
	"context"
	"net/url"
	"time"











<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package manager

import (
	"context"
	"net/url"
	"time"

Changes to box/manager/memstore/refs.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package memstore stored the index in main memory.
package memstore

import "zettelstore.de/z/domain/id"

func refsDiff(refsN, refsO id.Slice) (newRefs, remRefs id.Slice) {
	npos, opos := 0, 0
	for npos < len(refsN) && opos < len(refsO) {










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package memstore

import "zettelstore.de/z/domain/id"

func refsDiff(refsN, refsO id.Slice) (newRefs, remRefs id.Slice) {
	npos, opos := 0, 0
	for npos < len(refsN) && opos < len(refsO) {

Changes to box/manager/memstore/refs_test.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package memstore stored the index in main memory.
package memstore

import (
	"testing"

	"zettelstore.de/z/domain/id"
)










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package memstore

import (
	"testing"

	"zettelstore.de/z/domain/id"
)

Changes to box/manager/store/wordset.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package store contains general index data for storing a zettel index.
package store

// WordSet contains the set of all words, with the count of their occurrences.
type WordSet map[string]int

// NewWordSet returns a new WordSet.
func NewWordSet() WordSet { return make(WordSet) }










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package store

// WordSet contains the set of all words, with the count of their occurrences.
type WordSet map[string]int

// NewWordSet returns a new WordSet.
func NewWordSet() WordSet { return make(WordSet) }

Changes to box/manager/store/wordset_test.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package store contains general index data for storing a zettel index.
package store_test

import (
	"sort"
	"testing"

	"zettelstore.de/z/box/manager/store"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package store_test

import (
	"sort"
	"testing"

	"zettelstore.de/z/box/manager/store"

Changes to box/manager/store/zettel.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package store contains general index data for storing a zettel index.
package store

import "zettelstore.de/z/domain/id"

// ZettelIndex contains all index data of a zettel.
type ZettelIndex struct {
	Zid      id.Zid            // zid of the indexed zettel










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package store

import "zettelstore.de/z/domain/id"

// ZettelIndex contains all index data of a zettel.
type ZettelIndex struct {
	Zid      id.Zid            // zid of the indexed zettel

Changes to cmd/cmd_run.go.

63
64
65
66
67
68
69

70
71
72
73
74
75
76
	ucListMeta := usecase.NewListMeta(protectedBoxManager)
	ucListRoles := usecase.NewListRole(protectedBoxManager)
	ucListTags := usecase.NewListTags(protectedBoxManager)
	ucZettelContext := usecase.NewZettelContext(protectedBoxManager, rtConfig)
	ucDelete := usecase.NewDeleteZettel(protectedBoxManager)
	ucUpdate := usecase.NewUpdateZettel(protectedBoxManager)
	ucRename := usecase.NewRenameZettel(protectedBoxManager)


	webSrv.Handle("/", wui.MakeGetRootHandler(protectedBoxManager))

	// Web user interface
	if !authManager.IsReadonly() {
		webSrv.AddZettelRoute('b', server.MethodGet, wui.MakeGetRenameZettelHandler(
			ucGetMeta, &ucEvaluate))







>







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
	ucListMeta := usecase.NewListMeta(protectedBoxManager)
	ucListRoles := usecase.NewListRole(protectedBoxManager)
	ucListTags := usecase.NewListTags(protectedBoxManager)
	ucZettelContext := usecase.NewZettelContext(protectedBoxManager, rtConfig)
	ucDelete := usecase.NewDeleteZettel(protectedBoxManager)
	ucUpdate := usecase.NewUpdateZettel(protectedBoxManager)
	ucRename := usecase.NewRenameZettel(protectedBoxManager)
	ucUnlinkedRefs := usecase.NewUnlinkedReferences(protectedBoxManager, rtConfig)

	webSrv.Handle("/", wui.MakeGetRootHandler(protectedBoxManager))

	// Web user interface
	if !authManager.IsReadonly() {
		webSrv.AddZettelRoute('b', server.MethodGet, wui.MakeGetRenameZettelHandler(
			ucGetMeta, &ucEvaluate))
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118


119
120
121
122
123
124
125
	webSrv.AddListRoute('h', server.MethodGet, wui.MakeListHTMLMetaHandler(
		ucListMeta, ucListRoles, ucListTags, &ucEvaluate))
	webSrv.AddZettelRoute('h', server.MethodGet, wui.MakeGetHTMLZettelHandler(
		&ucEvaluate, ucGetMeta))
	webSrv.AddListRoute('i', server.MethodGet, wui.MakeGetLoginOutHandler())
	webSrv.AddListRoute('i', server.MethodPost, wui.MakePostLoginHandler(ucAuthenticate))
	webSrv.AddZettelRoute('i', server.MethodGet, wui.MakeGetInfoHandler(
		ucParseZettel, &ucEvaluate, ucGetMeta, ucGetAllMeta))
	webSrv.AddListRoute('i', server.MethodGet, wui.MakeGetLoginOutHandler())
	webSrv.AddZettelRoute('k', server.MethodGet, wui.MakeZettelContextHandler(
		ucZettelContext, &ucEvaluate))

	// API
	webSrv.AddListRoute('a', server.MethodPost, a.MakePostLoginHandler(ucAuthenticate))
	webSrv.AddListRoute('a', server.MethodPut, a.MakeRenewAuthHandler())
	webSrv.AddListRoute('j', server.MethodGet, api.MakeListMetaHandler(ucListMeta))
	webSrv.AddZettelRoute('j', server.MethodGet, api.MakeGetZettelHandler(ucGetZettel))
	webSrv.AddZettelRoute('l', server.MethodGet, api.MakeGetLinksHandler(ucEvaluate))
	webSrv.AddZettelRoute('m', server.MethodGet, api.MakeGetMetaHandler(ucGetMeta))
	webSrv.AddZettelRoute('o', server.MethodGet, api.MakeGetOrderHandler(
		usecase.NewZettelOrder(protectedBoxManager, ucEvaluate)))
	webSrv.AddZettelRoute('p', server.MethodGet, a.MakeGetParsedZettelHandler(ucParseZettel))
	webSrv.AddListRoute('r', server.MethodGet, api.MakeListRoleHandler(ucListRoles))
	webSrv.AddListRoute('t', server.MethodGet, api.MakeListTagsHandler(ucListTags))


	webSrv.AddListRoute('v', server.MethodPost, a.MakePostEncodeInlinesHandler(ucEvaluate))
	webSrv.AddZettelRoute('v', server.MethodGet, a.MakeGetEvalZettelHandler(ucEvaluate))
	webSrv.AddZettelRoute('x', server.MethodGet, api.MakeZettelContextHandler(ucZettelContext))
	webSrv.AddListRoute('z', server.MethodGet, a.MakeListPlainHandler(ucListMeta))
	webSrv.AddZettelRoute('z', server.MethodGet, a.MakeGetPlainZettelHandler(ucGetZettel))
	if !authManager.IsReadonly() {
		webSrv.AddListRoute('j', server.MethodPost, a.MakePostCreateZettelHandler(ucCreateZettel))







|
<















>
>







96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
	webSrv.AddListRoute('h', server.MethodGet, wui.MakeListHTMLMetaHandler(
		ucListMeta, ucListRoles, ucListTags, &ucEvaluate))
	webSrv.AddZettelRoute('h', server.MethodGet, wui.MakeGetHTMLZettelHandler(
		&ucEvaluate, ucGetMeta))
	webSrv.AddListRoute('i', server.MethodGet, wui.MakeGetLoginOutHandler())
	webSrv.AddListRoute('i', server.MethodPost, wui.MakePostLoginHandler(ucAuthenticate))
	webSrv.AddZettelRoute('i', server.MethodGet, wui.MakeGetInfoHandler(
		ucParseZettel, &ucEvaluate, ucGetMeta, ucGetAllMeta, ucUnlinkedRefs))

	webSrv.AddZettelRoute('k', server.MethodGet, wui.MakeZettelContextHandler(
		ucZettelContext, &ucEvaluate))

	// API
	webSrv.AddListRoute('a', server.MethodPost, a.MakePostLoginHandler(ucAuthenticate))
	webSrv.AddListRoute('a', server.MethodPut, a.MakeRenewAuthHandler())
	webSrv.AddListRoute('j', server.MethodGet, api.MakeListMetaHandler(ucListMeta))
	webSrv.AddZettelRoute('j', server.MethodGet, api.MakeGetZettelHandler(ucGetZettel))
	webSrv.AddZettelRoute('l', server.MethodGet, api.MakeGetLinksHandler(ucEvaluate))
	webSrv.AddZettelRoute('m', server.MethodGet, api.MakeGetMetaHandler(ucGetMeta))
	webSrv.AddZettelRoute('o', server.MethodGet, api.MakeGetOrderHandler(
		usecase.NewZettelOrder(protectedBoxManager, ucEvaluate)))
	webSrv.AddZettelRoute('p', server.MethodGet, a.MakeGetParsedZettelHandler(ucParseZettel))
	webSrv.AddListRoute('r', server.MethodGet, api.MakeListRoleHandler(ucListRoles))
	webSrv.AddListRoute('t', server.MethodGet, api.MakeListTagsHandler(ucListTags))
	webSrv.AddZettelRoute('u', server.MethodGet, api.MakeListUnlinkedMetaHandler(
		ucGetMeta, ucUnlinkedRefs, &ucEvaluate))
	webSrv.AddListRoute('v', server.MethodPost, a.MakePostEncodeInlinesHandler(ucEvaluate))
	webSrv.AddZettelRoute('v', server.MethodGet, a.MakeGetEvalZettelHandler(ucEvaluate))
	webSrv.AddZettelRoute('x', server.MethodGet, api.MakeZettelContextHandler(ucZettelContext))
	webSrv.AddListRoute('z', server.MethodGet, a.MakeListPlainHandler(ucListMeta))
	webSrv.AddZettelRoute('z', server.MethodGet, a.MakeGetPlainZettelHandler(ucGetZettel))
	if !authManager.IsReadonly() {
		webSrv.AddListRoute('j', server.MethodPost, a.MakePostCreateZettelHandler(ucCreateZettel))

Changes to cmd/command.go.

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Command stores information about commands / sub-commands.
type Command struct {
	Name       string              // command name as it appears on the command line
	Func       CommandFunc         // function that executes a command
	Boxes      bool                // if true then boxes will be set up
	Header     bool                // Print a heading on startup
	LineServer bool                // Start admin line server
	Flags      func(*flag.FlagSet) // function to set up flag.FlagSet
	flags      *flag.FlagSet       // flags that belong to the command
}

// CommandFunc is the function that executes the command.
// It accepts the parsed command line parameters.
// It returns the exit code and an error.
type CommandFunc func(*flag.FlagSet) (int, error)







|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Command stores information about commands / sub-commands.
type Command struct {
	Name       string              // command name as it appears on the command line
	Func       CommandFunc         // function that executes a command
	Boxes      bool                // if true then boxes will be set up
	Header     bool                // Print a heading on startup
	LineServer bool                // Start admin line server
	SetFlags   func(*flag.FlagSet) // function to set up flag.FlagSet
	flags      *flag.FlagSet       // flags that belong to the command
}

// CommandFunc is the function that executes the command.
// It accepts the parsed command line parameters.
// It returns the exit code and an error.
type CommandFunc func(*flag.FlagSet) (int, error)
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
	if cmd.Name == "" || cmd.Func == nil {
		panic("Required command values missing")
	}
	if _, ok := commands[cmd.Name]; ok {
		panic("Command already registered: " + cmd.Name)
	}
	cmd.flags = flag.NewFlagSet(cmd.Name, flag.ExitOnError)
	if cmd.Flags != nil {
		cmd.Flags(cmd.flags)
	}
	commands[cmd.Name] = cmd
}

// Get returns the command identified by the given name and a bool to signal success.
func Get(name string) (Command, bool) {
	cmd, ok := commands[name]







|
|







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
	if cmd.Name == "" || cmd.Func == nil {
		panic("Required command values missing")
	}
	if _, ok := commands[cmd.Name]; ok {
		panic("Command already registered: " + cmd.Name)
	}
	cmd.flags = flag.NewFlagSet(cmd.Name, flag.ExitOnError)
	if cmd.SetFlags != nil {
		cmd.SetFlags(cmd.flags)
	}
	commands[cmd.Name] = cmd
}

// Get returns the command identified by the given name and a bool to signal success.
func Get(name string) (Command, bool) {
	cmd, ok := commands[name]

Changes to cmd/main.go.

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
	})
	RegisterCommand(Command{
		Name:       "run",
		Func:       runFunc,
		Boxes:      true,
		Header:     true,
		LineServer: true,
		Flags:      flgRun,
	})
	RegisterCommand(Command{
		Name:   "run-simple",
		Func:   runSimpleFunc,
		Boxes:  true,
		Header: true,
		Flags:  flgSimpleRun,
	})
	RegisterCommand(Command{
		Name: "file",
		Func: cmdFile,
		Flags: func(fs *flag.FlagSet) {
			fs.String("t", api.EncoderHTML.String(), "target output encoding")
		},
	})
	RegisterCommand(Command{
		Name: "password",
		Func: cmdPassword,
	})







|


|
|
|
|
|




|







56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
	})
	RegisterCommand(Command{
		Name:       "run",
		Func:       runFunc,
		Boxes:      true,
		Header:     true,
		LineServer: true,
		SetFlags:   flgRun,
	})
	RegisterCommand(Command{
		Name:     "run-simple",
		Func:     runSimpleFunc,
		Boxes:    true,
		Header:   true,
		SetFlags: flgSimpleRun,
	})
	RegisterCommand(Command{
		Name: "file",
		Func: cmdFile,
		SetFlags: func(fs *flag.FlagSet) {
			fs.String("t", api.EncoderHTML.String(), "target output encoding")
		},
	})
	RegisterCommand(Command{
		Name: "password",
		Func: cmdPassword,
	})
262
263
264
265
266
267
268



269
270
271
272
273
274
275

276
277

278



279
280

281

282
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s: %v\n", name, err)
	}
	kernel.Main.Shutdown(true)
	return exitCode
}




// Main is the real entrypoint of the zettelstore.
func Main(progName, buildVersion string) {
	kernel.Main.SetConfig(kernel.CoreService, kernel.CoreProgname, progName)
	kernel.Main.SetConfig(kernel.CoreService, kernel.CoreVersion, buildVersion)
	var exitCode int
	if len(os.Args) <= 1 {
		exitCode = runSimple()

	} else {
		exitCode = executeCommand(os.Args[1], os.Args[2:]...)

	}



	if exitCode != 0 {
		os.Exit(exitCode)

	}

}







>
>
>

|


|
|
|
>
|
<
>
|
>
>
>
|
<
>

>

262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280

281
282
283
284
285
286

287
288
289
290
	if err != nil {
		fmt.Fprintf(os.Stderr, "%s: %v\n", name, err)
	}
	kernel.Main.Shutdown(true)
	return exitCode
}

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")

// Main is the real entrypoint of the zettelstore.
func Main(progName, buildVersion string) int {
	kernel.Main.SetConfig(kernel.CoreService, kernel.CoreProgname, progName)
	kernel.Main.SetConfig(kernel.CoreService, kernel.CoreVersion, buildVersion)
	flag.Parse()
	if *cpuprofile != "" || *memprofile != "" {
		if *cpuprofile != "" {
			kernel.Main.StartProfiling(kernel.ProfileCPU, *cpuprofile)
		} else {

			kernel.Main.StartProfiling(kernel.ProfileHead, *memprofile)
		}
		defer kernel.Main.StopProfiling()
	}
	args := flag.Args()
	if len(args) == 0 {

		return runSimple()
	}
	return executeCommand(args[0], args[1:]...)
}

Changes to cmd/zettelstore/main.go.

1
2
3
4
5
6
7
8
9
10
11
12
13



14

15
16
17
18
19
20

21
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package main is the starting point for the zettelstore command.
package main




import "zettelstore.de/z/cmd"


// Version variable. Will be filled by build process.
var version string = ""

func main() {
	cmd.Main("Zettelstore", version)

}













>
>
>
|
>





|
>

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
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package main is the starting point for the zettelstore command.
package main

import (
	"os"

	"zettelstore.de/z/cmd"
)

// Version variable. Will be filled by build process.
var version string = ""

func main() {
	exitCode := cmd.Main("Zettelstore", version)
	os.Exit(exitCode)
}

Changes to docs/development/20210916194900.zettel.

1
2
3
4
5
6


7
8
9
10
11
12
13
id: 20210916194900
title: Checklist for Release
role: zettel
syntax: zmk
modified: 20211110193448



# Clean up your Go workspace:
#* ``go run tools/build.go clean`` (alternatively: ``make clean``).
# All internal tests must succeed:
#* ``go run tools/build.go relcheck`` (alternatively: ``make relcheck``).
# The API tests must succeed on every development platform:
#* ``go run tools/build.go testapi`` (alternatively: ``make api``).
# Run [[linkchecker|https://linkchecker.github.io/linkchecker/]] with the manual:




|

>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
id: 20210916194900
title: Checklist for Release
role: zettel
syntax: zmk
modified: 20211113182134

# Sync with the official repository
#* ``fossil sync -u``
# Clean up your Go workspace:
#* ``go run tools/build.go clean`` (alternatively: ``make clean``).
# All internal tests must succeed:
#* ``go run tools/build.go relcheck`` (alternatively: ``make relcheck``).
# The API tests must succeed on every development platform:
#* ``go run tools/build.go testapi`` (alternatively: ``make api``).
# Run [[linkchecker|https://linkchecker.github.io/linkchecker/]] with the manual:

Changes to docs/manual/00001002000000.zettel.

1
2

3
4
5
6
7
8
9
10
11
12
id: 00001002000000
title: Design goals for the Zettelstore

tags: #design #goal #manual #zettelstore
syntax: zmk
role: manual

Zettelstore supports the following design goals:

; Longevity of stored notes / zettel
: Every zettel you create should be readable without the help of any tool, even without Zettelstore.
: It should be not hard to write other software that works with your zettel.
; Single user


>


|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001002000000
title: Design goals for the Zettelstore
role: manual
tags: #design #goal #manual #zettelstore
syntax: zmk
modified: 20211124131628

Zettelstore supports the following design goals:

; Longevity of stored notes / zettel
: Every zettel you create should be readable without the help of any tool, even without Zettelstore.
: It should be not hard to write other software that works with your zettel.
; Single user
20
21
22
23
24
25
26
27
28
29
30
31
; Ease of operation
: There is only one executable for Zettelstore and one directory, where your zettel are stored.
: If you decide to use multiple directories, you are free to configure Zettelstore appropriately.
; Multiple modes of operation
: You can use Zettelstore as a standalone software on your device, but you are not restricted to it.
: You can install the software on a central server, or you can install it on all your devices with no restrictions how to synchronize your zettel.
; Multiple user interfaces
: Zettelstore provides a default web-based user interface.
  Anybody can provide alternative user interfaces, e.g. for special purposes.
; Simple service
: The purpose of Zettelstore is to safely store your zettel and to provide some initial relations between them.
: External software can be written to deeply analyze your zettel and the structures they form.







|




21
22
23
24
25
26
27
28
29
30
31
32
; Ease of operation
: There is only one executable for Zettelstore and one directory, where your zettel are stored.
: If you decide to use multiple directories, you are free to configure Zettelstore appropriately.
; Multiple modes of operation
: You can use Zettelstore as a standalone software on your device, but you are not restricted to it.
: You can install the software on a central server, or you can install it on all your devices with no restrictions how to synchronize your zettel.
; Multiple user interfaces
: Zettelstore provides a default [[web-based user interface|00001014000000]].
  Anybody can provide alternative user interfaces, e.g. for special purposes.
; Simple service
: The purpose of Zettelstore is to safely store your zettel and to provide some initial relations between them.
: External software can be written to deeply analyze your zettel and the structures they form.

Changes to docs/manual/00001003000000.zettel.

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
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
id: 00001003000000
title: Installation of the Zettelstore software
role: manual
tags: #installation #manual #zettelstore
syntax: zmk


=== The curious user
You just want to check out the Zettelstore software

* Grab the appropriate executable and copy it into any directory
* Start the Zettelstore software, e.g. with a double click
* A sub-directory ""zettel"" will be created in the directory where you put the executable.
  It will contain your future zettel.
* Open the URI [[http://localhost:23123]] with your web browser.
  It will present you a mostly empty Zettelstore.
  There will be a zettel titled ""[[Home|00010000000000]]"" that contains some helpful information.
* Please read the instructions for the web-based user interface and learn about the various ways to write zettel.
* If you restart your device, please make sure to start your Zettelstore again.

=== The intermediate user
You already tried the Zettelstore software and now you want to use it permanently.


* Grab the appropriate executable and copy it into the appropriate directory
* ...

=== The server administrator
You want to provide a shared Zettelstore that can be used from your various devices.
Installing Zettelstore as a Linux service is not that hard.

Grab the appropriate executable and copy it into the appropriate directory:
```sh
# sudo mv zettelstore /usr/local/bin/zettelstore
```
Create a group named ''zettelstore'':
```sh
# sudo groupadd --system zettelstore
```
Create a system user of that group, named ''zettelstore'', with a home folder:
```sh
# sudo useradd --system --gid zettelstore \
    --create-home --home-dir /var/lib/zettelstore \
    --shell /usr/sbin/nologin \
    --comment "Zettelstore server" \
    zettelstore
```
Create a systemd service file and store it into ''/etc/systemd/system/zettelstore.service'':
```ini
[Unit]
Description=Zettelstore
After=network.target

[Service]
Type=simple
User=zettelstore
Group=zettelstore
ExecStart=/usr/local/bin/zettelstore run -d /var/lib/zettelstore
WorkingDirectory=/var/lib/zettelstore

[Install]
WantedBy=multi-user.target
```
Double-check everything. Now you can enable and start the zettelstore as a service:
```sh
# sudo systemctl daemon-reload
# sudo systemctl enable zettelstore
# sudo systemctl start zettelstore
```
Use the commands ``systemctl``{=sh} and ``journalctl``{=sh} to manage the service, e.g.:
```sh
# sudo systemctl status zettelstore  # verify that it is running
# sudo journalctl -u zettelstore     # obtain the output of the running zettelstore
```





>











|




>

<
|





<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
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
28
29
30





















31





















id: 00001003000000
title: Installation of the Zettelstore software
role: manual
tags: #installation #manual #zettelstore
syntax: zmk
modified: 20211125185908

=== The curious user
You just want to check out the Zettelstore software

* Grab the appropriate executable and copy it into any directory
* Start the Zettelstore software, e.g. with a double click
* A sub-directory ""zettel"" will be created in the directory where you put the executable.
  It will contain your future zettel.
* Open the URI [[http://localhost:23123]] with your web browser.
  It will present you a mostly empty Zettelstore.
  There will be a zettel titled ""[[Home|00010000000000]]"" that contains some helpful information.
* Please read the instructions for the [[web-based user interface|00001014000000]] and learn about the various ways to write zettel.
* If you restart your device, please make sure to start your Zettelstore again.

=== The intermediate user
You already tried the Zettelstore software and now you want to use it permanently.
Zettelstore should start automatically when you log into your computer.


Please follow [[these instructions|00001003300000]].

=== The server administrator
You want to provide a shared Zettelstore that can be used from your various devices.
Installing Zettelstore as a Linux service is not that hard.






















Please follow [[these instructions|00001003600000]].





















Added docs/manual/00001003300000.zettel.





















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
id: 00001003300000
title: Zettelstore installation for the intermediate user
role: manual
tags: #installation #manual #zettelstore
syntax: zmk
modified: 20211125190643

You already tried the Zettelstore software and now you want to use it permanently.
Zettelstore should start automatically when you log into your computer.

* Grab the appropriate executable and copy it into the appropriate directory
* If you want to place your zettel into another directory, or if you want more than one [[Zettelstore box|00001004011200]], or if you want to [[enable authentication|00001010040100]], or if you want to tweak your Zettelstore in some other way, create an appropriate [[startup configuration file|00001004010000]].
* If you created a startup configuration file, you need to test it:
** Start a command line prompt for your operating system.
** Navigate to the directory, where you placed the Zettelstore executable.
   In most cases, this is done by the command ``cd DIR``, where ''DIR'' denotes the directory, where you placed the executable.
** Start the Zettelstore:
*** On Windows execute the command ``zettelstore.exe run -c CONFIG_FILE``
*** On macOS execute the command ``./iZettelstore run -c CONFIG_FILE``
*** On Linux execute the command ``./zettelstore run -c CONFIG_FILE``
** In all cases ''CONFIG_FILE'' must be substituted by file name where you wrote the startup configuration.
** If you encounter some error messages, update the startup configuration, and try again.
* Depending on your operating system, there are different ways to register Zettelstore to start automatically:
** [[Windows|00001003305000]]
** macOS
** Linux

Added docs/manual/00001003305000.zettel.















































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
id: 00001003305000
title: Enable Zettelstore to start automatically on Windows
role: manual
tags: #installation #manual #zettelstore
syntax: zmk
modified: 20211125201602

Windows is a complicated beast. There are several ways to automatically start Zettelstore.

=== Startup folder

One way is to use the [[autostart folder|https://support.microsoft.com/en-us/windows/add-an-app-to-run-automatically-at-startup-in-windows-10-150da165-dcd9-7230-517b-cf3c295d89dd]].
Open the folder where you have placed in the Explorer.
Create a shortcut file for the Zettelstore executable.
There are some ways to do this:
* Execute a right-click on the executable, and choose the menu entry ""Create shortcut"",
* Execute a right-click on the executable, and then click Send To > Desktop (Create shortcut).
* Drag the executable to your Desktop with pressing the ++Alt++-Key.

If you have created the shortcut file, you must move it into the Startup folder.
Press the Windows logo key and the key ++R++, type ''shell:startup''.
Select the OK button.
This will open the Startup folder.
Move the shortcut file into this folder.

The next time you log into your computer, Zettelstore will be started automatically.
However, it remains visible, at least in the task bar.

You can modify the behavior by changing some properties of the shortcut file.

=== Task scheduler

The Windows Task scheduler allows you to start Zettelstore as an background task.

This is both an advantage and a disadvantage.

On the plus side, Zettelstore runs in the background, and it does not disturbs you.
All you have to do is to open your web browser, enter the appropriate URL, and there you go.

On the negative side, you will not be notified when you enter the wrong data in the Task scheduler and Zettelstore fails to start.
This can be mitigated by first using the command line prompt to start Zettelstore with the appropriate options.
Once everything works, you can register Zettelstore to be automatically started by the task scheduler.
There you should make sure that you have followed the first steps as described on the [[parent page|00001003300000]].

To sart the Task scheduler management console, press the Windows logo key and the key ++R++, type ''taskschd.msc''.
Select the OK button.

{{00001003305102}}

This will start the ""Task Scheduler"".

Now, create a new task with ""Create Task ...""

{{00001003305104}}

Enter a name for the task, e.g. ""Zettelstore"" and select the options ""Run whether user is logged in or not"" and ""Do not store password.""

{{00001003305106}}

Create a new trigger.

{{00001003305108}}

Select the option ""At startup"".

{{00001003305110}}

Create a new action.

{{00001003305112}}

The next steps are the trickiest.

If you did not created a startup configuration file, then create an action that starts a program.
Enter the file path where you placed the Zettelstore executable.
The ""Browse ..."" button helps you with that.[^I store my Zettelstore executable in the sub-directory ''bin'' of my home directory.]

It is essential that you also enter a directory, which serves as the environment for your zettelstore.
The (sub-) directory ''zettel'', which will contain your zettel, will be placed in this directory.
If you leave the field ""Start in (optional)"" empty, the directory will be an internal Windows system directory (most likely: ''C:\\Windows\\System32'').

If you press the OK button, the ""Create Task"" tab shows up as on the right image.

{{00001003305114}}\ {{00001003305116}}

If you have created a startup configuration file, you must enter something into the field ""Add arguments (optional)"".
Unfortunately, the text box is too narrow to fully see its content.

I have entered the string ''run -c "C:\\Users\\Detlef Stern\\bin\\zsconfig.txt"'', because my startup configuration file has the name ''zsconfig.txt'' and I placed it into the same folder that also contains the Zettelstore executable.
Maybe you have to adapt to this.

You must also enter appropriate data for the other form fields.
If you press the OK button, the ""Create Task"" tab shows up as on the right image.

{{00001003305118}}\ {{00001003305120}}

You should disable any additional conditions, since you typically want to use Zettelstore unconditionally.
Especially, make sure that ""Start the task only if the computer is on AC power"" is disabled.
Otherwise Zettelstore will not start if you run on battery power.

{{00001003305122}}

On the ""Settings"" tab, you should disable the option ""Stop the task if it runs longer than:"".

{{00001003305124}}

After entering the data, press the OK button.
Under some circumstances, Windows asks for permission and you have to enter your password.

As the last step, you could run the freshly created task manually.

Open your browser, enter the appropriate URL and use your Zettelstore.
In case of errors, the task will most likely stop immediately.
Make sure that all data you have entered is valid.
To not forget to check the content of the startup configuration file.
Use the command prompt to debug your configuration.

Sometimes, for example when your computer was in stand-by and it wakes up, these tasks are not started.
In this case execute the task scheduler and run the task manually.

Added docs/manual/00001003305102.png.

cannot compute difference between binary files

Added docs/manual/00001003305104.png.

cannot compute difference between binary files

Added docs/manual/00001003305106.png.

cannot compute difference between binary files

Added docs/manual/00001003305108.png.

cannot compute difference between binary files

Added docs/manual/00001003305110.png.

cannot compute difference between binary files

Added docs/manual/00001003305112.png.

cannot compute difference between binary files

Added docs/manual/00001003305114.png.

cannot compute difference between binary files

Added docs/manual/00001003305116.png.

cannot compute difference between binary files

Added docs/manual/00001003305118.png.

cannot compute difference between binary files

Added docs/manual/00001003305120.png.

cannot compute difference between binary files

Added docs/manual/00001003305122.png.

cannot compute difference between binary files

Added docs/manual/00001003305124.png.

cannot compute difference between binary files

Added docs/manual/00001003600000.zettel.











































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
id: 00001003600000
title: Installation of Zettelstore on a server
role: manual
tags: #installation #manual #zettelstore
syntax: zmk
modified: 20211125185833

You want to provide a shared Zettelstore that can be used from your various devices.
Installing Zettelstore as a Linux service is not that hard.

Grab the appropriate executable and copy it into the appropriate directory:
```sh
# sudo mv zettelstore /usr/local/bin/zettelstore
```
Create a group named ''zettelstore'':
```sh
# sudo groupadd --system zettelstore
```
Create a system user of that group, named ''zettelstore'', with a home folder:
```sh
# sudo useradd --system --gid zettelstore \
    --create-home --home-dir /var/lib/zettelstore \
    --shell /usr/sbin/nologin \
    --comment "Zettelstore server" \
    zettelstore
```
Create a systemd service file and store it into ''/etc/systemd/system/zettelstore.service'':
```ini
[Unit]
Description=Zettelstore
After=network.target

[Service]
Type=simple
User=zettelstore
Group=zettelstore
ExecStart=/usr/local/bin/zettelstore run -d /var/lib/zettelstore
WorkingDirectory=/var/lib/zettelstore

[Install]
WantedBy=multi-user.target
```
Double-check everything. Now you can enable and start the zettelstore as a service:
```sh
# sudo systemctl daemon-reload
# sudo systemctl enable zettelstore
# sudo systemctl start zettelstore
```
Use the commands ``systemctl``{=sh} and ``journalctl``{=sh} to manage the service, e.g.:
```sh
# sudo systemctl status zettelstore  # verify that it is running
# sudo journalctl -u zettelstore     # obtain the output of the running zettelstore
```

Changes to docs/manual/00001004010000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001004010000
title: Zettelstore startup configuration
role: manual
tags: #configuration #manual #zettelstore
syntax: zmk
modified: 20211109172143

The configuration file, as specified by the ''-c CONFIGFILE'' [[command line option|00001004051000]], allows you to specify some startup options.
These options cannot be stored in a [[configuration zettel|00001004020000]] because either they are needed before Zettelstore can start or because of security reasons.
For example, Zettelstore need to know in advance, on which network address is must listen or where zettel are stored.
An attacker that is able to change the owner can do anything.
Therefore only the owner of the computer on which Zettelstore runs can change this information.






|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001004010000
title: Zettelstore startup configuration
role: manual
tags: #configuration #manual #zettelstore
syntax: zmk
modified: 20211124140743

The configuration file, as specified by the ''-c CONFIGFILE'' [[command line option|00001004051000]], allows you to specify some startup options.
These options cannot be stored in a [[configuration zettel|00001004020000]] because either they are needed before Zettelstore can start or because of security reasons.
For example, Zettelstore need to know in advance, on which network address is must listen or where zettel are stored.
An attacker that is able to change the owner can do anything.
Therefore only the owner of the computer on which Zettelstore runs can change this information.

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
76
77
78
79
80
81
82
83
84
85
  Default: ''notify''
; [!insecure-cookie]''insecure-cookie''
: Must be set to ''true'', if authentication is enabled and Zettelstore is not accessible not via HTTPS (but via HTTP).
  Otherwise web browser are free to ignore the authentication cookie.

  Default: ''false''
; [!listen-addr]''listen-addr''
: Configures the network address, where is zettel web service is listening for requests.
  Syntax is: ''[NETWORKIP]:PORT'', where ''NETWORKIP'' is the IP-address of the networking interface (or something like ''0.0.0.0'' if you want to listen on all network interfaces, and ''PORT'' is the TCP port.

  Default value: ''"127.0.0.1:23123"''
; [!owner]''owner''
: [[Identifier|00001006050000]] of a zettel that contains data about the owner of the Zettelstore.
  The owner has full authorization for the Zettelstore.
  Only if owner is set to some value, user [[authentication|00001010000000]] is enabled.
; [!persistent-cookie]''persistent-cookie''
: A boolean value to make the access cookie persistent.
  This is helpful if you access the Zettelstore via a mobile device.
  On these devices, the operating system is free to stop the web browser and to remove temporary cookies.
  Therefore, an authenticated user will be logged off.

  If ''true'', a persistent cookie is used.
  Its lifetime exceeds the lifetime of the authentication token (see option ''token-lifetime-html'') by 30 seconds.

  Default: ''false''
; [!read-only-mode]''read-only-mode''
: Puts the Zettelstore web service into a read-only mode.
  No changes are possible.
  Default: false.
; [!token-lifetime-api]''token-lifetime-api'', [!token-lifetime-html]''token-lifetime-html''
: Define lifetime of access tokens in minutes.
  Values are only valid if authentication is enabled, i.e. key ''owner'' is set.

  ''token-lifetime-api'' is for accessing Zettelstore via its API.
  Default: 10.

  ''token-lifetime-html'' specifies the lifetime for the HTML views.
  Default: 60.
  It is automatically extended, when a new HTML view is rendered.
; [!url-prefix]''url-prefix''
: Add the given string as a prefix to the local part of a Zettelstore local URL/URI when rendering zettel representations.







|


















|






|







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
76
77
78
79
80
81
82
83
84
85
  Default: ''notify''
; [!insecure-cookie]''insecure-cookie''
: Must be set to ''true'', if authentication is enabled and Zettelstore is not accessible not via HTTPS (but via HTTP).
  Otherwise web browser are free to ignore the authentication cookie.

  Default: ''false''
; [!listen-addr]''listen-addr''
: Configures the network address, where the Zettelstore service is listening for requests.
  Syntax is: ''[NETWORKIP]:PORT'', where ''NETWORKIP'' is the IP-address of the networking interface (or something like ''0.0.0.0'' if you want to listen on all network interfaces, and ''PORT'' is the TCP port.

  Default value: ''"127.0.0.1:23123"''
; [!owner]''owner''
: [[Identifier|00001006050000]] of a zettel that contains data about the owner of the Zettelstore.
  The owner has full authorization for the Zettelstore.
  Only if owner is set to some value, user [[authentication|00001010000000]] is enabled.
; [!persistent-cookie]''persistent-cookie''
: A boolean value to make the access cookie persistent.
  This is helpful if you access the Zettelstore via a mobile device.
  On these devices, the operating system is free to stop the web browser and to remove temporary cookies.
  Therefore, an authenticated user will be logged off.

  If ''true'', a persistent cookie is used.
  Its lifetime exceeds the lifetime of the authentication token (see option ''token-lifetime-html'') by 30 seconds.

  Default: ''false''
; [!read-only-mode]''read-only-mode''
: Puts the Zettelstore service into a read-only mode.
  No changes are possible.
  Default: false.
; [!token-lifetime-api]''token-lifetime-api'', [!token-lifetime-html]''token-lifetime-html''
: Define lifetime of access tokens in minutes.
  Values are only valid if authentication is enabled, i.e. key ''owner'' is set.

  ''token-lifetime-api'' is for accessing Zettelstore via its [[API|00001012000000]].
  Default: 10.

  ''token-lifetime-html'' specifies the lifetime for the HTML views.
  Default: 60.
  It is automatically extended, when a new HTML view is rendered.
; [!url-prefix]''url-prefix''
: Add the given string as a prefix to the local part of a Zettelstore local URL/URI when rendering zettel representations.

Changes to docs/manual/00001004011400.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id: 00001004011400
title: Configure file directory boxes
role: manual
tags: #configuration #manual #zettelstore
syntax: zmk
modified: 20210525121232

Under certain circumstances, it is preferable to further configure a file directory box.
This is done by appending query parameters after the base box URI ''dir:\//DIR''.

The following parameters are supported:

|= Parameter:|Description|Default value:|
|type|(Sub-) Type of the directory service|(value of ''[[default-dir-box-type|00001004010000#default-dir-box-type]]'')
|rescan|Time (in seconds) after which the directory should be scanned fully|600
|worker|Number of worker that can access the directory in parallel|(depends on type)
|readonly|Allow only operations that do not change a zettel or create a new zettel|n/a

=== Type
On some operating systems, Zettelstore tries to detect changes to zettel files outside of Zettelstore's control[^This includes Linux, Windows, and macOS.].
On other operating systems, this may be not possible, due to technical limitations.
Automatic detection of external changes is also not possible, if zettel files are put on an external service, such as a file server accessed via SMD/CIFS or NFS.

To cope with this uncertainty, Zettelstore provides various internal implementations of a directory box.





|










|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id: 00001004011400
title: Configure file directory boxes
role: manual
tags: #configuration #manual #zettelstore
syntax: zmk
modified: 20211124135711

Under certain circumstances, it is preferable to further configure a file directory box.
This is done by appending query parameters after the base box URI ''dir:\//DIR''.

The following parameters are supported:

|= Parameter:|Description|Default value:|
|type|(Sub-) Type of the directory service|(value of ''[[default-dir-box-type|00001004010000#default-dir-box-type]]'')
|rescan|Time (in seconds) after which the directory should be scanned fully|600
|worker|Number of worker that can access the directory in parallel|(depends on type)
|readonly|Allow only operations that do not create or change zettel|n/a

=== Type
On some operating systems, Zettelstore tries to detect changes to zettel files outside of Zettelstore's control[^This includes Linux, Windows, and macOS.].
On other operating systems, this may be not possible, due to technical limitations.
Automatic detection of external changes is also not possible, if zettel files are put on an external service, such as a file server accessed via SMD/CIFS or NFS.

To cope with this uncertainty, Zettelstore provides various internal implementations of a directory box.

Changes to docs/manual/00001004020000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001004020000
title: Configure the running Zettelstore
role: manual
tags: #configuration #manual #zettelstore
syntax: zmk
modified: 20210810103936

You can configure a running Zettelstore by modifying the special zettel with the ID [[00000000000100]].
This zettel is called ""configuration zettel"".
The following metadata keys change the appearance / behavior of Zettelstore:

; [!default-copyright]''default-copyright''
: Copyright value to be used when rendering content.





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001004020000
title: Configure the running Zettelstore
role: manual
tags: #configuration #manual #zettelstore
syntax: zmk
modified: 20211124175528

You can configure a running Zettelstore by modifying the special zettel with the ID [[00000000000100]].
This zettel is called ""configuration zettel"".
The following metadata keys change the appearance / behavior of Zettelstore:

; [!default-copyright]''default-copyright''
: Copyright value to be used when rendering content.
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
  Can be overwritten in a zettel with [[meta key|00001006020000]] ''license''.
  Default: (the empty string).
; [!default-role]''default-role''
: Role to be used, if a zettel specifies no ''role'' [[meta key|00001006020000]].
  Default: ''zettel''.
; [!default-syntax]''default-syntax''
: Syntax to be used, if a zettel specifies no ''syntax'' [[meta key|00001006020000]].
  Default: ''zmk'' (""Zettelmarkup"").
; [!default-title]''default-title''
: Title to be used, if a zettel specifies no ''title'' [[meta key|00001006020000]].
  Default: ''Untitled''.

  You can use all [[inline-structured elements|00001007040000]] of Zettelmarkup.
; [!default-visibility]''default-visibility''
: Visibility to be used, if zettel does not specify a value for the [[''visibility''|00001006020000#visibility]] metadata key.
  Default: ''login''.
; [!expert-mode]''expert-mode''
: If set to a boolean true value, all zettel with [[visibility ""expert""|00001010070200]] will be shown (to the owner, if authentication is enabled; to all, otherwise).
  This affects most computed zettel.
  Default: False.
; [!footer-html]''footer-html''
: Contains some HTML code that will be included into the footer of each Zettelstore web page.
  It only affects the [[web user interface|00001014000000]].
  Zettel content, delivered via the [[API|00001012000000]] as JSON, etc. is not affected.
  Default: (the empty string).
; [!home-zettel]''home-zettel''
: Specifies the identifier of the zettel, that should be presented for the default view / home view.
  If not given or if the identifier does not identify a zettel, the zettel with the identifier ''00010000000000'' is shown.
; [!marker-external]''marker-external''
: Some HTML code that is displayed after a reference to external material.
  Default: ''&\#10138;'', to display a ""&#10138;"" sign.
; [!max-transclusions]''max-transclusions''
: Maximum number of indirect transclusion.
  This is used to avoid an exploding ""transclusion bomb"", a form of a [[billion laughs attack|https://en.wikipedia.org/wiki/Billion_laughs_attack]].
  Default: 1024.
; [!site-name]''site-name''
: Name of the Zettelstore instance.







|









|











|







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
  Can be overwritten in a zettel with [[meta key|00001006020000]] ''license''.
  Default: (the empty string).
; [!default-role]''default-role''
: Role to be used, if a zettel specifies no ''role'' [[meta key|00001006020000]].
  Default: ''zettel''.
; [!default-syntax]''default-syntax''
: Syntax to be used, if a zettel specifies no ''syntax'' [[meta key|00001006020000]].
  Default: ''zmk'' (""[[Zettelmarkup|00001007000000]]"").
; [!default-title]''default-title''
: Title to be used, if a zettel specifies no ''title'' [[meta key|00001006020000]].
  Default: ''Untitled''.

  You can use all [[inline-structured elements|00001007040000]] of Zettelmarkup.
; [!default-visibility]''default-visibility''
: Visibility to be used, if zettel does not specify a value for the [[''visibility''|00001006020000#visibility]] metadata key.
  Default: ''login''.
; [!expert-mode]''expert-mode''
: If set to a boolean true value, all zettel with [[visibility ""expert""|00001010070200]] will be shown (to the owner, if [[authentication is enabled|00001010040100]]; to all, otherwise).
  This affects most computed zettel.
  Default: False.
; [!footer-html]''footer-html''
: Contains some HTML code that will be included into the footer of each Zettelstore web page.
  It only affects the [[web user interface|00001014000000]].
  Zettel content, delivered via the [[API|00001012000000]] as JSON, etc. is not affected.
  Default: (the empty string).
; [!home-zettel]''home-zettel''
: Specifies the identifier of the zettel, that should be presented for the default view / home view.
  If not given or if the identifier does not identify a zettel, the zettel with the identifier ''00010000000000'' is shown.
; [!marker-external]''marker-external''
: Some HTML code that is displayed after a [[reference to external material|00001007040310]].
  Default: ''&\#10138;'', to display a ""&#10138;"" sign.
; [!max-transclusions]''max-transclusions''
: Maximum number of indirect transclusion.
  This is used to avoid an exploding ""transclusion bomb"", a form of a [[billion laughs attack|https://en.wikipedia.org/wiki/Billion_laughs_attack]].
  Default: 1024.
; [!site-name]''site-name''
: Name of the Zettelstore instance.

Changes to docs/manual/00001004050000.zettel.

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
28
id: 00001004050000
title: Command line parameters
role: manual
tags: #command #configuration #manual #zettelstore
syntax: zmk
modified: 20210511140731

Zettelstore is not just a web service that provides services of a zettelkasten.
It allows to some tasks to be executed at the command line.
Typically, the task (""sub-command"") will be given at the command line as the first parameter.

If no parameter is given, the Zettelstore is called as
```
zettelstore
```
This is equivalent to call it this way:
```sh
mkdir -p ./zettel
zettelstore run -d ./zettel -c ./.zscfg
```
Typically this is done by starting Zettelstore via a graphical user interface by double-clicking to its file icon.
=== Sub-commands
* [[``zettelstore help``|00001004050200]] lists all available sub-commands.
* [[``zettelstore version``|00001004050400]] to display version information of Zettelstore.
* [[``zettelstore run``|00001004051000]] to start the web-based Zettelstore service.
* [[``zettelstore run-simple``|00001004051100]] is typically called, when you start Zettelstore by a double.click in your GUI.
* [[``zettelstore file``|00001004051200]] to render files manually without activated/running Zettelstore services.
* [[``zettelstore password``|00001004051400]] to calculate data for user authentication.





|

|
















|


|
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
28
id: 00001004050000
title: Command line parameters
role: manual
tags: #command #configuration #manual #zettelstore
syntax: zmk
modified: 20211124141554

Zettelstore is not just a service that provides services of a zettelkasten.
It allows to some tasks to be executed at the command line.
Typically, the task (""sub-command"") will be given at the command line as the first parameter.

If no parameter is given, the Zettelstore is called as
```
zettelstore
```
This is equivalent to call it this way:
```sh
mkdir -p ./zettel
zettelstore run -d ./zettel -c ./.zscfg
```
Typically this is done by starting Zettelstore via a graphical user interface by double-clicking to its file icon.
=== Sub-commands
* [[``zettelstore help``|00001004050200]] lists all available sub-commands.
* [[``zettelstore version``|00001004050400]] to display version information of Zettelstore.
* [[``zettelstore run``|00001004051000]] to start the Zettelstore service.
* [[``zettelstore run-simple``|00001004051100]] is typically called, when you start Zettelstore by a double.click in your GUI.
* [[``zettelstore file``|00001004051200]] to render files manually without activated/running Zettelstore services.
* [[``zettelstore password``|00001004051400]] to calculate data for [[user authentication|00001010040200]].

Changes to docs/manual/00001004050400.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001004050400
title: The ''version'' sub-command
role: manual
tags: #command #configuration #manual #zettelstore
syntax: zmk
modified: 20210712234031

Emits some information about the Zettelstore's version.
This allows you to check, whether your installed Zettelstore is 

The name of the software (""Zettelstore"") and the build version information is given, as well as the compiler version, and an indication about the operating system and the processor architecture of that computer.

The build version information is a string like ''1.0.2+351ae138b4''.





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001004050400
title: The ''version'' sub-command
role: manual
tags: #command #configuration #manual #zettelstore
syntax: zmk
modified: 20211124182041

Emits some information about the Zettelstore's version.
This allows you to check, whether your installed Zettelstore is 

The name of the software (""Zettelstore"") and the build version information is given, as well as the compiler version, and an indication about the operating system and the processor architecture of that computer.

The build version information is a string like ''1.0.2+351ae138b4''.
21
22
23
24
25
26
27



```
# zettelstore version
Zettelstore 1.0.2+351ae138b4 (go1.16.5@linux/amd64)
Licensed under the latest version of the EUPL (European Union Public License)
```
In this example, Zettelstore is running in the released version ""1.0.2"" and was compiled using [[Go, version 1.16.5|https://golang.org/doc/go1.16]].
The software was build for running under a Linux operating system with an ""amd64"" processor.










>
>
>
21
22
23
24
25
26
27
28
29
30
```
# zettelstore version
Zettelstore 1.0.2+351ae138b4 (go1.16.5@linux/amd64)
Licensed under the latest version of the EUPL (European Union Public License)
```
In this example, Zettelstore is running in the released version ""1.0.2"" and was compiled using [[Go, version 1.16.5|https://golang.org/doc/go1.16]].
The software was build for running under a Linux operating system with an ""amd64"" processor.

The build version is also stored in the public, [[predefined|00001005090000]] zettel titled ""[[Zettelstore Version|00000000000001]]"".
However, to access this zettel, you need a [[running zettelstore|00001004051000]].

Changes to docs/manual/00001004051000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001004051000
title: The ''run'' sub-command
role: manual
tags: #command #configuration #manual #zettelstore
syntax: zmk
modified: 20211109172046

=== ``zettelstore run``
This starts the web service.

```
zettelstore run [-a PORT] [-c CONFIGFILE] [-d DIR] [-debug] [-p PORT] [-r] [-v]
```





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001004051000
title: The ''run'' sub-command
role: manual
tags: #command #configuration #manual #zettelstore
syntax: zmk
modified: 20211124140711

=== ``zettelstore run``
This starts the web service.

```
zettelstore run [-a PORT] [-c CONFIGFILE] [-d DIR] [-debug] [-p PORT] [-r] [-v]
```
35
36
37
38
39
40
41
42
43
44
45
46
47
48

  Default: 23123.

  Zettelstore listens only on ''127.0.0.1'', e.g. only requests from the current computer will be processed.
  If you want to listen on network card to process requests from other computer, please use [[''listen-addr''|00001004010000#listen-addr]] of the configuration file as described below.
; [!r]''-r''
: Puts the Zettelstore in read-only mode.
  No changes are possible via the web interface / via the API.

  This allows to publish your content without any risks of unauthorized changes.
; [!v]''-v''
: Be more verbose when writing logs.

Command line options take precedence over [[configuration file|00001004010000]] options.







|






35
36
37
38
39
40
41
42
43
44
45
46
47
48

  Default: 23123.

  Zettelstore listens only on ''127.0.0.1'', e.g. only requests from the current computer will be processed.
  If you want to listen on network card to process requests from other computer, please use [[''listen-addr''|00001004010000#listen-addr]] of the configuration file as described below.
; [!r]''-r''
: Puts the Zettelstore in read-only mode.
  No changes are possible via the [[web user interface|00001014000000]] / via the [[API|00001012000000]].

  This allows to publish your content without any risks of unauthorized changes.
; [!v]''-v''
: Be more verbose when writing logs.

Command line options take precedence over [[configuration file|00001004010000]] options.

Added docs/manual/00001004059900.zettel.

















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id: 00001004059900
title: Command line flags for profiling the application
role: manual
tags: #command #configuration #manual #zettelstore
syntax: zmk
modified: 20211122174951

If you want to measure potential bottlenecks within the software Zettelstore,
there are two [[command line|00001004050000]] flags for enabling the measurement (also called __profiling__):

; ''-cpuprofile FILE''
: Enables CPU profiling.
  ''FILE'' must be the name of the file where the data is stored.
; ''-memprofile FILE''
: Enables memory profiling.
  ''FILE'' must be the name of the file where the data is stored.

Normally, profiling will stop when you stop the software Zettelstore.
The given ''FILE'' can be used to analyze the data via the tool ``go tool pprof FILE``.

Please notice that ''-cpuprofile'' takes precedence over ''-memprofile''.
You cannot measure both.

You also can use the [[administrator console|00001004100000]] to begin and end profiling manually for a already running Zettelstore.

Changes to docs/manual/00001004101000.zettel.

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
28
29
30
31
32
33
id: 00001004101000
title: List of supported commands of the administrator console
role: manual
tags: #configuration #manual #zettelstore
syntax: zmk
modified: 20211103161956

; ''bye''
: Closes the connection to the administrator console.
; ''config SERVICE''
: Displays all valid configuration keys for the given service.

  If a key ends with the hyphen-minus character (""''-''"", ''U+002D''), the key denotes a list value.
  Keys of list elements are specified by appending a number greater than zero to the key.
; ''crlf''
: Toggles CRLF mode for console output.
  Changes end of line sequences between Windows mode (==\\r\\n==) and non-Windows mode (==\\n==, initial value).
  Often used on Windows telnet clients that otherwise scramble the output of commands.
; ''dump-index''
: Displays the content of the internal search index.
; ''dump-recover RECOVER''
: Displays data about the last given recovered internal activity.

  The value for ''RECOVER'' can be obtained via the command ``stat core``, which lists all overview data about all recoveries.
; ''echo''
: Toggles the echo mode, where each command is printed before execution


; ''env''
: Display environment values.
; ''help''
: Displays a list of all available commands.
; ''get-config''
: Displays current configuration data.






|



















|
>
>







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
28
29
30
31
32
33
34
35
id: 00001004101000
title: List of supported commands of the administrator console
role: manual
tags: #configuration #manual #zettelstore
syntax: zmk
modified: 20211122175810

; ''bye''
: Closes the connection to the administrator console.
; ''config SERVICE''
: Displays all valid configuration keys for the given service.

  If a key ends with the hyphen-minus character (""''-''"", ''U+002D''), the key denotes a list value.
  Keys of list elements are specified by appending a number greater than zero to the key.
; ''crlf''
: Toggles CRLF mode for console output.
  Changes end of line sequences between Windows mode (==\\r\\n==) and non-Windows mode (==\\n==, initial value).
  Often used on Windows telnet clients that otherwise scramble the output of commands.
; ''dump-index''
: Displays the content of the internal search index.
; ''dump-recover RECOVER''
: Displays data about the last given recovered internal activity.

  The value for ''RECOVER'' can be obtained via the command ``stat core``, which lists all overview data about all recoveries.
; ''echo''
: Toggles the echo mode, where each command is printed before execution.
; ''end-profile''
: Stops profiling the application.
; ''env''
: Display environment values.
; ''help''
: Displays a list of all available commands.
; ''get-config''
: Displays current configuration data.

46
47
48
49
50
51
52












53
54
55
56
57
58
59
  It will be the current configuration, if the corresponding services is restarted.

  ``next-config`` shows all next configuration data.

  ``next-config SERVICE`` shows only the next configuration data of the given service.

  ``next-config SERVICE KEY`` shows the next configuration data for the given service and key.












; ''restart SERVICE''
: Restart the given service and all other that depend on this.
; ''services''
: Displays s list of all available services and their current status.
; ''set-config SERVICE KEY VALUE''
: Sets a single configuration value for the next configuration of a given service.
  It will become effective if the service is restarted.







>
>
>
>
>
>
>
>
>
>
>
>







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
  It will be the current configuration, if the corresponding services is restarted.

  ``next-config`` shows all next configuration data.

  ``next-config SERVICE`` shows only the next configuration data of the given service.

  ``next-config SERVICE KEY`` shows the next configuration data for the given service and key.
; ''profile [PROFILE] [FILE]''
: Starts to profile the software with the profile PROFILE and writes profiling data to file FILE.
  If PROFILE is not given, a value ''CPU'' is assumed, which specifies to profile CPU usage.
  If FILE is not given, a value ''PROFILE.prof'' will be used.

  Other values for ''PROFILE'' are: ''goroutine'', ''heap'', ''allocs'', ''threadcreate'', ''block'', and ''mutex''.
  In the future, more values may be appropriate.
  See the [[Go documentation|https://pkg.go.dev/runtime/pprof#Profile]] for details.

  This feature is dependent on the internal implementation language of Zettelstore, Go.
  It may be removed without any further notice at any time.
  In most cases, it is a tool for software developers to optimize Zettelstore's internal workings.
; ''restart SERVICE''
: Restart the given service and all other that depend on this.
; ''services''
: Displays s list of all available services and their current status.
; ''set-config SERVICE KEY VALUE''
: Sets a single configuration value for the next configuration of a given service.
  It will become effective if the service is restarted.

Changes to docs/manual/00001005000000.zettel.

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
28
29
30
31
32
33
34
35
36
37
id: 00001005000000
title: Structure of Zettelstore
role: manual
tags: #design #manual #zettelstore
syntax: zmk
modified: 20211103163918

Zettelstore is a software that manages your zettel.
Since every zettel must be readable without any special tool, most zettel has to be stored as ordinary files within specific directories.
Typically, file names and file content must comply to specific rules so that Zettelstore can manage them.
If you add, delete, or change zettel files with other tools, e.g. a text editor, Zettelstore will monitor these actions.

Zettelstore provides additional services to the user.
Via a builtin web interface you can work with zettel in various ways.
For example, you are able to list zettel, to create new zettel, to edit them, or to delete them.
You can view zettel details and relations between zettel.

In addition, Zettelstore provides an ""application programming interface"" (API) that allows other software to communicate with the Zettelstore.
Zettelstore becomes extensible by external software.
For example, a more sophisticated web interface could be build, or an application for your mobile device that allows you to send content to your Zettelstore as new zettel.

=== Where zettel are stored

Your zettel are stored typically as files in a specific directory.
If you have not explicitly specified the directory, a default directory will be used.
The directory has to be specified at [[startup time|00001004010000]].
Nested directories are not supported (yet).

Every file in this directory that should be monitored by Zettelstore must have a file name that begins with 14 digits (0-9), the [[zettel identifier|00001006050000]].
If you create a new zettel via the web interface or the API, the zettel identifier will be the timestamp of the current date and time (format is ''YYYYMMDDhhmmss'').
This allows zettel to be sorted naturally by creation time.

Since the only restriction on zettel identifiers are the 14 digits, you are free to use other digit sequences.
The [[configuration zettel|00001004020000]] is one prominent example, as well as these manual zettel.
You can create these special zettel identifiers either with the __rename__ function of Zettelstore or by manually renaming the underlying zettel files.

It is allowed that the file name contains other characters after the 14 digits.





|







|



|

|









|







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
28
29
30
31
32
33
34
35
36
37
id: 00001005000000
title: Structure of Zettelstore
role: manual
tags: #design #manual #zettelstore
syntax: zmk
modified: 20211124183108

Zettelstore is a software that manages your zettel.
Since every zettel must be readable without any special tool, most zettel has to be stored as ordinary files within specific directories.
Typically, file names and file content must comply to specific rules so that Zettelstore can manage them.
If you add, delete, or change zettel files with other tools, e.g. a text editor, Zettelstore will monitor these actions.

Zettelstore provides additional services to the user.
Via the builtin [[web user interface|00001014000000]] you can work with zettel in various ways.
For example, you are able to list zettel, to create new zettel, to edit them, or to delete them.
You can view zettel details and relations between zettel.

In addition, Zettelstore provides an ""application programming interface"" ([[API|00001012000000]]) that allows other software to communicate with the Zettelstore.
Zettelstore becomes extensible by external software.
For example, a more sophisticated user interface could be build, or an application for your mobile device that allows you to send content to your Zettelstore as new zettel.

=== Where zettel are stored

Your zettel are stored typically as files in a specific directory.
If you have not explicitly specified the directory, a default directory will be used.
The directory has to be specified at [[startup time|00001004010000]].
Nested directories are not supported (yet).

Every file in this directory that should be monitored by Zettelstore must have a file name that begins with 14 digits (0-9), the [[zettel identifier|00001006050000]].
If you create a new zettel via the [[web user interface|00001014000000]] or via the [[API|00001012053200]], the zettel identifier will be the timestamp of the current date and time (format is ''YYYYMMDDhhmmss'').
This allows zettel to be sorted naturally by creation time.

Since the only restriction on zettel identifiers are the 14 digits, you are free to use other digit sequences.
The [[configuration zettel|00001004020000]] is one prominent example, as well as these manual zettel.
You can create these special zettel identifiers either with the __rename__ function of Zettelstore or by manually renaming the underlying zettel files.

It is allowed that the file name contains other characters after the 14 digits.
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
In case of some textual zettel content you do not want to store the metadata and the zettel content in two different files.
Here the ''.zettel'' extension will signal that the metadata and the zettel content will be put in the same file, separated by an empty line or a line with three dashes (""''-\-\-''"", also known as ""YAML separator"").

=== Predefined zettel

Zettelstore contains some [[predefined zettel|00001005090000]] to work properly.
The [[configuration zettel|00001004020000]] is one example.
To render the builtin web interface, some templates are used, as well as a layout specification in CSS.
The icon that visualizes a broken image is a predefined GIF image.
All of these are visible to the Zettelstore as zettel.

One reason for this is to allow you to modify these zettel to adapt Zettelstore to your needs and visual preferences.

Where are these zettel stored?
They are stored within the Zettelstore software itself, because one design goal was to have just one executable file to use Zettelstore.
But data stored within an executable programm cannot be changed later[^Well, it can, but it is a very bad idea to allow this. Mostly for security reasons.].

To allow changing predefined zettel, both the file store and the internal zettel store are internally chained together.
If you change a zettel, it will be always stored as a file.
If a zettel is requested, Zettelstore will first try to read that zettel from a file.
If such a file was not found, the internal zettel store is searched secondly.

Therefore, the file store ""shadows"" the internal zettel store.
If you want to read the original zettel, you either have to delete the zettel (which removes it from the file directory), or you have to rename it to another zettel identifier.
Now we have two places where zettel are stored: in the specific directory and within the Zettelstore software.

* [[List of predefined zettel|00001005090000]]

=== Boxes: other ways to store zettel
As described above, a zettel may be stored as a file inside a directory or inside the Zettelstore software itself.
Zettelstore allows other ways to store zettel by providing an abstraction called __box__.[^Formerly, zettel were stored physically in boxes, often made of wood.]

A file directory which stores zettel is called a ""directory box"".
But zettel may be also stored in a ZIP file, which is called ""file box"".
For testing purposes, zettel may be stored in volatile memory (called __RAM__).
This way is called ""memory box"".

Other types of boxes could be added to Zettelstore.
What about a ""remote Zettelstore box""?







|
|





|













|










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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
In case of some textual zettel content you do not want to store the metadata and the zettel content in two different files.
Here the ''.zettel'' extension will signal that the metadata and the zettel content will be put in the same file, separated by an empty line or a line with three dashes (""''-\-\-''"", also known as ""YAML separator"").

=== Predefined zettel

Zettelstore contains some [[predefined zettel|00001005090000]] to work properly.
The [[configuration zettel|00001004020000]] is one example.
To render the builtin [[web user interface|00001014000000]], some templates are used, as well as a [[layout specification in CSS|00000000020001]].
The icon that visualizes a broken image is a [[predefined GIF image|00000000040001]].
All of these are visible to the Zettelstore as zettel.

One reason for this is to allow you to modify these zettel to adapt Zettelstore to your needs and visual preferences.

Where are these zettel stored?
They are stored within the Zettelstore software itself, because one [[design goal|00001002000000]] was to have just one executable file to use Zettelstore.
But data stored within an executable programm cannot be changed later[^Well, it can, but it is a very bad idea to allow this. Mostly for security reasons.].

To allow changing predefined zettel, both the file store and the internal zettel store are internally chained together.
If you change a zettel, it will be always stored as a file.
If a zettel is requested, Zettelstore will first try to read that zettel from a file.
If such a file was not found, the internal zettel store is searched secondly.

Therefore, the file store ""shadows"" the internal zettel store.
If you want to read the original zettel, you either have to delete the zettel (which removes it from the file directory), or you have to rename it to another zettel identifier.
Now we have two places where zettel are stored: in the specific directory and within the Zettelstore software.

* [[List of predefined zettel|00001005090000]]

=== Boxes: alternative ways to store zettel
As described above, a zettel may be stored as a file inside a directory or inside the Zettelstore software itself.
Zettelstore allows other ways to store zettel by providing an abstraction called __box__.[^Formerly, zettel were stored physically in boxes, often made of wood.]

A file directory which stores zettel is called a ""directory box"".
But zettel may be also stored in a ZIP file, which is called ""file box"".
For testing purposes, zettel may be stored in volatile memory (called __RAM__).
This way is called ""memory box"".

Other types of boxes could be added to Zettelstore.
What about a ""remote Zettelstore box""?

Changes to docs/manual/00001005090000.zettel.

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
id: 00001005090000
title: List of predefined zettel
role: manual
tags: #manual #reference #zettelstore
syntax: zmk
modified: 20210622124647

The following table lists all predefined zettel with their purpose.

|= Identifier :|= Title | Purpose
| [[00000000000001]] | Zettelstore Version | Contains the version string of the running Zettelstore
| [[00000000000002]] | Zettelstore Host | Contains the name of the computer running the Zettelstore
| [[00000000000003]] | Zettelstore Operating System | Contains the operating system and CPU architecture of the computer running the Zettelstore
| [[00000000000004]] | Zettelstore License | Lists the license of Zettelstore
| [[00000000000005]] | Zettelstore Contributors | Lists all contributors of Zettelstore
| [[00000000000006]] | Zettelstore Dependencies | Lists all licensed content
| [[00000000000020]] | Zettelstore Box Manager | Contains some statistics about zettel boxes and the the index process
| [[00000000000090]] | Zettelstore Supported Metadata Keys | Contains all supported metadata keys, their [[types|00001006030000]], and more

| [[00000000000096]] | Zettelstore Startup Configuration | Contains the effective values of the [[startup configuration|00001004010000]]
| [[00000000000100]] | Zettelstore Runtime Configuration | Allows to [[configure Zettelstore at runtime|00001004020000]]
| [[00000000010100]] | Zettelstore Base HTML Template | Contains the general layout of the HTML view
| [[00000000010200]] | Zettelstore Login Form HTML Template | Layout of the login form, when authentication is [[enabled|00001010040100]]
| [[00000000010300]] | Zettelstore List Meta HTML Template | Used when displaying a list of zettel
| [[00000000010401]] | Zettelstore Detail HTML Template | Layout for the HTML detail view of one zettel
| [[00000000010402]] | Zettelstore Info HTML Templöate | Layout for the information view of a specific zettel
| [[00000000010403]] | Zettelstore Form HTML Template | Form that is used to create a new or to change an existing zettel that contains text
| [[00000000010404]] | Zettelstore Rename Form HTML Template | View that is displayed to change the [[zettel identifier|00001006050000]]
| [[00000000010405]] | Zettelstore Delete HTML Template | View to confirm the deletion of a zettel
| [[00000000010500]] | Zettelstore List Roles HTML Template | Layout for listing all roles
| [[00000000010600]] | Zettelstore List Tags HTML Template | Layout of tags lists
| [[00000000020001]] | Zettelstore Base CSS | System-defined CSS file that is included by the [[Base HTML Template|00000000010100]]
| [[00000000025001]] | Zettelstore User CSS | User-defined CSS file that is included by the [[Base HTML Template|00000000010100]]
| [[00000000040001]] | Generic Emoji | Image that is shown if original image reference is invalid
| [[00000000090000]] | New Menu | Contains items that should contain in the zettel template menu
| [[00000000090001]] | New Zettel | Template for a new zettel with role ""[[zettel|00001006020100]]""
| [[00000000090002]] | New User | Template for a new zettel with role ""[[user|00001006020100#user]]""
| [[00010000000000]] | Home | Default home zettel, contains some welcome information

If a zettel is not linked, it is not accessible for the current user.

**Important:** All identifier may change until a stable version of the software is released.





|












>














|


|





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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
id: 00001005090000
title: List of predefined zettel
role: manual
tags: #manual #reference #zettelstore
syntax: zmk
modified: 20211127174712

The following table lists all predefined zettel with their purpose.

|= Identifier :|= Title | Purpose
| [[00000000000001]] | Zettelstore Version | Contains the version string of the running Zettelstore
| [[00000000000002]] | Zettelstore Host | Contains the name of the computer running the Zettelstore
| [[00000000000003]] | Zettelstore Operating System | Contains the operating system and CPU architecture of the computer running the Zettelstore
| [[00000000000004]] | Zettelstore License | Lists the license of Zettelstore
| [[00000000000005]] | Zettelstore Contributors | Lists all contributors of Zettelstore
| [[00000000000006]] | Zettelstore Dependencies | Lists all licensed content
| [[00000000000020]] | Zettelstore Box Manager | Contains some statistics about zettel boxes and the the index process
| [[00000000000090]] | Zettelstore Supported Metadata Keys | Contains all supported metadata keys, their [[types|00001006030000]], and more
| [[00000000000092]] | Zettelstore Supported Parser | Lists all supported values for metadata [[syntax|00001006020000#syntax]] that are recognized by Zettelstore
| [[00000000000096]] | Zettelstore Startup Configuration | Contains the effective values of the [[startup configuration|00001004010000]]
| [[00000000000100]] | Zettelstore Runtime Configuration | Allows to [[configure Zettelstore at runtime|00001004020000]]
| [[00000000010100]] | Zettelstore Base HTML Template | Contains the general layout of the HTML view
| [[00000000010200]] | Zettelstore Login Form HTML Template | Layout of the login form, when authentication is [[enabled|00001010040100]]
| [[00000000010300]] | Zettelstore List Meta HTML Template | Used when displaying a list of zettel
| [[00000000010401]] | Zettelstore Detail HTML Template | Layout for the HTML detail view of one zettel
| [[00000000010402]] | Zettelstore Info HTML Templöate | Layout for the information view of a specific zettel
| [[00000000010403]] | Zettelstore Form HTML Template | Form that is used to create a new or to change an existing zettel that contains text
| [[00000000010404]] | Zettelstore Rename Form HTML Template | View that is displayed to change the [[zettel identifier|00001006050000]]
| [[00000000010405]] | Zettelstore Delete HTML Template | View to confirm the deletion of a zettel
| [[00000000010500]] | Zettelstore List Roles HTML Template | Layout for listing all roles
| [[00000000010600]] | Zettelstore List Tags HTML Template | Layout of tags lists
| [[00000000020001]] | Zettelstore Base CSS | System-defined CSS file that is included by the [[Base HTML Template|00000000010100]]
| [[00000000025001]] | Zettelstore User CSS | User-defined CSS file that is included by the [[Base HTML Template|00000000010100]]
| [[00000000040001]] | Generic Emoji | Image that is shown if [[original image reference|00001007040322]] is invalid
| [[00000000090000]] | New Menu | Contains items that should contain in the zettel template menu
| [[00000000090001]] | New Zettel | Template for a new zettel with role ""[[zettel|00001006020100]]""
| [[00000000090002]] | New User | Template for a new [[user zettel|00001010040200]]
| [[00010000000000]] | Home | Default home zettel, contains some welcome information

If a zettel is not linked, it is not accessible for the current user.

**Important:** All identifier may change until a stable version of the software is released.

Changes to docs/manual/00001006020000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001006020000
title: Supported Metadata Keys
role: manual
tags: #manual #meta #reference #zettel #zettelstore
syntax: zmk
modified: 20211103163613

Although you are free to define your own metadata, by using any key (according to the [[syntax|00001006010000]]), some keys have a special meaning that is enforced by Zettelstore.
See the [[computed list of supported metadata keys|00000000000090]] for details.

Most keys conform to a [[type|00001006030000]].

; [!all-tags]''all-tags''





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001006020000
title: Supported Metadata Keys
role: manual
tags: #manual #meta #reference #zettel #zettelstore
syntax: zmk
modified: 20211124141832

Although you are free to define your own metadata, by using any key (according to the [[syntax|00001006010000]]), some keys have a special meaning that is enforced by Zettelstore.
See the [[computed list of supported metadata keys|00000000000090]] for details.

Most keys conform to a [[type|00001006030000]].

; [!all-tags]''all-tags''
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
: Specifies the title of the zettel.
  If not given, the value ''default-title'' from the [[configuration zettel|00001004020000#default-title]] will be used.

  You can use all [[inline-structured elements|00001007040000]] of Zettelmarkup.
; [!url]''url''
: Defines an URL / URI for this zettel that possibly references external material.
  One use case is to specify the document that the current zettel comments on.
  The URL will be rendered special on the web user interface if you use the default template.
; [!user-id]''user-id''
: Provides some unique user identification for a user zettel.
  It is used as a user name for authentication.

  It is only used for zettel with a ''role'' value of ""user"".
; [!user-role]''user-role''
: Defines the basic privileges of an authenticated user, e.g. reading / changing zettel.
  Is only valid in a user zettel.

  See [[User roles|00001010070300]] for more details.
; [!visibility]''visibility''
: When you work with authentication, you can give every zettel a value to decide, who can see the zettel.
  Its default value can be set with [[''default-visibility''|00001004020000#default-visibility]] of the configuration zettel.

  See [[visibility rules for zettel|00001010070200]] for more details.







|

|













89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
: Specifies the title of the zettel.
  If not given, the value ''default-title'' from the [[configuration zettel|00001004020000#default-title]] will be used.

  You can use all [[inline-structured elements|00001007040000]] of Zettelmarkup.
; [!url]''url''
: Defines an URL / URI for this zettel that possibly references external material.
  One use case is to specify the document that the current zettel comments on.
  The URL will be rendered special in the [[web user interface|00001014000000]] if you use the default template.
; [!user-id]''user-id''
: Provides some unique user identification for an [[user zettel|00001010040200]].
  It is used as a user name for authentication.

  It is only used for zettel with a ''role'' value of ""user"".
; [!user-role]''user-role''
: Defines the basic privileges of an authenticated user, e.g. reading / changing zettel.
  Is only valid in a user zettel.

  See [[User roles|00001010070300]] for more details.
; [!visibility]''visibility''
: When you work with authentication, you can give every zettel a value to decide, who can see the zettel.
  Its default value can be set with [[''default-visibility''|00001004020000#default-visibility]] of the configuration zettel.

  See [[visibility rules for zettel|00001010070200]] for more details.

Changes to docs/manual/00001006020100.zettel.

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
28
29
30
31
32
id: 00001006020100
title: Supported Zettel Roles
role: manual
tags: #manual #meta #reference #zettel #zettelstore
syntax: zmk
modified: 20210727120817

The [[''role'' key|00001006020000#role]] defines what kind of zettel you are writing.
The following values are used internally by Zettelstore and must exist:

; [!user]''user''
: If you want to use [[authentication|00001010000000]], all zettel that identify users of the zettel store must have this role.

Beside of this, you are free to define your own roles.

The role ''zettel'' is predefined as the default role, but you can [[change this|00001004020000#default-role]].

Some roles are defined for technical reasons:

; [!configuration]''configuration''
: A zettel that contains some configuration data for the Zettelstore.
  Most prominent is [[00000000000100]], as described in [[00001004020000]].
; [!manual]''manual''
: All zettel that document the inner workings of the Zettelstore software.
  This role is used in this specific Zettelstore.

If you adhere to the process outlined by Niklas Luhmann, a zettel could have one of the following three roles:

; [!note]''note''
: A small note, to remember something.
  Notes are not real zettel, they just help to create a real zettel.
  Think of them as Post-it notes.





|


<
<
<
<
<
|










|







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
id: 00001006020100
title: Supported Zettel Roles
role: manual
tags: #manual #meta #reference #zettel #zettelstore
syntax: zmk
modified: 20211127174441

The [[''role'' key|00001006020000#role]] defines what kind of zettel you are writing.





You are free to define your own roles.

The role ''zettel'' is predefined as the default role, but you can [[change this|00001004020000#default-role]].

Some roles are defined for technical reasons:

; [!configuration]''configuration''
: A zettel that contains some configuration data for the Zettelstore.
  Most prominent is [[00000000000100]], as described in [[00001004020000]].
; [!manual]''manual''
: All zettel that document the inner workings of the Zettelstore software.
  This role is only used in this specific Zettelstore.

If you adhere to the process outlined by Niklas Luhmann, a zettel could have one of the following three roles:

; [!note]''note''
: A small note, to remember something.
  Notes are not real zettel, they just help to create a real zettel.
  Think of them as Post-it notes.

Changes to docs/manual/00001006020400.zettel.

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
28
29
30
31
32
33
34
35
36
37
38
39
40
id: 00001006020400
title: Supported values for metadata key ''read-only''
role: manual
tags: #manual #meta #reference #zettel #zettelstore
syntax: zmk


A zettel can be marked as read-only, if it contains a metadata value for key
[[''read-only''|00001006020000#read-only]].
If user authentication is [[enabled|00001010040100]], it is possible to allow some users to change the zettel,
depending on their [[user role|00001010070300]].
Otherwise, the read-only mark is just a binary value.

=== No authentication
If there is no metadata value for key ''read-only'' or if its [[boolean value|00001006030500]]
is interpreted as ""false"", anybody can modify the zettel.

If the metadata value is something else (the value ""true"" is recommended),
the user cannot modify the zettel through the web interface.
However, if the zettel is stored as a file in a [[directory box|00001004011400]],
the zettel could be modified using an external editor.

=== Authentication enabled
If there is no metadata value for key ''read-only'' or if its [[boolean value|00001006030500]]
is interpreted as ""false"", anybody can modify the zettel.

If the metadata value is the same as an explicit [[user role|00001010070300]],
users with that role (or a role with lower rights) are not allowed to modify the zettel.

; ""reader""
: Neither an unauthenticated user nor a user with role ""reader"" is allowed to modify the zettel.
  Users with role ""writer"" or the owner itself still can modify the zettel.
; ""writer""
: Neither an unauthenticated user, nor users with roles ""reader"" or ""writer"" are allowed to modify the zettel.
  Only the owner of the Zettelstore can modify the zettel.

If the metadata value is something else (one of the values ""true"" or ""owner"" is recommended),
no user is allowed modify the zettel through the web interface.
However, if the zettel is accessible as a file in a [[directory box|00001004011400]],
the zettel could be modified using an external editor.
Typically the owner of a Zettelstore have such an access.





>












|


















|



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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
id: 00001006020400
title: Supported values for metadata key ''read-only''
role: manual
tags: #manual #meta #reference #zettel #zettelstore
syntax: zmk
modified: 20211124132040

A zettel can be marked as read-only, if it contains a metadata value for key
[[''read-only''|00001006020000#read-only]].
If user authentication is [[enabled|00001010040100]], it is possible to allow some users to change the zettel,
depending on their [[user role|00001010070300]].
Otherwise, the read-only mark is just a binary value.

=== No authentication
If there is no metadata value for key ''read-only'' or if its [[boolean value|00001006030500]]
is interpreted as ""false"", anybody can modify the zettel.

If the metadata value is something else (the value ""true"" is recommended),
the user cannot modify the zettel through the [[web user interface|00001014000000]].
However, if the zettel is stored as a file in a [[directory box|00001004011400]],
the zettel could be modified using an external editor.

=== Authentication enabled
If there is no metadata value for key ''read-only'' or if its [[boolean value|00001006030500]]
is interpreted as ""false"", anybody can modify the zettel.

If the metadata value is the same as an explicit [[user role|00001010070300]],
users with that role (or a role with lower rights) are not allowed to modify the zettel.

; ""reader""
: Neither an unauthenticated user nor a user with role ""reader"" is allowed to modify the zettel.
  Users with role ""writer"" or the owner itself still can modify the zettel.
; ""writer""
: Neither an unauthenticated user, nor users with roles ""reader"" or ""writer"" are allowed to modify the zettel.
  Only the owner of the Zettelstore can modify the zettel.

If the metadata value is something else (one of the values ""true"" or ""owner"" is recommended),
no user is allowed modify the zettel through the [[web user interface|00001014000000]].
However, if the zettel is accessible as a file in a [[directory box|00001004011400]],
the zettel could be modified using an external editor.
Typically the owner of a Zettelstore have such an access.

Changes to docs/manual/00001006055000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001006055000
title: Reserved zettel identifier
role: manual
tags: #design #manual #zettelstore
syntax: zmk
modified: 20211001125243

[[Zettel identifier|00001006050000]] are typically created by examine the current date and time.
By renaming a zettel, you are able to provide any sequence of 14 digits.
If no other zettel has the same identifier, you are allowed to rename a zettel.

To make things easier, you normally should not use zettel identifier that begin with four zeroes (''0000'').






|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001006055000
title: Reserved zettel identifier
role: manual
tags: #design #manual #zettelstore
syntax: zmk
modified: 20211124182600

[[Zettel identifier|00001006050000]] are typically created by examine the current date and time.
By renaming a zettel, you are able to provide any sequence of 14 digits.
If no other zettel has the same identifier, you are allowed to rename a zettel.

To make things easier, you normally should not use zettel identifier that begin with four zeroes (''0000'').

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  If you need more than 10.000, your justification will contain more words.

=== Reserved Zettel Identifier

|= From | To | Description
| 00000000000000 | 0000000000000 | This is an invalid zettel identifier
| 00000000000001 | 0000009999999 | [[Predefined zettel|00001005090000]]
| 00000100000000 | 0000019999999 | Zettelstore manual
| 00000200000000 | 0000899999999 | Reserved for future use
| 00009000000000 | 0000999999999 | Reserved for applications

This list may change in the future.

==== External Applications
|= From | To | Description
| 00009000001000 | 00009000001999 | [[Zettel Presenter|https://zettelstore.de/contrib]], an application to display zettel as a HTML-based slideshow







|








28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  If you need more than 10.000, your justification will contain more words.

=== Reserved Zettel Identifier

|= From | To | Description
| 00000000000000 | 0000000000000 | This is an invalid zettel identifier
| 00000000000001 | 0000009999999 | [[Predefined zettel|00001005090000]]
| 00000100000000 | 0000019999999 | This [[Zettelstore manual|00001000000000]]
| 00000200000000 | 0000899999999 | Reserved for future use
| 00009000000000 | 0000999999999 | Reserved for applications

This list may change in the future.

==== External Applications
|= From | To | Description
| 00009000001000 | 00009000001999 | [[Zettel Presenter|https://zettelstore.de/contrib]], an application to display zettel as a HTML-based slideshow

Changes to docs/manual/00001007000000.zettel.

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
28
29
30
31
32
33
34
35
36
37
id: 00001007000000
title: Zettelmarkup
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211103162744

Zettelmarkup is a rich plain-text based markup language for writing zettel content.
Besides the zettel content, Zettelmarkup is also used for specifying the title of a zettel, regardless of the syntax of a zettel.

Zettelmark supports the longevity of stored notes by providing a syntax that any person can easily read, as well as a computer.
Zettelmark can be much easier parsed / consumed by a software compared to other markup languages.
Writing a parser for [[Markdown|https://daringfireball.net/projects/markdown/syntax]] is quite challenging.
[[CommonMark|https://commonmark.org/]] is an attempt to make it simpler by providing a comprehensive specification, combined with an extra chapter to give hints for the implementation.
Zettelmark follows some simple principles that anybody who knows to ho write software should be able understand to create an implementation.

Zettelmarkup is a markup language on its own.
This is in contrast to Markdown, which is basically a superset of HTML.
While HTML is a markup language that will probably last for a long time, it cannot be easily translated to other formats, such as PDF, JSON, or LaTeX.
Additionally, it is allowed to embed other languages into HTML, such as CSS or even JavaScript.
This could create problems with longevity as well as security problems.

Zettelmarkup is a rich markup language, but it focusses on relatively short zettel content.
It allows embedding other content, simple tables, quotations, description lists, and images.
It provides a broad range of inline formatting, including __emphasized__, **strong**, ;;small;;, ~~deleted~~{-} and >>inserted>> text.
Footnotes[^like this] are supported, links to other zettel and to external material, as well as citation keys.

Zettelmarkup might be seen as a proprietary markup language.
But if you want to use Markdown/CommonMark and you need support for footnotes, you'll end up with a proprietary extension.
However, the Zettelstore supports CommonMark as a zettel syntax, so you can mix both Zettelmarkup zettel and CommonMark zettel in one store to get the best of both worlds.

* [[General principles|00001007010000]]
* [[Basic definitions|00001007020000]]
* [[Block-structured elements|00001007030000]]
* [[Inline-structured element|00001007040000]]
* [[Attributes|00001007050000]]
* [[Summary of formatting characters|00001007060000]]





|


















|



|








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
28
29
30
31
32
33
34
35
36
37
id: 00001007000000
title: Zettelmarkup
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124173311

Zettelmarkup is a rich plain-text based markup language for writing zettel content.
Besides the zettel content, Zettelmarkup is also used for specifying the title of a zettel, regardless of the syntax of a zettel.

Zettelmark supports the longevity of stored notes by providing a syntax that any person can easily read, as well as a computer.
Zettelmark can be much easier parsed / consumed by a software compared to other markup languages.
Writing a parser for [[Markdown|https://daringfireball.net/projects/markdown/syntax]] is quite challenging.
[[CommonMark|https://commonmark.org/]] is an attempt to make it simpler by providing a comprehensive specification, combined with an extra chapter to give hints for the implementation.
Zettelmark follows some simple principles that anybody who knows to ho write software should be able understand to create an implementation.

Zettelmarkup is a markup language on its own.
This is in contrast to Markdown, which is basically a superset of HTML.
While HTML is a markup language that will probably last for a long time, it cannot be easily translated to other formats, such as PDF, JSON, or LaTeX.
Additionally, it is allowed to embed other languages into HTML, such as CSS or even JavaScript.
This could create problems with longevity as well as security problems.

Zettelmarkup is a rich markup language, but it focusses on relatively short zettel content.
It allows embedding other content, simple tables, quotations, description lists, and images.
It provides a broad range of inline formatting, including __emphasized__, **strong**, ~~deleted~~{-} and >>inserted>> text.
Footnotes[^like this] are supported, links to other zettel and to external material, as well as citation keys.

Zettelmarkup might be seen as a proprietary markup language.
But if you want to use [[Markdown/CommonMark|00001008010000]] and you need support for footnotes or tables, you'll end up with proprietary extensions.
However, the Zettelstore supports CommonMark as a zettel syntax, so you can mix both Zettelmarkup zettel and CommonMark zettel in one store to get the best of both worlds.

* [[General principles|00001007010000]]
* [[Basic definitions|00001007020000]]
* [[Block-structured elements|00001007030000]]
* [[Inline-structured element|00001007040000]]
* [[Attributes|00001007050000]]
* [[Summary of formatting characters|00001007060000]]

Changes to docs/manual/00001007010000.zettel.

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
id: 00001007010000
title: Zettelmarkup: General Principles

tags: #manual #zettelmarkup #zettelstore
syntax: zmk
role: manual

Any document can be thought as a sequence of paragraphs and other blocks-structural elements (""blocks""), such as headings, lists, quotations, and code blocks.
Some of these blocks can contain other blocks, for example lists may contain other lists or paragraphs.
Other blocks contain inline-structural elements (""inlines""), such as text, links, emphasized text, and images.

With the exception of lists and tables, the markup for blocks always begins at the first position of a line with three or more identical characters.
List blocks also begins at the first position of a line, but may need one or more character, plus a space character.
Table blocks begins at the first position of a line with the character ""``|``"".
Non-list blocks are either fully specified on that line or they span multiple lines and are delimited with the same three or more character.
It depends on the block kind, whether blocks are specified on one line or on at least two lines.

If a line does not begin with an explicit block element. the line is treated as a (implicit) paragraph block element that contains inline elements.
This paragraph ends when a block element is detected at the beginning of a next line or when an empty line occurs.
Some blocks may also contain inline elements, e.g. a heading.

Inline elements mostly begins with two non-space, often identical characters.
With some exceptions, two identical non-space characters begins a formatting range that is ended with the same two characters.

Exceptions are: links, images, edits, comments, and both the ""en-dash"" and the ""horizontal ellipsis"".
A link is given with ``[[...]]``{=zmk}, an images with ``{{...}}``{=zmk}, and an edit formatting with ``((...))``{=zmk}.
An inline comment, beginning with the sequence ``%%``{=zmk}, always ends at the end of the line where it begins.
The ""en-dash"" (""--"") is specified as ``--``{=zmk}, the ""horizontal ellipsis"" (""..."") as ``...``{=zmk}[^If put at the end of non-space text.].

Some inline elements do not follow the rule of two identical character, especially to specify footnotes, citation keys, and local marks.
These elements begin with one opening square bracket (""``[``""), use a character for specifying the kind of the inline, typically allow to specify some content, and end with one closing square bracket (""``]``"").

One inline element that does not begin with two characters is the ""entity"".
It allows to specify any Unicode character.
The specification of that character is put between an ampersand character and a semicolon: ``&...;``{=zmk}.
For exmple, an ""n-dash"" could also be specified as ``&ndash;``{==zmk}.

The backslash character (""``\\``"") possibly gives the next character a special meaning.
This allows to resolve some left ambiguities.
For example, a list of depth 2 will begin a line with ``** Item 2.2``{=zmk}.
An inline element to strongly emphasize some text begin with a space will be specified as ``** Text**``{=zmk}.
To force the inline element formatting at the beginning of a line, ``**\\ Text**``{=zmk} should better be specified.

Many block and inline elements can be refined by additional attributes.
Attributes resemble roughly HTML attributes and are put near the corresponding elements by using the syntax ``{...}``{=zmk}.
One example is to make space characters visible inside a inline literal element: ``1 + 2 = 3``{-} was specified by using the default attribute: ``\`\`1 + 2 = 3\`\`{-}``.

To summarize:

* With some exceptions, blocks-structural elements begins at the for position of a line with three identical characters.
* The most important exception to this rule is the specification of lists.


>


|

|

|


|
|















|













|







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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
id: 00001007010000
title: Zettelmarkup: General Principles
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124175047

Any document can be thought as a sequence of paragraphs and other [[block-structured elements|00001007030000]] (""blocks""), such as [[headings|00001007030300]], [[lists|00001007030200]], quotations, and code blocks.
Some of these blocks can contain other blocks, for example lists may contain other lists or paragraphs.
Other blocks contain [[inline-structured elements|00001007040000]] (""inlines""), such as text, [[links|00001007040310]], emphasized text, and images.

With the exception of lists and tables, the markup for blocks always begins at the first position of a line with three or more identical characters.
List blocks also begin at the first position of a line, but may need one or more identical character, plus a space character.
[[Table blocks|00001007031000]] begin at the first position of a line with the character ""``|``"".
Non-list blocks are either fully specified on that line or they span multiple lines and are delimited with the same three or more character.
It depends on the block kind, whether blocks are specified on one line or on at least two lines.

If a line does not begin with an explicit block element. the line is treated as a (implicit) paragraph block element that contains inline elements.
This paragraph ends when a block element is detected at the beginning of a next line or when an empty line occurs.
Some blocks may also contain inline elements, e.g. a heading.

Inline elements mostly begins with two non-space, often identical characters.
With some exceptions, two identical non-space characters begins a formatting range that is ended with the same two characters.

Exceptions are: links, images, edits, comments, and both the ""en-dash"" and the ""horizontal ellipsis"".
A link is given with ``[[...]]``{=zmk}, an images with ``{{...}}``{=zmk}, and an edit formatting with ``((...))``{=zmk}.
An inline comment, beginning with the sequence ``%%``{=zmk}, always ends at the end of the line where it begins.
The ""en-dash"" (""--"") is specified as ``--``{=zmk}, the ""horizontal ellipsis"" (""..."") as ``...``{=zmk}[^If put at the end of non-space text.].

Some inline elements do not follow the rule of two identical character, especially to specify [[footnotes|00001007040330]], [[citation keys|00001007040340]], and local marks.
These elements begin with one opening square bracket (""``[``""), use a character for specifying the kind of the inline, typically allow to specify some content, and end with one closing square bracket (""``]``"").

One inline element that does not begin with two characters is the ""entity"".
It allows to specify any Unicode character.
The specification of that character is put between an ampersand character and a semicolon: ``&...;``{=zmk}.
For exmple, an ""n-dash"" could also be specified as ``&ndash;``{==zmk}.

The backslash character (""``\\``"") possibly gives the next character a special meaning.
This allows to resolve some left ambiguities.
For example, a list of depth 2 will begin a line with ``** Item 2.2``{=zmk}.
An inline element to strongly emphasize some text begin with a space will be specified as ``** Text**``{=zmk}.
To force the inline element formatting at the beginning of a line, ``**\\ Text**``{=zmk} should better be specified.

Many block and inline elements can be refined by additional [[attributes|00001007050000]].
Attributes resemble roughly HTML attributes and are put near the corresponding elements by using the syntax ``{...}``{=zmk}.
One example is to make space characters visible inside a inline literal element: ``1 + 2 = 3``{-} was specified by using the default attribute: ``\`\`1 + 2 = 3\`\`{-}``.

To summarize:

* With some exceptions, blocks-structural elements begins at the for position of a line with three identical characters.
* The most important exception to this rule is the specification of lists.

Changes to docs/manual/00001007020000.zettel.

1
2

3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19
id: 00001007020000
title: Zettelmarkup: Basic Definitions

tags: #manual #zettelmarkup #zettelstore
syntax: zmk
role: manual

Every zettelmark content consists of a sequence of Unicode codepoints.
Unicode codepoints are called in the following as **character**s.

Characters are encoded with UTF-8.


A **line** is a sequence of characters, except newline (''U+000A'') and carraige return (''U+000D''), followed by a line ending sequence or the end of content.

A **line ending** is either a newline not followed by a carriage return, a newline followed by a carriage return, or a carriage return.
Different line can be finalized by different line endings.

An **empty line** is an empty sequence of characters, followed by a line ending or the end of content.

The **space** character is the Unicode codepoint ''U+0020''.


>


|






>
|
|
|
|
|
|
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
id: 00001007020000
title: Zettelmarkup: Basic Definitions
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124175237

Every zettelmark content consists of a sequence of Unicode codepoints.
Unicode codepoints are called in the following as **character**s.

Characters are encoded with UTF-8.

; Line
: A __line__ is a sequence of characters, except newline (''U+000A'') and carraige return (''U+000D''), followed by a line ending sequence or the end of content.
; Line ending
: A __line ending__ is either a newline not followed by a carriage return, a newline followed by a carriage return, or a carriage return.
  Different line can be finalized by different line endings.
; Empty line
: An __empty line__ is an empty sequence of characters, followed by a line ending or the end of content.
; Space character
: The __space character__ is the Unicode codepoint ''U+0020''.

Changes to docs/manual/00001007030000.zettel.

1
2

3
4
5
6
7
8
9
10
11
12
id: 00001007030000
title: Zettelmarkup: Blocks-Structured Elements

tags: #manual #zettelmarkup #zettelstore
syntax: zmk
role: manual

Every markup for blocks-structured elements (""blocks"") begins at the very first position of a line.

There are five kinds of blocks: lists, one-line blocks, line-range blocks, tables, and paragraphs.

=== Lists


|
>


|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001007030000
title: Zettelmarkup: Block-Structured Elements
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124174747

Every markup for blocks-structured elements (""blocks"") begins at the very first position of a line.

There are five kinds of blocks: lists, one-line blocks, line-range blocks, tables, and paragraphs.

=== Lists

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
To be useful, they encompass more lines.
They begin with at least three identical characters at the first position of the beginning line.
They end at the line, that contains at least the same number of these identical characters, beginning at the first position of that line.
This allows line-range blocks to be nested.
Additionally, all other blocks elements are allowed in line-range blocks.

* [[Verbatim blocks|00001007030500]] do not interpret their content,
* [[Quotation blocks|00001007030600]] specify a block-length quotation,
* [[Verse blocks|00001007030700]] allow to enter poetry, lyrics and similar text, where line endings are important
* [[Region blocks|00001007030800]] just mark a region, e.g. for common formatting
* [[Comment blocks|00001007030900]] allow to enter text that will be ignored when rendered

=== Tables

Similar to lists are tables not specified explicitly.
A sequence of table rows is considered a [[table|00001007031000]].
A table row itself is a sequence of table cells.








|
|
|
|







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
To be useful, they encompass more lines.
They begin with at least three identical characters at the first position of the beginning line.
They end at the line, that contains at least the same number of these identical characters, beginning at the first position of that line.
This allows line-range blocks to be nested.
Additionally, all other blocks elements are allowed in line-range blocks.

* [[Verbatim blocks|00001007030500]] do not interpret their content,
* [[Quotation blocks|00001007030600]] specify a block-length quotations,
* [[Verse blocks|00001007030700]] allow to enter poetry, lyrics and similar text, where line endings are important,
* [[Region blocks|00001007030800]] just mark regions, e.g. for common formatting,
* [[Comment blocks|00001007030900]] allow to enter text that will be ignored when rendered.

=== Tables

Similar to lists are tables not specified explicitly.
A sequence of table rows is considered a [[table|00001007031000]].
A table row itself is a sequence of table cells.

Changes to docs/manual/00001007030100.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
id: 00001007030100
title: Zettelmarkup: Description Lists
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211103163447

A description list is a sequence of terms to be described together with the descriptions of each term.
Every term can described in multiple ways.

A description term (short: __term__) is specified with one semicolon (""'';''"", ''U+003B'') at the first position, followed by a space character and the described term, specified as a sequence of line elements.
If the following lines should also be part of the term, exactly two spaces must be given at the beginning of each following line.

The description of a term is given with one colon (""'':''"", ''U+003A'') at the first position, followed by a space character and the description itself, specified as a sequence of inline elements.
Similar to terms, following lines can also be part of the actual description, if they begin at each line with exactly two space characters.

In contrast to terms, the actual descriptions are merged into a paragraph.
This is because, an actual description can contain more than one paragraph.
As usual, paragraphs are separated by an empty line.
Every following paragraph of an actual description must be indented by two space characters.






|







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
id: 00001007030100
title: Zettelmarkup: Description Lists
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124172247

A description list is a sequence of terms to be described together with the descriptions of each term.
Every term can described in multiple ways.

A description term (short: __term__) is specified with one semicolon (""'';''"", ''U+003B'') at the first position, followed by a space character and the described term, specified as a sequence of line elements.
If the following lines should also be part of the term, exactly two spaces must be given at the beginning of each following line.

The description of a term is given with one colon (""'':''"", ''U+003A'') at the first position, followed by a space character and the description itself, specified as a sequence of [[inline elements|00001007040000]].
Similar to terms, following lines can also be part of the actual description, if they begin at each line with exactly two space characters.

In contrast to terms, the actual descriptions are merged into a paragraph.
This is because, an actual description can contain more than one paragraph.
As usual, paragraphs are separated by an empty line.
Every following paragraph of an actual description must be indented by two space characters.

Changes to docs/manual/00001007030200.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
id: 00001007030200
title: Zettelmarkup: Nested Lists
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211103162835

There are thee kinds of lists that can be nested: ordered lists, unordered lists, and quotation lists.

Ordered lists are specified with the number sign (""''#''"", ''U+0023''), unordered lists use the asterisk (""''*''"", ''U+002A''), and quotation lists are specified with the greater-than sing (""''>''"", ''U+003E'').
Let's call these three characters __list characters__.

Any nested list item is specified by a non-empty sequence of list characters, followed by a space character and a sequence of inline elements.
In case of a quotation list as the last list character, the space character followed by a sequence of inline elements is optional.
The number / count of list characters gives the nesting of the lists.
If the following lines should also be part of the list item, exactly the same number of spaces must be given at the beginning of each of the following lines as it is the lists are nested, plus one additional space character.
In other words: the inline elements must begin at the same column as it was on the previous line.

The resulting sequence on inline elements is merged into a paragraph.
Appropriately indented paragraphs can specified after the first one.





|






|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
id: 00001007030200
title: Zettelmarkup: Nested Lists
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124172337

There are thee kinds of lists that can be nested: ordered lists, unordered lists, and quotation lists.

Ordered lists are specified with the number sign (""''#''"", ''U+0023''), unordered lists use the asterisk (""''*''"", ''U+002A''), and quotation lists are specified with the greater-than sing (""''>''"", ''U+003E'').
Let's call these three characters __list characters__.

Any nested list item is specified by a non-empty sequence of list characters, followed by a space character and a sequence of [[inline elements|00001007040000]].
In case of a quotation list as the last list character, the space character followed by a sequence of inline elements is optional.
The number / count of list characters gives the nesting of the lists.
If the following lines should also be part of the list item, exactly the same number of spaces must be given at the beginning of each of the following lines as it is the lists are nested, plus one additional space character.
In other words: the inline elements must begin at the same column as it was on the previous line.

The resulting sequence on inline elements is merged into a paragraph.
Appropriately indented paragraphs can specified after the first one.
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
* B

* C
:::

Please note that two lists cannot be separated by an empty line.
Instead you should put a horizonal rule (""thematic break"") between them.
You could also use a mark element or a hard line break to separate the two lists:
```zmk
# One
# Two
[!sep]
# Uno
# Due
---







|







88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
* B

* C
:::

Please note that two lists cannot be separated by an empty line.
Instead you should put a horizonal rule (""thematic break"") between them.
You could also use a [[mark element|00001007040350]] or a hard line break to separate the two lists:
```zmk
# One
# Two
[!sep]
# Uno
# Due
---

Changes to docs/manual/00001007030300.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
id: 00001007030300
title: Zettelmarkup: Headings
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211103163105

To specify a (sub-) section of a zettel, you should use the headings syntax: at
the beginning of a new line type at least three equal signs (""''=''"", ''U+003D''), plus at least one
space and enter the text of the heading as inline elements.

```zmk
=== Level 1 Heading
==== Level 2 Heading
===== Level 3 Heading
====== Level 4 Heading
======= Level 5 Heading





|



|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
id: 00001007030300
title: Zettelmarkup: Headings
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124172401

To specify a (sub-) section of a zettel, you should use the headings syntax: at
the beginning of a new line type at least three equal signs (""''=''"", ''U+003D''), plus at least one
space and enter the text of the heading as [[inline elements|00001007040000]].

```zmk
=== Level 1 Heading
==== Level 2 Heading
===== Level 3 Heading
====== Level 4 Heading
======= Level 5 Heading

Changes to docs/manual/00001007030400.zettel.

1
2

3
4
5
6
7
8
9
10
11
12
id: 00001007030400
title: Zettelmarkup: Horizontal Rule

tags: #manual #zettelmarkup #zettelstore
syntax: zmk
role: manual

To signal a thematic break, you can specify a horizonal rule.
This is done by entering at least three hyphen-minus characters (""''-''"", ''U+002D'') at the first position of a line.
You can add some [[attributes|00001007050000]], although the horizontal rule does not support the default attribute.
Any other character in this line will be ignored

If you do not enter the three hyphen-minus charachter at the very first position of a line, the are interpreted as [[inline elements|00001007040000]], typically as an ""en-dash" followed by a hyphen-minus.

|
>


|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001007030400
title: Zettelmarkup: Horizontal Rules / Thematic Break
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124174251

To signal a thematic break, you can specify a horizonal rule.
This is done by entering at least three hyphen-minus characters (""''-''"", ''U+002D'') at the first position of a line.
You can add some [[attributes|00001007050000]], although the horizontal rule does not support the default attribute.
Any other character in this line will be ignored

If you do not enter the three hyphen-minus charachter at the very first position of a line, the are interpreted as [[inline elements|00001007040000]], typically as an ""en-dash" followed by a hyphen-minus.

Changes to docs/manual/00001007030500.zettel.

1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
id: 00001007030500
title: Zettelmarkup: Verbatim Blocks

tags: #manual #zettelmarkup #zettelstore
syntax: zmk
role: manual

Verbatim blocks are used to enter text that should not be interpreted.
They begin with at least three grave accent characters (""''`''"", ''U+0060'') at the first position of a line.
Alternatively, a modifier letter grave accent (""''ˋ''"", ''U+02CB'') is also allowed[^On some devices, such as an iPhone / iPad, a grave accent character is harder to enter and is often confused with a modifier letter grave accent.].

You can add some [[attributes|00001007050000]] on the beginning line of a verbatim block, following the initiating characters.
The verbatim block supports the default attribute: when given, all spaces in the text are rendered in HTML as open box characters (""''&#x2423;''"", ''U+2423'').
If you want to give only one attribute and this attribute is the generic attribute, you can omit the most of the attribute syntax and just specify the value.
It will be interpreted as a (programming) language to support colourizing the text when rendered in HTML.

Any other character in this line will be ignored

Text following the beginning line will not be interpreted, until a line begins with at least the same number of the same characters given at the beginning line.
This allows to enter some grave accent characters in the text that should not be interpreted.

For example:


>


|








|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
id: 00001007030500
title: Zettelmarkup: Verbatim Blocks
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124170510

Verbatim blocks are used to enter text that should not be interpreted.
They begin with at least three grave accent characters (""''`''"", ''U+0060'') at the first position of a line.
Alternatively, a modifier letter grave accent (""''ˋ''"", ''U+02CB'') is also allowed[^On some devices, such as an iPhone / iPad, a grave accent character is harder to enter and is often confused with a modifier letter grave accent.].

You can add some [[attributes|00001007050000]] on the beginning line of a verbatim block, following the initiating characters.
The verbatim block supports the default attribute: when given, all spaces in the text are rendered in HTML as open box characters (""''&#x2423;''"", ''U+2423'').
If you want to give only one attribute and this attribute is the generic attribute, you can omit the most of the attribute syntax and just specify the value.
It will be interpreted as a ([[programming|00001007050200]]) language to support colorizing the text when rendered in HTML.

Any other character in this line will be ignored

Text following the beginning line will not be interpreted, until a line begins with at least the same number of the same characters given at the beginning line.
This allows to enter some grave accent characters in the text that should not be interpreted.

For example:

Changes to docs/manual/00001007030600.zettel.

1
2

3
4
5
6
7
8
9
10
11
12
id: 00001007030600
title: Zettelmarkup: Quotation Blocks

tags: #manual #zettelmarkup #zettelstore
syntax: zmk
role: manual

A simple way to enter a quotation is to use the [[quotation list|00001007030200]].
A quotation list loosely follows the convention of quoting text within emails.
However, if you want to attribute the quotation to seomeone, a quotation block is more appropriately.

This kind of line-range block begins with at least three less-than characters (""''<''"", ''U+003C'') at the first position of a line.
You can add some [[attributes|00001007050000]] on the beginning line of a quotation block, following the initiating characters.


>


|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001007030600
title: Zettelmarkup: Quotation Blocks
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124174017

A simple way to enter a quotation is to use the [[quotation list|00001007030200]].
A quotation list loosely follows the convention of quoting text within emails.
However, if you want to attribute the quotation to seomeone, a quotation block is more appropriately.

This kind of line-range block begins with at least three less-than characters (""''<''"", ''U+003C'') at the first position of a line.
You can add some [[attributes|00001007050000]] on the beginning line of a quotation block, following the initiating characters.
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

```zmk
<<<<
A quotation with an embedded quotation
<<<{style=color:green}
Embedded
<<< Embedded Author
<<<<
```
will be rendered in HTML as:
:::example
<<<<
A quotation with an embedded quotation
<<<{style=color:green}
Embedded
<<< Embedded Author
<<<< Quotation Author
:::







|










24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

```zmk
<<<<
A quotation with an embedded quotation
<<<{style=color:green}
Embedded
<<< Embedded Author
<<<< Quotation Author
```
will be rendered in HTML as:
:::example
<<<<
A quotation with an embedded quotation
<<<{style=color:green}
Embedded
<<< Embedded Author
<<<< Quotation Author
:::

Changes to docs/manual/00001007030800.zettel.

1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
id: 00001007030800
title: Zettelmarkup: Region Blocks

tags: #manual #zettelmarkup #zettelstore
syntax: zmk
role: manual

Region blocks does not directly have a visual representation.
They just group a range of lines.
You can use region blocks to enter [[attributes|00001007050000]] that apply only to this range of lines.
One example is to enter a multi-line warning that should be visible.

This kind of line-range block begins with at least three colon characters (""'':''"", ''U+003A'') at the first position of a line[^Since a [[description text|00001007030100]] only use exactly one colon character at the first position of a line, there is no possible ambiguity between these elements.].
You can add some [[attributes|00001007050000]] on the beginning line of a verse block, following the initiating characters.
The region block does not support the default attribute, but it supports the generic attribute.
Some generic attributes, like ``=note``, ``=warning`` will be rendered special.
Attributes are interpreted on HTML rendering.
Any other character in this line will be ignored.

Text following the beginning line will be interpreted, until a line begins with at least the same number of the same characters given at the beginning line.
This allows to enter a region block within a region block.


>


|







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
id: 00001007030800
title: Zettelmarkup: Region Blocks
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124173940

Region blocks does not directly have a visual representation.
They just group a range of lines.
You can use region blocks to enter [[attributes|00001007050000]] that apply only to this range of lines.
One example is to enter a multi-line warning that should be visible.

This kind of line-range block begins with at least three colon characters (""'':''"", ''U+003A'') at the first position of a line[^Since a [[description text|00001007030100]] only use exactly one colon character at the first position of a line, there is no possible ambiguity between these elements.].
You can add some [[attributes|00001007050000]] on the beginning line of a region block, following the initiating characters.
The region block does not support the default attribute, but it supports the generic attribute.
Some generic attributes, like ``=note``, ``=warning`` will be rendered special.
Attributes are interpreted on HTML rendering.
Any other character in this line will be ignored.

Text following the beginning line will be interpreted, until a line begins with at least the same number of the same characters given at the beginning line.
This allows to enter a region block within a region block.

Changes to docs/manual/00001007031000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001007031000
title: Zettelmarkup: Tables
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211103161859

Tables are used to show some data in a two-dimenensional fashion.
In zettelmarkup, table are not specified explicitly, but by entering __table rows__.
Therefore, a table can be seen as a sequence of table rows.
A table row is nothing as a sequence of __table cells__.
The length of a table is the number of table rows, the width of a table is the maximum length of its rows.






|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001007031000
title: Zettelmarkup: Tables
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124174218

Tables are used to show some data in a two-dimenensional fashion.
In zettelmarkup, table are not specified explicitly, but by entering __table rows__.
Therefore, a table can be seen as a sequence of table rows.
A table row is nothing as a sequence of __table cells__.
The length of a table is the number of table rows, the width of a table is the maximum length of its rows.

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|>R|:C|<L
|123456|123456|123456|123456|
|123|123|123|123
:::

=== Rows to be ignored
A line that begins with the sequence ''|%'' (vertical bar character (""''|''"", ''U+007C''), followed by a percent sign character (“%”, U+0025)) will be ignored.
This allows to specify a horizontal rule that is not rendered.
Such tables are emitted by some commands of the [[administrator console|00001004100000]].
For example, the command ``get-config box`` will emit
```
|=Key        | Value  | Description
|%-----------+--------+---------------------------
| defdirtype | notify | Default directory box type
```
This is rendered in HTML as:
:::example
|=Key        | Value  | Description
|%-----------+--------+---------------------------
| defdirtype | notify | Default directory box type
:::







|

|











87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|>R|:C|<L
|123456|123456|123456|123456|
|123|123|123|123
:::

=== Rows to be ignored
A line that begins with the sequence ''|%'' (vertical bar character (""''|''"", ''U+007C''), followed by a percent sign character (“%”, U+0025)) will be ignored.
For example, this allows to specify a horizontal rule that is not rendered.
Such tables are emitted by some commands of the [[administrator console|00001004100000]].
For example, the command ``get-config box`` will emit:
```
|=Key        | Value  | Description
|%-----------+--------+---------------------------
| defdirtype | notify | Default directory box type
```
This is rendered in HTML as:
:::example
|=Key        | Value  | Description
|%-----------+--------+---------------------------
| defdirtype | notify | Default directory box type
:::

Changes to docs/manual/00001007040000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001007040000
title: Zettelmarkup: Inline-Structured Elements
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211103162150

Most characters you type is concerned with inline-structured elements.
The content of a zettel contains is many cases just ordinary text, lightly formatted.
Inline-structured elements allow to format your text and add some helpful links or images.
Sometimes, you want to enter characters that have no representation on your keyboard.

; Text formatting





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001007040000
title: Zettelmarkup: Inline-Structured Elements
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124165337

Most characters you type is concerned with inline-structured elements.
The content of a zettel contains is many cases just ordinary text, lightly formatted.
Inline-structured elements allow to format your text and add some helpful links or images.
Sometimes, you want to enter characters that have no representation on your keyboard.

; Text formatting
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
It ends at the end of the line where it begins.

==== Backslash
The backslash character (""''\\''"", ''U+005C'') gives the next character another meaning.
* If a space character follows, it is converted in a non-breaking space (''U+00A0'').
* If a line ending follows the backslash character, the line break is converted from a __soft break__ into a __hard break__.
* Every other character is taken as itself, but without the interpretation of a Zettelmarkup element.
  For example, if you want to enter a ""'']''"" into a footnote text, you should escape it with a backslash.

==== Tag
Any text that begins with a number sign character (""''#''"", ''U+0023''), followed by a non-empty sequence of Unicode letters, Unicode digits, the hyphen-minus character (""''-''"", ''U+002D''), or the low line character (""''_''"", ''U+005F'') is interpreted as an __inline tag__.
They are be considered equivalent to tags in metadata.

==== Entities & more
Sometimes it is not easy to enter special characters.







|







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
It ends at the end of the line where it begins.

==== Backslash
The backslash character (""''\\''"", ''U+005C'') gives the next character another meaning.
* If a space character follows, it is converted in a non-breaking space (''U+00A0'').
* If a line ending follows the backslash character, the line break is converted from a __soft break__ into a __hard break__.
* Every other character is taken as itself, but without the interpretation of a Zettelmarkup element.
  For example, if you want to enter a ""'']''"" into a [[footnote text|00001007040330]], you should escape it with a backslash.

==== Tag
Any text that begins with a number sign character (""''#''"", ''U+0023''), followed by a non-empty sequence of Unicode letters, Unicode digits, the hyphen-minus character (""''-''"", ''U+002D''), or the low line character (""''_''"", ''U+005F'') is interpreted as an __inline tag__.
They are be considered equivalent to tags in metadata.

==== Entities & more
Sometimes it is not easy to enter special characters.

Changes to docs/manual/00001007040100.zettel.

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
id: 00001007040100
title: Zettelmarkup: Text Formatting
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211103161716

Text formatting is the way to make your text visually different.
Every text formatting element begins with two same characters.
It ends when these two same characters occur the second time.
It is possible that some [[attributes|00001007050000]] follow immediately, without any separating character.

Text formatting can be nested, up to a reasonable limit.

The following characters begin a text formatting:

* The low line character (""''_''"", ''U+005F'') emphasizes its text.
** Example: ``abc __def__ ghi`` is rendered in HTML as: ::abc __def__ ghi::{=example}.
** The slash character (""''/''"", ''U+002F'') is still allowed to emphasize text, but its use is deprecated.
   With version 0.0.17 it will no longer be supported. 
*** Example: ``abc //def// ghi`` is rendered in HTML as: ::abc //def// ghi::{=example}.
* The asterisk character (""''*''"", ''U+002A'') strongly emphasized its enclosed text.
** Example: ``abc **def** ghi`` is rendered in HTML as: ::abc **def** ghi::{=example}.
* The low line character (""''_''"", ''U+005F'') marks text as inserted.
** Example: ``abc __def__ ghi`` is rendered in HTML as: ::abc __def__ ghi::{=example}.
* Similar, the tilde character (""''~''"", ''U+007E'') marks deleted text.
** Example: ``abc ~~def~~ ghi`` is rendered in HTML as: ::abc ~~def~~ ghi::{=example}.
* The apostrophe character (""''\'''"", ''U+0027'') renders text in mono-space / fixed font width.
** Example: ``abc ''def'' ghi`` is rendered in HTML as: ::abc ''def'' ghi::{=example}.
* The circumflex accent character (""''^''"", ''U+005E'') allows to enter superscripted text.
** Example: ``e=mc^^2^^`` is rendered in HTML as: ::e=mc^^2^^::{=example}.
* The comma character (""'',''"", ''U+002C'') produces subscripted text.
** Example: ``H,,2,,O`` is rendered in HTML as: ::H,,2,,O::{=example}.
* The less-than sign character (""''<''"", ''U+003C'') marks an inline quotation.
** Example: ``<<To be or not<<`` is rendered in HTML as: ::<<To be or not<<::{=example}.
* The quotation mark character (""''"''"", ''U+0022'') produces the right typographic quotation marks according to the [[specified language|00001007050100]].
** Example: ``""To be or not""`` is rendered in HTML as: ::""To be or not""::{=example}.
** Example: ``""Sein oder nicht""{lang=de}`` is rendered in HTML as: ::""Sein oder nicht""{lang=de}::{=example}.
* The semicolon character (""'';''"", ''U+003B'') specifies text that should be rendered with small characters.
** Example: ``abc ;;def;; ghi`` is rendered in HTML as: ::abc ;;def;; ghi::{=example}.
* The colon character (""'':''"", ''U+003A'') mark some text that should belong together. It fills a similar role as [[region blocks|00001007030800]], but just for inline elements.
** Example: ``abc ::def::{style=color:green} ghi`` is rendered in HTML as: abc ::def::{style=color:green} ghi.





|












<
<
<


|
|













<
<

|
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
28
29
30
31
32
33
34
35


36
37
id: 00001007040100
title: Zettelmarkup: Text Formatting
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211113193440

Text formatting is the way to make your text visually different.
Every text formatting element begins with two same characters.
It ends when these two same characters occur the second time.
It is possible that some [[attributes|00001007050000]] follow immediately, without any separating character.

Text formatting can be nested, up to a reasonable limit.

The following characters begin a text formatting:

* The low line character (""''_''"", ''U+005F'') emphasizes its text.
** Example: ``abc __def__ ghi`` is rendered in HTML as: ::abc __def__ ghi::{=example}.



* The asterisk character (""''*''"", ''U+002A'') strongly emphasized its enclosed text.
** Example: ``abc **def** ghi`` is rendered in HTML as: ::abc **def** ghi::{=example}.
* The greater-than sign character (""''>''"", ''U+003E'') marks text as inserted.
** Example: ``abc >>def>> ghi`` is rendered in HTML as: ::abc >>def>> ghi::{=example}.
* Similar, the tilde character (""''~''"", ''U+007E'') marks deleted text.
** Example: ``abc ~~def~~ ghi`` is rendered in HTML as: ::abc ~~def~~ ghi::{=example}.
* The apostrophe character (""''\'''"", ''U+0027'') renders text in mono-space / fixed font width.
** Example: ``abc ''def'' ghi`` is rendered in HTML as: ::abc ''def'' ghi::{=example}.
* The circumflex accent character (""''^''"", ''U+005E'') allows to enter superscripted text.
** Example: ``e=mc^^2^^`` is rendered in HTML as: ::e=mc^^2^^::{=example}.
* The comma character (""'',''"", ''U+002C'') produces subscripted text.
** Example: ``H,,2,,O`` is rendered in HTML as: ::H,,2,,O::{=example}.
* The less-than sign character (""''<''"", ''U+003C'') marks an inline quotation.
** Example: ``<<To be or not<<`` is rendered in HTML as: ::<<To be or not<<::{=example}.
* The quotation mark character (""''"''"", ''U+0022'') produces the right typographic quotation marks according to the [[specified language|00001007050100]].
** Example: ``""To be or not""`` is rendered in HTML as: ::""To be or not""::{=example}.
** Example: ``""Sein oder nicht""{lang=de}`` is rendered in HTML as: ::""Sein oder nicht""{lang=de}::{=example}.


* The colon character (""'':''"", ''U+003A'') mark some text that should belong together. It fills a similar role as [[region blocks|00001007030800]], but just for inline elements.
** Example: ``abc ::def::{=example} ghi`` is rendered in HTML as: abc ::def::{=example} ghi.

Changes to docs/manual/00001007040200.zettel.

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
id: 00001007040200
title: Zettelmarkup: Literal-like formatting
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20210525121114

There are some reasons to mark text that should be rendered as uninterpreted:
* Mark text as literal, sometimes as part of a program.
* Mark text as input you give into a computer via a keyboard.
* Mark text as output from some computer, e.g. shown at the command line.

=== Literal text
Literal text somehow relates to [[verbatim blocks|00001007030500]]: their content should not be interpreted further, but may be rendered special.
It is specified by two grave accent characters (""''`''"", ''U+0060''), followed by the text, followed by again two grave accent characters, optionally followed by an [[attribute|00001007050000]] specification.
Similar to the verbatim block, the literal element allows also a modifier letter grave accent (""''ˋ''"", ''U+02CB'') as an alternative to the grave accent character[^On some devices, such as an iPhone / iPad, a grave accent character is harder to enter and is often confused with a modifier letter grave accent.].
However, all four characters must be the same.

The literal element supports the default attribute: when given, all spaces in the text are rendered in HTML as open box characters (""''&#x2423;''"", ''U+2423'').
The use of a generic attribute allwos to specify a (programming) language that controls syntax colouring when rendered in HTML.

If you want to specify a grave accent character in the text, either use modifier grave accent characters as delimiters for the element, or put a backslash character before the grave accent character you want to use inside the element.
If you want to enter a backslash character, you need to enter two of these.

Examples:
* ``\`\`abc def\`\``` is rendered in HTML as ::``abc def``::{=example}.
* ``\`\`abc def\`\`{-}`` is rendered in HTML as ::``abc def``{-}::{=example}.





|













|







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
id: 00001007040200
title: Zettelmarkup: Literal-like formatting
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124170441

There are some reasons to mark text that should be rendered as uninterpreted:
* Mark text as literal, sometimes as part of a program.
* Mark text as input you give into a computer via a keyboard.
* Mark text as output from some computer, e.g. shown at the command line.

=== Literal text
Literal text somehow relates to [[verbatim blocks|00001007030500]]: their content should not be interpreted further, but may be rendered special.
It is specified by two grave accent characters (""''`''"", ''U+0060''), followed by the text, followed by again two grave accent characters, optionally followed by an [[attribute|00001007050000]] specification.
Similar to the verbatim block, the literal element allows also a modifier letter grave accent (""''ˋ''"", ''U+02CB'') as an alternative to the grave accent character[^On some devices, such as an iPhone / iPad, a grave accent character is harder to enter and is often confused with a modifier letter grave accent.].
However, all four characters must be the same.

The literal element supports the default attribute: when given, all spaces in the text are rendered in HTML as open box characters (""''&#x2423;''"", ''U+2423'').
The use of a generic attribute allows to specify a ([[programming|00001007050200]]) language that controls syntax coloring when rendered in HTML.

If you want to specify a grave accent character in the text, either use modifier grave accent characters as delimiters for the element, or put a backslash character before the grave accent character you want to use inside the element.
If you want to enter a backslash character, you need to enter two of these.

Examples:
* ``\`\`abc def\`\``` is rendered in HTML as ::``abc def``::{=example}.
* ``\`\`abc def\`\`{-}`` is rendered in HTML as ::``abc def``{-}::{=example}.

Changes to docs/manual/00001007040310.zettel.

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
id: 00001007040310
title: Zettelmarkup: Links
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk


There are two kinds of links, regardless of links to (internal) other zettel or to (external) material.
Both kinds begin with two consecutive left square bracket characters (""''[''"", ''U+005B'') and ends with two consecutive right square bracket characters (""'']''"", ''U+005D'').

The first form provides some text plus the link specification, delimited by a vertical bar character (""''|''"", ''U+007C''): ``[[text|linkspecification]]``.

The second form just provides a link specification between the square brackets.
Its text is derived from the link specification, e.g. by interpreting the link specification as text: ``[[linkspecification]]``.

The link specification for another zettel within the same Zettelstore is just the [[zettel identifier|00001006050000]].
To reference some content within a zettel, you can append a number sign character (""''#''"", ''U+0023'') and the name of the mark to the zettel identifier.
The resulting reference is called ""zettel reference"".

To specify some material outside the Zettelstore, just use an normal Uniform Resource Identifier (URI) as defined by [[RFC\ 3986|https://tools.ietf.org/html/rfc3986]].
If the URL begins with the slash character (""/"", ''U+002F''), or if it begins with ""./"" or with ""../"", i.e. without scheme, user info, and host name, the reference will be treated as a ""local reference"", otherwise as an ""external reference"".
If the URL begins with two slash characters, it will be interpreted relative to the value of [[''url-prefix''|00001004010000#url-prefix]].

The text in the second form is just a sequence of inline elements.





>

















|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id: 00001007040310
title: Zettelmarkup: Links
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124172143

There are two kinds of links, regardless of links to (internal) other zettel or to (external) material.
Both kinds begin with two consecutive left square bracket characters (""''[''"", ''U+005B'') and ends with two consecutive right square bracket characters (""'']''"", ''U+005D'').

The first form provides some text plus the link specification, delimited by a vertical bar character (""''|''"", ''U+007C''): ``[[text|linkspecification]]``.

The second form just provides a link specification between the square brackets.
Its text is derived from the link specification, e.g. by interpreting the link specification as text: ``[[linkspecification]]``.

The link specification for another zettel within the same Zettelstore is just the [[zettel identifier|00001006050000]].
To reference some content within a zettel, you can append a number sign character (""''#''"", ''U+0023'') and the name of the mark to the zettel identifier.
The resulting reference is called ""zettel reference"".

To specify some material outside the Zettelstore, just use an normal Uniform Resource Identifier (URI) as defined by [[RFC\ 3986|https://tools.ietf.org/html/rfc3986]].
If the URL begins with the slash character (""/"", ''U+002F''), or if it begins with ""./"" or with ""../"", i.e. without scheme, user info, and host name, the reference will be treated as a ""local reference"", otherwise as an ""external reference"".
If the URL begins with two slash characters, it will be interpreted relative to the value of [[''url-prefix''|00001004010000#url-prefix]].

The text in the second form is just a sequence of [[inline elements|00001007040000]].

Changes to docs/manual/00001007040320.zettel.

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
id: 00001007040320
title: Zettelmarkup: Inline Embedding / Transclusion
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20210811162201

To some degree, an specification for embedded material is conceptually not too far away from a specification for [[linked material|00001007040310]].
Both contain a reference specification and optionally some text.
In contrast to a link, the specification of embedded material must currently resolve to some kind of real content.
This content replaces the embed specification.

An image specification begins with two consecutive left curly bracket characters (""''{''"", ''U+007B'') and ends with two consecutive right curly bracket characters (""''}''"", ''U+007D'').
The curly brackets delimits either a reference specification or some text, a vertical bar character and the link specification, similar to a link.

One difference to a link: if the text was not given, an empty string is assumed.

The reference must point to some content, either zettel content or URL-referenced content.
If the referenced zettel does not exist, or is not readable, a spinning emoji is presented as a visual hint:
 
Example: ``{{00000000000000}}`` will be rendered as ::{{00000000000000}}::{=example}.

There are two kind of content:
# [[image content|00001007040322]],
# [[textual content|00001007040324]].





|












|






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
id: 00001007040320
title: Zettelmarkup: Inline Embedding / Transclusion
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124183250

To some degree, an specification for embedded material is conceptually not too far away from a specification for [[linked material|00001007040310]].
Both contain a reference specification and optionally some text.
In contrast to a link, the specification of embedded material must currently resolve to some kind of real content.
This content replaces the embed specification.

An image specification begins with two consecutive left curly bracket characters (""''{''"", ''U+007B'') and ends with two consecutive right curly bracket characters (""''}''"", ''U+007D'').
The curly brackets delimits either a reference specification or some text, a vertical bar character and the link specification, similar to a link.

One difference to a link: if the text was not given, an empty string is assumed.

The reference must point to some content, either zettel content or URL-referenced content.
If the referenced zettel does not exist, or is not readable, a [[spinning emoji|00000000040001]] is presented as a visual hint:
 
Example: ``{{00000000000000}}`` will be rendered as ::{{00000000000000}}::{=example}.

There are two kind of content:
# [[image content|00001007040322]],
# [[textual content|00001007040324]].

Changes to docs/manual/00001007040324.zettel.

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
id: 00001007040324
title: Zettelmarkup: Inline Transclusion of Text
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20210811172440

Inline transclusion applies to all zettel that are parsed in an non-trivial way.
For example, textual content is assumed if the [[syntax|00001006020000#syntax]] of a zettel is ''zmk'' ([[Zettelmarkup|00001007000000]]), or ''markdown'' / ''md'' ([[Markdown|00001008010000]]).

Since the transclusion is at the level of [[inline-structured elements|00001007040000]], the transclude specification must be replaced with some inline-structured elements.

First, the referenced zettel is read.
If it contains inline transclusions, these will be expanded, recursively.
When an endless recursion is detected, expansion does not take place.
Instead an error message replaces the transclude specification.

The result of this (indirect) transclusion is searched for inline-structured elements.

* If only the zettel identifier was specified, the first top-level [[paragraph|00001007030000#paragraphs]] is used.
  Since a paragraph is basically a sequence of inline-structured elements, these elements will replace the transclude specification.

  Example: ``{{00010000000000}}`` (see [[00010000000000]]) is rendered as ::{{00010000000000}}::{=example}.

* If a fragment identifier was additionally specified, the element with the given fragment is searched:
** If it specifies a [[heading|00001007030300]], the next top-level paragraph is used.






|













|







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
id: 00001007040324
title: Zettelmarkup: Inline Transclusion of Text
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124175757

Inline transclusion applies to all zettel that are parsed in an non-trivial way.
For example, textual content is assumed if the [[syntax|00001006020000#syntax]] of a zettel is ''zmk'' ([[Zettelmarkup|00001007000000]]), or ''markdown'' / ''md'' ([[Markdown|00001008010000]]).

Since the transclusion is at the level of [[inline-structured elements|00001007040000]], the transclude specification must be replaced with some inline-structured elements.

First, the referenced zettel is read.
If it contains inline transclusions, these will be expanded, recursively.
When an endless recursion is detected, expansion does not take place.
Instead an error message replaces the transclude specification.

The result of this (indirect) transclusion is searched for inline-structured elements.

* If only an [[zettel identifier|00001006050000]] was specified, the first top-level [[paragraph|00001007030000#paragraphs]] is used.
  Since a paragraph is basically a sequence of inline-structured elements, these elements will replace the transclude specification.

  Example: ``{{00010000000000}}`` (see [[00010000000000]]) is rendered as ::{{00010000000000}}::{=example}.

* If a fragment identifier was additionally specified, the element with the given fragment is searched:
** If it specifies a [[heading|00001007030300]], the next top-level paragraph is used.

Changes to docs/manual/00001007040340.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
id: 00001007040340
title: Zettelmarkup: Citation Key
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20210810172339

A citation key references some external material that is part of a bibliografical collection.

Currently, Zettelstore implements this only partially, it is ""work in progress"".

However, the syntax is: beginning with a left square bracket and followed by an at sign character (""''@''"", ''U+0040''), a the citation key is given.
The key is typically a sequence of letters and digits.
If a comma character (""'',''"", ''U+002C'') or a vertical bar character is given, the following is interpreted as inline elements.
A right square bracket ends the text and the citation key element.





|







|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
id: 00001007040340
title: Zettelmarkup: Citation Key
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124172111

A citation key references some external material that is part of a bibliografical collection.

Currently, Zettelstore implements this only partially, it is ""work in progress"".

However, the syntax is: beginning with a left square bracket and followed by an at sign character (""''@''"", ''U+0040''), a the citation key is given.
The key is typically a sequence of letters and digits.
If a comma character (""'',''"", ''U+002C'') or a vertical bar character is given, the following is interpreted as [[inline elements|00001007040000]].
A right square bracket ends the text and the citation key element.

Changes to docs/manual/00001007050000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001007050000
title: Zettelmarkup: Attributes
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211103162617

Attributes allows to modify the way how material is presented.
Alternatively, they provide additional information to markup elements.
To some degree, attributes are similar to [[HTML attributes|https://html.spec.whatwg.org/multipage/dom.html#global-attributes]].

Typical use cases for attributes are to specify the (natural) [[language|00001007050100]] for a text region, to specify the [[programming language|00001007050200]] for highlighting program code, or to make white space visible in plain text.






|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001007050000
title: Zettelmarkup: Attributes
role: manual
tags: #manual #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124174947

Attributes allows to modify the way how material is presented.
Alternatively, they provide additional information to markup elements.
To some degree, attributes are similar to [[HTML attributes|https://html.spec.whatwg.org/multipage/dom.html#global-attributes]].

Typical use cases for attributes are to specify the (natural) [[language|00001007050100]] for a text region, to specify the [[programming language|00001007050200]] for highlighting program code, or to make white space visible in plain text.

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
The key ""''-''"" (just hyphen-minus) is special.
It is called __default attribute__ and has a markup specific meaning.
For example, when used for plain text, it replaces the non-visible space with a visible representation:

* ++``Hello, world``{-}++ produces ==Hello, world=={-}.
* ++``Hello, world``++ produces ==Hello, world==.

For some block elements, there is a syntax variant if you only want to specify a generic attribute.
For all line-range blocks you can specify the generic attributes directly in the first line, after the three (or more) block characters.

```
:::attr
...
:::
```







|







50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
The key ""''-''"" (just hyphen-minus) is special.
It is called __default attribute__ and has a markup specific meaning.
For example, when used for plain text, it replaces the non-visible space with a visible representation:

* ++``Hello, world``{-}++ produces ==Hello, world=={-}.
* ++``Hello, world``++ produces ==Hello, world==.

For some [[block-structured elements|00001007030000]], there is a syntax variant if you only want to specify a generic attribute.
For all line-range blocks you can specify the generic attributes directly in the first line, after the three (or more) block characters.

```
:::attr
...
:::
```
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
```
is not allowed. Same for
```
=== Heading {style=color:"
green"}
```.

For inlines, the attributes must immediately follow the inline markup.
However, the attributes may be continued on the next line when a space or line ending character is possible.

``::GREEN::{style=color:green}`` is allowed, but not ``::GREEN:: {style=color:green}``.

```
::GREEN::{style=color:green
background=grey}







|







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
```
is not allowed. Same for
```
=== Heading {style=color:"
green"}
```.

For [[inline-structued elements|00001007040000]], the attributes must immediately follow the inline markup.
However, the attributes may be continued on the next line when a space or line ending character is possible.

``::GREEN::{style=color:green}`` is allowed, but not ``::GREEN:: {style=color:green}``.

```
::GREEN::{style=color:green
background=grey}

Changes to docs/manual/00001007060000.zettel.

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
id: 00001007060000
title: Zettelmarkup: Summary of Formatting Characters
role: manual
tags: #manual #reference #zettelmarkup #zettelstore
syntax: zmk
modified: 20211103173317

The following table gives an overview about the use of all characters that begin a markup element.

|= Character :|= Blocks <|= Inlines <
| ''!''  | (free) | (free)
| ''"''  | [[Verse block|00001007030700]] | [[Typographic quotation mark|00001007040100]]
| ''#''  | [[Ordered list|00001007030200]] | [[Tag|00001007040000]]
| ''$''  | (reserved) | (reserved)
| ''%''  | [[Comment block|00001007030900]] | [[Comment|00001007040000]]
| ''&''  | (free) | [[Entity|00001007040000]]
| ''\'''  | (free)  | [[Monospace text|00001007040100]]
| ''(''  | (free) | (free)
| '')''  | (free) | (free)
| ''*''  | [[Unordered list|00001007030200]] | [[strongly emphasized text|00001007040100]]
| ''+''  | (free) | [[Keyboard input|00001007040200]]
| '',''  | (free) | [[Subscripted text|00001007040100]]
| ''-''  | [[Horizonal rule|00001007030400]] | ""[[en-dash|00001007040000]]""
| ''.''  | (free) | [[Horizontal ellipsis|00001007040000]]
| ''/''  | (free) | [[//Emphasized text//|00001007040100]] (deprecated for v0.0.17)
| '':''  | [[Region block|00001007030800]] / [[description text|00001007030100]] | [[Inline region|00001007040100]]
| '';''  | [[Description term|00001007030100]] | [[Small text|00001007040100]]
| ''<''  | [[Quotation block|00001007030600]] | [[Short inline quote|00001007040100]]
| ''=''  | [[Headings|00001007030300]] | [[Computer output|00001007040200]]
| ''>''  | [[Quotation lists|00001007030200]] | [[Inserted text|00001007040100]]
| ''?''  | (free) | (free)
| ''@''  | (free) | (reserved)
| ''[''  | (reserved)  | [[Linked material|00001007040300]], [[citation key|00001007040300]], [[footnote|00001007040300]], [[mark|00001007040300]]
| ''\\'' | (blocked by inline meaning) | [[Escape character|00001007040000]]
| '']''  | (reserved) | End of link, citation key, footnote, mark
| ''^''  | (free) | [[Superscripted text|00001007040100]]
| ''_''  | (free) | [[Emphasized text|00001007040100]]
| ''`''  | [[Verbatim block|00001007030500]] | [[Literal text|00001007040200]]
| ''{''  | (reserved) | [[Embedded material|00001007040300]], [[Attribute|00001007050000]]
| ''|''  | [[Table row / table cell|00001007031000]] | Separator within link and embed formatting
| ''}''  | (reserved) | End of embedded material, End of Attribute
| ''~''  | (free) | [[Deleted text|00001007040100]]





|


















|









|




|


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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
id: 00001007060000
title: Zettelmarkup: Summary of Formatting Characters
role: manual
tags: #manual #reference #zettelmarkup #zettelstore
syntax: zmk
modified: 20211124171238

The following table gives an overview about the use of all characters that begin a markup element.

|= Character :|= Blocks <|= Inlines <
| ''!''  | (free) | (free)
| ''"''  | [[Verse block|00001007030700]] | [[Typographic quotation mark|00001007040100]]
| ''#''  | [[Ordered list|00001007030200]] | [[Tag|00001007040000]]
| ''$''  | (reserved) | (reserved)
| ''%''  | [[Comment block|00001007030900]] | [[Comment|00001007040000]]
| ''&''  | (free) | [[Entity|00001007040000]]
| ''\'''  | (free)  | [[Monospace text|00001007040100]]
| ''(''  | (free) | (free)
| '')''  | (free) | (free)
| ''*''  | [[Unordered list|00001007030200]] | [[strongly emphasized text|00001007040100]]
| ''+''  | (free) | [[Keyboard input|00001007040200]]
| '',''  | (free) | [[Subscripted text|00001007040100]]
| ''-''  | [[Horizonal rule|00001007030400]] | ""[[en-dash|00001007040000]]""
| ''.''  | (free) | [[Horizontal ellipsis|00001007040000]]
| ''/''  | (free) | (free)
| '':''  | [[Region block|00001007030800]] / [[description text|00001007030100]] | [[Inline region|00001007040100]]
| '';''  | [[Description term|00001007030100]] | [[Small text|00001007040100]]
| ''<''  | [[Quotation block|00001007030600]] | [[Short inline quote|00001007040100]]
| ''=''  | [[Headings|00001007030300]] | [[Computer output|00001007040200]]
| ''>''  | [[Quotation lists|00001007030200]] | [[Inserted text|00001007040100]]
| ''?''  | (free) | (free)
| ''@''  | (free) | (reserved)
| ''[''  | (reserved)  | [[Linked material|00001007040300]], [[citation key|00001007040300]], [[footnote|00001007040300]], [[mark|00001007040300]]
| ''\\'' | (blocked by inline meaning) | [[Escape character|00001007040000]]
| '']''  | (reserved) | End of [[link|00001007040300]], [[citation key|00001007040300]], [[footnote|00001007040300]], [[mark|00001007040300]]
| ''^''  | (free) | [[Superscripted text|00001007040100]]
| ''_''  | (free) | [[Emphasized text|00001007040100]]
| ''`''  | [[Verbatim block|00001007030500]] | [[Literal text|00001007040200]]
| ''{''  | (reserved) | [[Embedded material|00001007040300]], [[Attribute|00001007050000]]
| ''|''  | [[Table row / table cell|00001007031000]] | Separator within link and [[embed|00001007040320]] formatting
| ''}''  | (reserved) | End of embedded material, End of Attribute
| ''~''  | (free) | [[Deleted text|00001007040100]]

Changes to docs/manual/00001008000000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001008000000
title: Other Markup Languages
role: manual
tags: #manual #zettelstore
syntax: zmk
modified: 20210705111758

[[Zettelmarkup|00001007000000]] is not the only markup language you can use to define your content.
Zettelstore is quite agnostic with respect to markup languages.
Of course, Zettelmarkup plays an important role.
However, with the exception of zettel titles, you can use any (markup) language that is supported:

* CSS





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001008000000
title: Other Markup Languages
role: manual
tags: #manual #zettelstore
syntax: zmk
modified: 20211124193725

[[Zettelmarkup|00001007000000]] is not the only markup language you can use to define your content.
Zettelstore is quite agnostic with respect to markup languages.
Of course, Zettelmarkup plays an important role.
However, with the exception of zettel titles, you can use any (markup) language that is supported:

* CSS
45
46
47
48
49
50
51
52


53
; [!svg]''svg''
: A [[Scalable Vector Graphics|https://www.w3.org/TR/SVG2/]].
  The icon for external material is an example.
; [!text]''text'', [!plain]''plain'', [!txt]''txt''
: Just plain text that must not be interpreted further.
; [!zmk]''zmk''
: [[Zettelmarkup|00001007000000]].



If you specify something else, your content will be interpreted as plain text.








>
>

45
46
47
48
49
50
51
52
53
54
55
; [!svg]''svg''
: A [[Scalable Vector Graphics|https://www.w3.org/TR/SVG2/]].
  The icon for external material is an example.
; [!text]''text'', [!plain]''plain'', [!txt]''txt''
: Just plain text that must not be interpreted further.
; [!zmk]''zmk''
: [[Zettelmarkup|00001007000000]].

The actual values are also listed in a zettel named [[Zettelstore Supported Parser|00000000000092]].

If you specify something else, your content will be interpreted as plain text.

Changes to docs/manual/00001008010000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001008010000
title: Use Markdown within Zettelstore
role: manual
tags: #manual #markdown #zettelstore
syntax: zmk
modified: 20210726191610

If you are customized to use Markdown as your markup language, you can configure Zettelstore to support your decision.
Zettelstore supports [[CommonMark|https://commonmark.org/]], an [[attempt|https://xkcd.com/927/]] to unify all the different, divergent dialects of Markdown.

=== Use Markdown as the default markup language of Zettelstore

Add the key ''default-syntax'' with a value of ''md'' or ''markdown'' to the [[configuration zettel|00000000000100]].





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001008010000
title: Use Markdown within Zettelstore
role: manual
tags: #manual #markdown #zettelstore
syntax: zmk
modified: 20211124134155

If you are customized to use Markdown as your markup language, you can configure Zettelstore to support your decision.
Zettelstore supports [[CommonMark|https://commonmark.org/]], an [[attempt|https://xkcd.com/927/]] to unify all the different, divergent dialects of Markdown.

=== Use Markdown as the default markup language of Zettelstore

Add the key ''default-syntax'' with a value of ''md'' or ''markdown'' to the [[configuration zettel|00000000000100]].
40
41
42
43
44
45
46
47
When a zettel is displayed, JavaScript code might be executed, sometimes with harmful results.

Zettelstore mitigates this problem by ignoring suspicious text when it encodes a zettel as HTML.
Any HTML text that might contain the ``<script>`` tag or the ``<iframe>`` tag is ignored.
This may lead to unexpected results if you depend on these.
Other [[encodings|00001012920500]] may still contain the full HTML text.

Any external client of Zettelstore, which does not use Zettelstore's HTML encoding, must be programmed to take care of malicious code.







|
40
41
42
43
44
45
46
47
When a zettel is displayed, JavaScript code might be executed, sometimes with harmful results.

Zettelstore mitigates this problem by ignoring suspicious text when it encodes a zettel as HTML.
Any HTML text that might contain the ``<script>`` tag or the ``<iframe>`` tag is ignored.
This may lead to unexpected results if you depend on these.
Other [[encodings|00001012920500]] may still contain the full HTML text.

Any external client of Zettelstore, which does not use Zettelstore's [[HTML encoding|00001012920510]], must be programmed to take care of malicious code.

Changes to docs/manual/00001010000000.zettel.

1
2
3
4
5

6
7
8
9
10
11
12
id: 00001010000000
title: Security
role: manual
tags: #configuration #manual #security #zettelstore
syntax: zmk


Your zettel could contain sensitive content.
You probably want to ensure that only authorized person can read and/or modify them.
Zettelstore ensures this in various ways.

=== Local first
The Zettelstore is designed to run on your local computer.





>







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001010000000
title: Security
role: manual
tags: #configuration #manual #security #zettelstore
syntax: zmk
modified: 20211124140614

Your zettel could contain sensitive content.
You probably want to ensure that only authorized person can read and/or modify them.
Zettelstore ensures this in various ways.

=== Local first
The Zettelstore is designed to run on your local computer.
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Once you have enabled authentication, it is possible to allow others to access your Zettelstore.
Maybe, others should be able to read some or all of your zettel.
Or you want to allow them to create new zettel, or to change them.
It is up to you.

If someone is authenticated as the owner of the Zettelstore (hopefully you), no restrictions apply.
But as an owner, you can create ""user zettel"" to allow others to access your Zettelstore in various ways.
Even if you do not want to share your Zettelstore with other persons, creating user zettel can be useful if you plan to access your Zettelstore via the API.

Additionally, you can specify that a zettel is publicily visible.
In this case no one has to authenticate itself to see the content of the zettel.
Or you can specify that a zettel is visible only to the owner.
In this case, no authenticated user will be able to read and change that protected zettel.

* [[Visibility rules for zettel|00001010070200]]







|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Once you have enabled authentication, it is possible to allow others to access your Zettelstore.
Maybe, others should be able to read some or all of your zettel.
Or you want to allow them to create new zettel, or to change them.
It is up to you.

If someone is authenticated as the owner of the Zettelstore (hopefully you), no restrictions apply.
But as an owner, you can create ""user zettel"" to allow others to access your Zettelstore in various ways.
Even if you do not want to share your Zettelstore with other persons, creating user zettel can be useful if you plan to access your Zettelstore via the [[API|00001012000000]].

Additionally, you can specify that a zettel is publicily visible.
In this case no one has to authenticate itself to see the content of the zettel.
Or you can specify that a zettel is visible only to the owner.
In this case, no authenticated user will be able to read and change that protected zettel.

* [[Visibility rules for zettel|00001010070200]]

Changes to docs/manual/00001010040200.zettel.

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
28
29
30
id: 00001010040200
title: Creating an user zettel

tags: #authentication #configuration #manual #security #zettelstore
syntax: zmk
role: manual

All data to be used for authenticating a user is store in a special zettel called ""user zettel"". 
A user zettel must have set the following three metadata fields:

; ''user-id'' (""user identification"")
: The unique identification to be specified for authentication.
; ''credential''
: A hashed password as generated by the [[``zettelstore password``{=sh}|00001004051400]] command.
; ''role''
: Must contain the value ""user"".

The title of the zettel typically specifies the real name of the user.






A user zettel can only be created by the owner of the Zettelstore.

The owner should execute the following steps to create a new user zettel:

# Create a new zettel with the role ""user"".
# Save the zettel to get a [[identifier|00001006050000]] for this zettel.
# Choose a unique identification for the user.
#* If the identifier is not unique, authentication will not work for this user.
# Execute the [[``zettelstore password``|00001004051400]] command.
#* You have to specify the user identification and the zettel identifier
#* If you should not know the password of the new user, send her/him the user identification and the user zettel identifier, so that the person can create the hashed password herself.
# Edit the user zettel and add the hashed password under the meta key ''credential'' and the user identification under the key ''user-id''.


>


|


|





<
<



>
>
>
>
>




|







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
28
29
30
31
32
33
34
id: 00001010040200
title: Creating an user zettel
role: manual
tags: #authentication #configuration #manual #security #zettelstore
syntax: zmk
modified: 20211127174207

All data to be used for authenticating a user is store in a special zettel called ""user zettel"". 
A user zettel must have set the following two metadata fields:

; ''user-id'' (""user identification"")
: The unique identification to be specified for authentication.
; ''credential''
: A hashed password as generated by the [[``zettelstore password``{=sh}|00001004051400]] command.



The title of the zettel typically specifies the real name of the user.

The following metadata elements are optional:

; ''user-role''
: Associate the user with some basic privileges, e.g. a [[user role|00001010070300]]

A user zettel can only be created by the owner of the Zettelstore.

The owner should execute the following steps to create a new user zettel:

# Create a new zettel.
# Save the zettel to get a [[identifier|00001006050000]] for this zettel.
# Choose a unique identification for the user.
#* If the identifier is not unique, authentication will not work for this user.
# Execute the [[``zettelstore password``|00001004051400]] command.
#* You have to specify the user identification and the zettel identifier
#* If you should not know the password of the new user, send her/him the user identification and the user zettel identifier, so that the person can create the hashed password herself.
# Edit the user zettel and add the hashed password under the meta key ''credential'' and the user identification under the key ''user-id''.

Changes to docs/manual/00001010040400.zettel.

1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
id: 00001010040400
title: Authentication process

tags: #authentication #configuration #manual #security #zettelstore
syntax: zmk
role: manual

When someone tries to authenticate itself with an user identifier / ""user name"" and a password, the following process is executed:

# If meta key ''owner'' of the configuration zettel does not have a valid [[zettel identifier|00001006050000]] as value, authentication fails.
# Retrieve all zettel, where the meta key ''user-id'' has the same value as the given user identification. If the list is empty, authentication fails.
# From above list, the zettel with the numerically smallest identifier is selected.
  Or in other words: the oldest zettel is selected[^This is done to prevent an attacker from creating a new note with the same user identification].
# If the zettel does not have role ''user'', authentication fails.
# If the zettel does not have a value for the meta key ''credential'', authentication fails.
# The value of the meta key ''credential'' is compared with the given password.
  If they do not match, authentication fails.
The authentication is successful, because the Zettelstore has an owner, the identifier matches a user zettel, and the password conforms to the stored credential.


>


|







<



|
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
id: 00001010040400
title: Authentication process
role: manual
tags: #authentication #configuration #manual #security #zettelstore
syntax: zmk
modified: 20211127174943

When someone tries to authenticate itself with an user identifier / ""user name"" and a password, the following process is executed:

# If meta key ''owner'' of the configuration zettel does not have a valid [[zettel identifier|00001006050000]] as value, authentication fails.
# Retrieve all zettel, where the meta key ''user-id'' has the same value as the given user identification. If the list is empty, authentication fails.
# From above list, the zettel with the numerically smallest identifier is selected.
  Or in other words: the oldest zettel is selected[^This is done to prevent an attacker from creating a new note with the same user identification].

# If the zettel does not have a value for the meta key ''credential'', authentication fails.
# The value of the meta key ''credential'' is compared with the given password.
  If they do not match, authentication fails.
The authentication is successful, because the Zettelstore has an owner, the identifier matches an [[user zettel|00001010040200]], and the password conforms to the stored credential.

Changes to docs/manual/00001010040700.zettel.

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
id: 00001010040700
title: Access token
role: manual
tags: #authentication #configuration #manual #security #zettelstore
syntax: zmk


If an user is authenticated, an ""access token"" is created that must be sent with every request to prove the identity of the caller.
Otherwise the user will not be recognized by Zettelstore.

If the user was authenticated via the web interface, the access token is stored in a [[""session cookie""|https://en.wikipedia.org/wiki/HTTP_cookie#Session_cookie]].
When the web browser is closed, theses cookies are not saved.
If you want web browser to store the cookie as long as lifetime of that token, the owner must set ''persistent-cookie'' of the [[startup configuration|00001004010000]] to ''true''.

If the web browser remains inactive for a period, the user will be automatically logged off, because each access token has a limited lifetime.
The maximum length of this period is specified by the ''token-lifetime-html'' value of the startup configuration.
Every time a web page is displayed, a fresh token is created and stored inside the cookie.






>




|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
id: 00001010040700
title: Access token
role: manual
tags: #authentication #configuration #manual #security #zettelstore
syntax: zmk
modified: 20211124131831

If an user is authenticated, an ""access token"" is created that must be sent with every request to prove the identity of the caller.
Otherwise the user will not be recognized by Zettelstore.

If the user was authenticated via the [[web user interface|00001014000000]], the access token is stored in a [[""session cookie""|https://en.wikipedia.org/wiki/HTTP_cookie#Session_cookie]].
When the web browser is closed, theses cookies are not saved.
If you want web browser to store the cookie as long as lifetime of that token, the owner must set ''persistent-cookie'' of the [[startup configuration|00001004010000]] to ''true''.

If the web browser remains inactive for a period, the user will be automatically logged off, because each access token has a limited lifetime.
The maximum length of this period is specified by the ''token-lifetime-html'' value of the startup configuration.
Every time a web page is displayed, a fresh token is created and stored inside the cookie.

Changes to docs/manual/00001010070200.zettel.

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
28
29
30
31
32
33
34
35
36
37
38
39









id: 00001010070200
title: Visibility rules for zettel
role: manual
tags: #authorization #configuration #manual #security #zettelstore
syntax: zmk
modified: 20210728162201

For every zettel you can specify under which condition the zettel is visible to others.
This is controlled with the metadata key [[''visibility''|00001006020000#visibility]].
The following values are supported:

; [!public]""public""
: The zettel is visible to everybody, even if the user is not authenticated.
; [!login]""login""
: Only an authenticated user can access the zettel.

  This is the default value for [[''default-visibility''|00001004020000#default-visibility]].


; [!owner]""owner""
: Only the owner of the Zettelstore can access the zettel.

  This is for zettel with sensitive content, e.g. the [[configuration zettel|00001004020000]] or the various zettel that contains the templates for rendering zettel in HTML.
; [!expert]""expert""
: Only the owner of the Zettelstore can access the zettel, if runtime configuration [[''expert-mode''|00001004020000#expert-mode]] is set to a boolean true value.

  This is for zettel with sensitive content that might irritate the owner.
  Computed zettel with internal runtime information are examples for such a zettel.

When you install a Zettelstore, only some zettel have visibility ""public"".
One is the zettel that contains CSS for displaying the web interface.
This is to ensure that the web interface looks nice even for not authenticated users.
Another is the zettel containing the [[version|00000000000001]] of the Zettelstore.
Yet other zettel lists the Zettelstore [[license|00000000000004]], its [[contributors|00000000000005]], and external, licensed [[dependencies|00000000000006]], such as program code written by others or graphics designed by others.
The [[default image|00000000040001]], used if an image reference is invalid, is also public visible.

Please note: if authentication is not enabled, every user has the same rights as the owner of a Zettelstore.
This is also true, if the Zettelstore runs additionally in [[read-only mode|00001004010000#read-only-mode]].
In this case, the [[runtime configuration zettel|00001004020000]] is shown (its visibility is ""owner"").
The [[startup configuration|00001004010000]] is not shown, because the associated computed zettel with identifier ''00000000000096'' is stored with the visibility ""expert"".
If you want to show such a zettel, you must set ''expert-mode'' to true.














|











>
>










|
|

|
<


|




>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
id: 00001010070200
title: Visibility rules for zettel
role: manual
tags: #authorization #configuration #manual #security #zettelstore
syntax: zmk
modified: 20211126185655

For every zettel you can specify under which condition the zettel is visible to others.
This is controlled with the metadata key [[''visibility''|00001006020000#visibility]].
The following values are supported:

; [!public]""public""
: The zettel is visible to everybody, even if the user is not authenticated.
; [!login]""login""
: Only an authenticated user can access the zettel.

  This is the default value for [[''default-visibility''|00001004020000#default-visibility]].
; [!creator]""creator""
: Only an authenticated user that is allowed to create new zettel can access the zettel.
; [!owner]""owner""
: Only the owner of the Zettelstore can access the zettel.

  This is for zettel with sensitive content, e.g. the [[configuration zettel|00001004020000]] or the various zettel that contains the templates for rendering zettel in HTML.
; [!expert]""expert""
: Only the owner of the Zettelstore can access the zettel, if runtime configuration [[''expert-mode''|00001004020000#expert-mode]] is set to a boolean true value.

  This is for zettel with sensitive content that might irritate the owner.
  Computed zettel with internal runtime information are examples for such a zettel.

When you install a Zettelstore, only [[some zettel|//h?visibility=public]] have visibility ""public"".
One is the zettel that contains [[CSS|00000000020001]] for displaying the [[web user interface|00001014000000]].
This is to ensure that the web interface looks nice even for not authenticated users.
Another is the zettel containing the Zettelstore [[license|00000000000004]].

The [[default image|00000000040001]], used if an image reference is invalid, is also public visible.

Please note: if [[authentication is not enabled|00001010040100]], every user has the same rights as the owner of a Zettelstore.
This is also true, if the Zettelstore runs additionally in [[read-only mode|00001004010000#read-only-mode]].
In this case, the [[runtime configuration zettel|00001004020000]] is shown (its visibility is ""owner"").
The [[startup configuration|00001004010000]] is not shown, because the associated computed zettel with identifier ''00000000000096'' is stored with the visibility ""expert"".
If you want to show such a zettel, you must set ''expert-mode'' to true.

=== Examples
Similar to the [[API|00001012051810]], you can easily create a zettel list based on the ''visibility'' metadata key:

| public  | [[//h?visibility=public]]
| login   | [[//h?visibility=login]]
| creator | [[//h?visibility=creator]]
| owner   | [[//h?visibility=owner]]
| expert  | [[//h?visibility=expert]][^Only if [[''expert-mode''|00001004020000#expert-mode]] is enabled, this list will show some zettel.]

Changes to docs/manual/00001010070300.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
id: 00001010070300
title: User roles
role: manual
tags: #authorization #configuration #manual #security #zettelstore
syntax: zmk
modified: 20210702165501

Every user is associated with some basic privileges.
These are specified in the user zettel with the key ''user-role''.
The following values are supported:

; [!reader]""reader""
: The user is allowed to read zettel.
  This is the default value for any user except the owner of the Zettelstore.
; [!writer]""writer""
: The user is allowed to create new zettel and to change existing zettel.





|


|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
id: 00001010070300
title: User roles
role: manual
tags: #authorization #configuration #manual #security #zettelstore
syntax: zmk
modified: 20211124141938

Every user is associated with some basic privileges.
These are specified in the [[user zettel|00001010040200]] with the key ''user-role''.
The following values are supported:

; [!reader]""reader""
: The user is allowed to read zettel.
  This is the default value for any user except the owner of the Zettelstore.
; [!writer]""writer""
: The user is allowed to create new zettel and to change existing zettel.

Changes to docs/manual/00001010070600.zettel.

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
id: 00001010070600
title: Access rules
role: manual
tags: #authorization #configuration #manual #security #zettelstore
syntax: zmk
modified: 20210702165416

Whether an operation of the Zettelstore is allowed or rejected, depends on various factors.

The following rules are checked first, in this order:

# In read-only mode, every operation except the ""Read"" operation is rejected.
# If there is no owner, authentication is disabled and every operation is allowed for everybody.
# If the user is authenticated and it is the owner, then the operation is allowed.

In the second step, when authentication is enabled and the requesting user is not the owner, everything depends on the requested operation.

* Read a zettel:
** If the visibility is ""public"", the access is granted.
** If the visibility is ""owner"", the access is rejected.
** If the user is not authenticated, access is rejected.
** If the zettel requested is an user zettel, reject the access if the users identification is not the same as of the ''ident'' meta key in the zettel.

   In other words: only the requesting user is allowed to access its own user zettel.
** If the ''user-role'' of the user is ""creator"", reject the access.
** Otherwise the user is authenticated, no sensitive zettel is requested.
   Allow to read the zettel.
* Create a new zettel
** If the user is not authenticated, reject the access.
** If the ''user-role'' of the user is ""reader"", reject the access.
** If the user tries to create an user zettel, the access is rejected.

   Only the owner is allowed to create user zettel.
** In all other cases allow to create the zettel.
* Change an existing zettel
** If the user is not allowed to read the zettel (see above), reject the access.
** If the user is not authenticated, reject the access.
** If the zettel is the user zettel of the authenticated user, proceed as follows:
*** If some sensitive meta values are changed (e.g. user identifier, zettel role, user role, but not hashed password), reject the access
*** Since the user just updates some uncritical values, grant the access
   In other words: a user is allowed to change its user zettel, even if s/he has no writer privilege and if only uncritical data is changed.
** If the ''user-role'' of the user is ""reader"", reject the access.
** If the user is not allowed to create a new zettel, reject the access.
** Otherwise grant the access.
* Rename a zettel
** Reject the access.
   Only the owner of the Zettelstore is currently allowed to give a new identifier for a zettel.
* Delete a zettel
** Reject the access.
   Only the owner of the Zettelstore is allowed to delete a zettel.
   This may change in the future.





|









|





|








|

|




|













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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
id: 00001010070600
title: Access rules
role: manual
tags: #authorization #configuration #manual #security #zettelstore
syntax: zmk
modified: 20211124142456

Whether an operation of the Zettelstore is allowed or rejected, depends on various factors.

The following rules are checked first, in this order:

# In read-only mode, every operation except the ""Read"" operation is rejected.
# If there is no owner, authentication is disabled and every operation is allowed for everybody.
# If the user is authenticated and it is the owner, then the operation is allowed.

In the second step, when [[authentication is enabled|00001010040100]] and the requesting user is not the owner, everything depends on the requested operation.

* Read a zettel:
** If the visibility is ""public"", the access is granted.
** If the visibility is ""owner"", the access is rejected.
** If the user is not authenticated, access is rejected.
** If the zettel requested is an [[user zettel|00001010040200]], reject the access if the users identification is not the same as of the ''user-id'' metadata value in the zettel.

   In other words: only the requesting user is allowed to access its own user zettel.
** If the ''user-role'' of the user is ""creator"", reject the access.
** Otherwise the user is authenticated, no sensitive zettel is requested.
   Allow to read the zettel.
* Create a new zettel
** If the user is not authenticated, reject the access.
** If the ''user-role'' of the user is ""reader"", reject the access.
** If the user tries to create an [[user zettel|00001010040200]], the access is rejected.

   Only the owner of the Zettelstore is allowed to create user zettel.
** In all other cases allow to create the zettel.
* Change an existing zettel
** If the user is not allowed to read the zettel (see above), reject the access.
** If the user is not authenticated, reject the access.
** If the zettel is the [[user zettel|00001010040200]] of the authenticated user, proceed as follows:
*** If some sensitive meta values are changed (e.g. user identifier, zettel role, user role, but not hashed password), reject the access
*** Since the user just updates some uncritical values, grant the access
   In other words: a user is allowed to change its user zettel, even if s/he has no writer privilege and if only uncritical data is changed.
** If the ''user-role'' of the user is ""reader"", reject the access.
** If the user is not allowed to create a new zettel, reject the access.
** Otherwise grant the access.
* Rename a zettel
** Reject the access.
   Only the owner of the Zettelstore is currently allowed to give a new identifier for a zettel.
* Delete a zettel
** Reject the access.
   Only the owner of the Zettelstore is allowed to delete a zettel.
   This may change in the future.

Changes to docs/manual/00001012000000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001012000000
title: API
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211004111103

The API (short for ""**A**pplication **P**rogramming **I**nterface"") is the primary way to communicate with a running Zettelstore.
Most integration with other systems and services is done through the API.
The [[web user interface|00001014000000]] is just an alternative, secondary way of interacting with a Zettelstore.

=== Background
The API is HTTP-based and uses plain text and JSON as its main encoding format for exchanging messages between a Zettelstore and its client software.





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001012000000
title: API
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211119134156

The API (short for ""**A**pplication **P**rogramming **I**nterface"") is the primary way to communicate with a running Zettelstore.
Most integration with other systems and services is done through the API.
The [[web user interface|00001014000000]] is just an alternative, secondary way of interacting with a Zettelstore.

=== Background
The API is HTTP-based and uses plain text and JSON as its main encoding format for exchanging messages between a Zettelstore and its client software.
34
35
36
37
38
39
40

41
42
43
44
45
46
47
* [[Create a new zettel|00001012053200]]
* [[Retrieve metadata and content of an existing zettel|00001012053300]]
* [[Retrieve metadata of an existing zettel|00001012053400]]
* [[Retrieve evaluated metadata and content of an existing zettel in various encodings|00001012053500]]
* [[Retrieve parsed metadata and content of an existing zettel in various encodings|00001012053600]]
* [[Retrieve references of an existing zettel|00001012053700]]
* [[Retrieve context of an existing zettel|00001012053800]]

* [[Retrieve zettel order within an existing zettel|00001012054000]]
* [[Update metadata and content of a zettel|00001012054200]]
* [[Rename a zettel|00001012054400]]
* [[Delete a zettel|00001012054600]]

=== Various helper method
* [[Encode Zettelmarkup inline material as HTML/Text|00001012070500]]







>







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
* [[Create a new zettel|00001012053200]]
* [[Retrieve metadata and content of an existing zettel|00001012053300]]
* [[Retrieve metadata of an existing zettel|00001012053400]]
* [[Retrieve evaluated metadata and content of an existing zettel in various encodings|00001012053500]]
* [[Retrieve parsed metadata and content of an existing zettel in various encodings|00001012053600]]
* [[Retrieve references of an existing zettel|00001012053700]]
* [[Retrieve context of an existing zettel|00001012053800]]
* [[Retrieve unlinked references to an existing zettel|00001012053900]]
* [[Retrieve zettel order within an existing zettel|00001012054000]]
* [[Update metadata and content of a zettel|00001012054200]]
* [[Rename a zettel|00001012054400]]
* [[Delete a zettel|00001012054600]]

=== Various helper method
* [[Encode Zettelmarkup inline material as HTML/Text|00001012070500]]

Changes to docs/manual/00001012051200.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id: 00001012051200
title: API: List metadata of all zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211004124401

To list the metadata of all zettel just send a HTTP GET request to the [[endpoint|00001012920000]] ''/j''[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header].
If successful, the output is a JSON object:

```sh
# curl http://127.0.0.1:23123/j
{"query":"","list":[{"id":"00001012051200","meta":{"title":"API: Renew an access token","tags":"#api #manual #zettelstore","syntax":"zmk","role":"manual"}},{"id":"00001012050600","meta":{"title":"API: Provide an access token","tags":"#api #manual #zettelstore","syntax":"zmk","role":"manual"}},{"id":"00001012050400","meta":{"title":"API: Renew an access token","tags":"#api #manual #zettelstore","syntax":"zmk","role":"manual"}},{"id":"00001012050200","meta":{"title":"API: Authenticate a client","tags":"#api #manual #zettelstore","syntax":"zmk","role":"manual"}},{"id":"00001012000000","meta":{"title":"API","tags":"#api #manual #zettelstore","syntax":"zmk","role":"manual"}}]}
```

The JSON object contains a key ''"list"'' where its value is a list of zettel JSON objects.
These zettel JSON objects themselves contains the keys ''"id"'' (value is a string containing the zettel identifier), , and ''"meta"'' (value as a JSON object).
The value of key ''"meta"'' effectively contains all metadata of the identified zettel, where metadata keys are encoded as JSON object keys and metadata values encoded as JSON strings.

Additionally, the JSON object contains a key ''"query"'' with a string value.
It will contain a textual description of the underlying query if you [[select only some zettel|00001012051810]].
Without a selection, the value is the empty string.

If you reformat the JSON output from the ''GET /j'' call, you'll see its structure better:





|










|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id: 00001012051200
title: API: List metadata of all zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124175849

To list the metadata of all zettel just send a HTTP GET request to the [[endpoint|00001012920000]] ''/j''[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header].
If successful, the output is a JSON object:

```sh
# curl http://127.0.0.1:23123/j
{"query":"","list":[{"id":"00001012051200","meta":{"title":"API: Renew an access token","tags":"#api #manual #zettelstore","syntax":"zmk","role":"manual"}},{"id":"00001012050600","meta":{"title":"API: Provide an access token","tags":"#api #manual #zettelstore","syntax":"zmk","role":"manual"}},{"id":"00001012050400","meta":{"title":"API: Renew an access token","tags":"#api #manual #zettelstore","syntax":"zmk","role":"manual"}},{"id":"00001012050200","meta":{"title":"API: Authenticate a client","tags":"#api #manual #zettelstore","syntax":"zmk","role":"manual"}},{"id":"00001012000000","meta":{"title":"API","tags":"#api #manual #zettelstore","syntax":"zmk","role":"manual"}}]}
```

The JSON object contains a key ''"list"'' where its value is a list of zettel JSON objects.
These zettel JSON objects themselves contains the keys ''"id"'' (value is a string containing the [[zettel identifier|00001006050000]]), and ''"meta"'' (value as a JSON object).
The value of key ''"meta"'' effectively contains all metadata of the identified zettel, where metadata keys are encoded as JSON object keys and metadata values encoded as JSON strings.

Additionally, the JSON object contains a key ''"query"'' with a string value.
It will contain a textual description of the underlying query if you [[select only some zettel|00001012051810]].
Without a selection, the value is the empty string.

If you reformat the JSON output from the ''GET /j'' call, you'll see its structure better:
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  ]
}
```
In this special case, the metadata of each zettel just contains the four default keys ''title'', ''tags'', ''syntax'', and ''role''.

[!plain]Alternatively, you can retrieve the list of zettel in a simple, plain format using the [[endpoint|00001012920000]] ''/z''.
In this case, a plain text document is returned, with one line per zettel.
Each line contains in the first 14 characters the zettel identifier.
Separated by a space character, the title of the zettel follows:

```sh
# curl http://127.0.0.1:23123/z
00001012051200 API: Renew an access token
00001012050600 API: Provide an access token
00001012050400 API: Renew an access token







|







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  ]
}
```
In this special case, the metadata of each zettel just contains the four default keys ''title'', ''tags'', ''syntax'', and ''role''.

[!plain]Alternatively, you can retrieve the list of zettel in a simple, plain format using the [[endpoint|00001012920000]] ''/z''.
In this case, a plain text document is returned, with one line per zettel.
Each line contains in the first 14 characters the [[zettel identifier|00001006050000]].
Separated by a space character, the title of the zettel follows:

```sh
# curl http://127.0.0.1:23123/z
00001012051200 API: Renew an access token
00001012050600 API: Provide an access token
00001012050400 API: Renew an access token

Changes to docs/manual/00001012051810.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001012051810
title: API: Select zettel based on their metadata
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211103164030

Every query parameter that does __not__ begin with the low line character (""_"", ''U+005F'') is treated as the name of a [[metadata|00001006010000]] key.
According to the [[type|00001006030000]] of a metadata key, zettel are possibly selected.
All [[supported|00001006020000]] metadata keys have a well-defined type.
User-defined keys have the type ''e'' (string, possibly empty).

For example, if you want to retrieve all zettel that contain the string ""API"" in its title, your request will be:





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001012051810
title: API: Select zettel based on their metadata
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211121170308

Every query parameter that does __not__ begin with the low line character (""_"", ''U+005F'') is treated as the name of a [[metadata|00001006010000]] key.
According to the [[type|00001006030000]] of a metadata key, zettel are possibly selected.
All [[supported|00001006020000]] metadata keys have a well-defined type.
User-defined keys have the type ''e'' (string, possibly empty).

For example, if you want to retrieve all zettel that contain the string ""API"" in its title, your request will be:
30
31
32
33
34
35
36



37
38
39
40
41
42
43
Both ``curl 'http://localhost:23123/j?url='`` and ``curl 'http://localhost:23123/j?url=!'`` may result in an empty list.

The empty query parameter values matches all zettel that contain the given metadata key.
Similar, if you specify just the exclamation mark character as a query parameter value, only those zettel match that does not contain the given metadata key.
This is in contrast to above rule that the metadata value must exist before a match is done.
For example ``curl 'http://localhost:23123/j?back=!&backward='`` returns all zettel that are reachable via other zettel, but also references these zettel.




Above example shows that all sub-expressions of a select specification must be true so that no zettel is rejected from the final list.

If you specify the query parameter ''_negate'', either with or without a value, the whole selection will be negated.
Because of the precondition described above, ``curl 'http://127.0.0.1:23123/j?url=!com'`` and ``curl 'http://127.0.0.1:23123/j?url=com&_negate'`` may produce different lists.
The first query produces a zettel list, where each zettel does have a ''url'' metadata value, which does not contain the characters ""com"".
The second query produces a zettel list, that excludes any zettel containing a ''url'' metadata value that contains the characters ""com""; this also includes all zettel that do not contain the metadata key ''url''.








>
>
>







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Both ``curl 'http://localhost:23123/j?url='`` and ``curl 'http://localhost:23123/j?url=!'`` may result in an empty list.

The empty query parameter values matches all zettel that contain the given metadata key.
Similar, if you specify just the exclamation mark character as a query parameter value, only those zettel match that does not contain the given metadata key.
This is in contrast to above rule that the metadata value must exist before a match is done.
For example ``curl 'http://localhost:23123/j?back=!&backward='`` returns all zettel that are reachable via other zettel, but also references these zettel.

As stated above, the exact rule for comparison depends on the [[type|00001006030000]] of the specified metadata key.
By using a [[simple search syntax|00001012051890]], you are able to specify other comparison operations.[^One is the already mentioned exclamation mark character.]

Above example shows that all sub-expressions of a select specification must be true so that no zettel is rejected from the final list.

If you specify the query parameter ''_negate'', either with or without a value, the whole selection will be negated.
Because of the precondition described above, ``curl 'http://127.0.0.1:23123/j?url=!com'`` and ``curl 'http://127.0.0.1:23123/j?url=com&_negate'`` may produce different lists.
The first query produces a zettel list, where each zettel does have a ''url'' metadata value, which does not contain the characters ""com"".
The second query produces a zettel list, that excludes any zettel containing a ''url'' metadata value that contains the characters ""com""; this also includes all zettel that do not contain the metadata key ''url''.

Changes to docs/manual/00001012051840.zettel.

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
28
29
30
31
32
33
34
35
36
id: 00001012051840
title: API: Shape the list of zettel metadata by searching the content
role: manual
tags: #api #manual #zettelstore
syntax: zmk


The query parameter ""''_s''"" allows to provide a string for a full-text search of all zettel.
The search string will be normalized according to Unicode NKFD, ignoring everything except letters and numbers.

If the search string starts with the character ""''!''"", it will be removed and the query matches all zettel that **do not match** the search string.

In the next step, the first character of the search string will be inspected.
If it contains one of the characters ""'':''"", ""''=''"", ""''>''"", or ""''~''"", this will modify how the search will be performed.
The character will be removed from the start of the search string.

For example, assume the search string is ""def"":

; ""'':''"", ""''~''"" (or none of these characters)[^""'':''"" is always the character for specifying the default comparison. In this case, it is equal to ""''~''"". If you omit a comparison character, the default comparison is used.]
: The zettel must contain a word that contains the search string.
  ""def"", ""defghi"", and ""abcdefghi"" are matching the search string.
; ""''=''""
: The zettel must contain a word that is equal to the search string.
  Only the word ""def"" matches the search string.
; ""''>''""
: The zettel must contain a word with the search string as a prefix.
  A word like ""def"" or ""defghi"" matches the search string.

If you want to include an initial ""''!''"" into the search string, you must prefix that with the escape character ""''\\''"".
For example ""\\!abc"" will search for zettel that contains the string ""!abc"".
A similar rule applies to the characters that specify the way how the search will be done.
For example, ""!\\=abc"" will search for zettel that do not contains the string ""=abc"".

You are allowed to specify this query parameter more than once.
All results will be intersected, i.e. a zettel will be included into the list if all of the provided values match.

This parameter loosely resembles the search box of the web user interface.





>




<
|
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




|
1
2
3
4
5
6
7
8
9
10

11



12
















13
14
15
16
17
id: 00001012051840
title: API: Shape the list of zettel metadata by searching the content
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124182444

The query parameter ""''_s''"" allows to provide a string for a full-text search of all zettel.
The search string will be normalized according to Unicode NKFD, ignoring everything except letters and numbers.


If you want to search in a specific way, you must apply the [[simple search syntax|00001012051890]].



Otherwise, the content of each zettel is examined to just contain the words of the search string.

















You are allowed to specify this query parameter more than once.
All results will be intersected, i.e. a zettel will be included into the list if all of the provided values match.

This parameter loosely resembles the search form of the [[web user interface|00001014000000]].

Added docs/manual/00001012051890.zettel.















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
id: 00001012051890
title: API: Search syntax (simple)
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124135846

If the search string starts with the exclamation mark character (""!"", ''U+0021''), it will be removed and the query matches all values that **do not match** the search string.

In the next step, the first character of the search string will be inspected.
If it contains one of the characters ""'':''"", ""''=''"", ""''>''"", ""''<''"", or ""''~''"", this will modify how the search will be performed.
The character will be removed from the start of the search string.

For example, assume the search string is ""def"":

; The colon character (""'':''"", ''U+003A'') (or none of these characters)
: This is the __default__ comparison.
  The comparison depends on the type of the underlying values.
  For a content search, it is equal to the tilde character ""''~''"", which returns true if a word within the content just contains the search string.
  For metadata, it depends on the key [[type|00001006030000]].

  It you omit the the comparison character, the default comparison is also used.
; The tilde character (""''~''"", ''U+007E'')
: The inspected text[^Either all words of the zettel content and/or some metadata values] contains the search string.
  ""def"", ""defghi"", and ""abcdefghi"" are matching the search string.
; The equal sign character (""''=''"", ''U+003D'')
: The inspected text must contain a word that is equal to the search string.
  Only the word ""def"" matches the search string.
; The greater-than sign character (""''>''"", ''U+003E'')
: The inspected text must contain a word with the search string as a prefix.
  A word like ""def"" or ""defghi"" matches the search string.
; The less-than sign character (""''<''"", ''U+003C'')
: The inspected text must contain a word with the search string as a suffix.
  A word like ""def"" or ""abcdef"" matches the search string.

If you want to include an initial ""''!''"" into the search string, you must prefix that with the escape character ""''\\''"".
For example ""\\!abc"" will search for the string ""!abc"".
A similar rule applies to the characters that specify the way how the search will be done.
For example, ""!\\=abc"" will search for content that does not contains the string ""=abc"".

Changes to docs/manual/00001012052000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
id: 00001012052000
title: API: Sort the list of zettel metadata
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20210709162242

If not specified, the list of zettel is sorted descending by the value of the zettel identifier.
The highest zettel identifier, which is a number, comes first.
You change that with the ""''_sort''"" query parameter.
Alternatively, you can also use the ""''_order''"" query parameter.
It is an alias.

Its value is the name of a metadata key, optionally prefixed with a hyphen-minus character (""-"", ''U+002D'').
According to the [[type|00001006030000]] of a metadata key, the list of zettel is sorted.





|

|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
id: 00001012052000
title: API: Sort the list of zettel metadata
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124175920

If not specified, the list of zettel is sorted descending by the value of the [[zettel identifier|00001006050000]].
The highest zettel identifier, which is a number, comes first.
You change that with the ""''_sort''"" query parameter.
Alternatively, you can also use the ""''_order''"" query parameter.
It is an alias.

Its value is the name of a metadata key, optionally prefixed with a hyphen-minus character (""-"", ''U+002D'').
According to the [[type|00001006030000]] of a metadata key, the list of zettel is sorted.

Changes to docs/manual/00001012053200.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001012053200
title: API: Create a new zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20210905204325

A zettel is created by adding it to the [[list of zettel|00001012000000#zettel-lists]].
Therefore, the [[endpoint|00001012920000]] to create a new zettel is also ''/j'', but you must send the data of the new zettel via a HTTP POST request.

The body of the POST request must contain a JSON object that specifies metadata and content of the zettel to be created.
The following keys of the JSON object are used:
; ''"meta"''





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001012053200
title: API: Create a new zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124180030

A zettel is created by adding it to the [[list of zettel|00001012000000#zettel-lists]].
Therefore, the [[endpoint|00001012920000]] to create a new zettel is also ''/j'', but you must send the data of the new zettel via a HTTP POST request.

The body of the POST request must contain a JSON object that specifies metadata and content of the zettel to be created.
The following keys of the JSON object are used:
; ''"meta"''
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

```
# curl -X POST --data '{}' http://127.0.0.1:23123/j
{"id":"20210713161000"}
```
If creating the zettel was successful, the HTTP response will contain a JSON object with one key:
; ''"id"''
: Contains the zettel identifier of the created zettel for further usage.

In addition, the HTTP response header contains a key ''Location'' with a relative URL for the new zettel.
A client must prepend the HTTP protocol scheme, the host name, and (optional, but often needed) the post number to make it an absolute URL.

As an example, a zettel with title ""Note"" and content ""Important content."" can be created by issuing:
```
# curl -X POST --data '{"meta":{"title":"Note"},"content":"Important content."}' http://127.0.0.1:23123/j
{"id":"20210713163100"}
```

[!plain]Alternatively, you can use the [[endpoint|00001012920000]] ''/z'' to create a new zettel.
In this case, the zettel must be encoded in a [[plain|00001006000000]] format: first comes the [[metadata|00001006010000]] and the following content is separated by an empty line.
This is the same format as used by storing zettel within a [[directory box|00001006010000]].
```
# curl -X POST --data $'title: Note\n\nImportant content.' http://127.0.0.1:23123/z
20210903211500
```

=== HTTP Status codes
; ''201''
: Zettel creation was successful, the body contains a JSON object that contains its zettel identifier.
; ''400''
: Request was not valid. 
  There are several reasons for this.
  Most likely, the JSON was not formed according to above rules.
; ''403''
: You are not allowed to create a new zettel.







|




















|






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

```
# curl -X POST --data '{}' http://127.0.0.1:23123/j
{"id":"20210713161000"}
```
If creating the zettel was successful, the HTTP response will contain a JSON object with one key:
; ''"id"''
: Contains the [[zettel identifier|00001006050000]] of the created zettel for further usage.

In addition, the HTTP response header contains a key ''Location'' with a relative URL for the new zettel.
A client must prepend the HTTP protocol scheme, the host name, and (optional, but often needed) the post number to make it an absolute URL.

As an example, a zettel with title ""Note"" and content ""Important content."" can be created by issuing:
```
# curl -X POST --data '{"meta":{"title":"Note"},"content":"Important content."}' http://127.0.0.1:23123/j
{"id":"20210713163100"}
```

[!plain]Alternatively, you can use the [[endpoint|00001012920000]] ''/z'' to create a new zettel.
In this case, the zettel must be encoded in a [[plain|00001006000000]] format: first comes the [[metadata|00001006010000]] and the following content is separated by an empty line.
This is the same format as used by storing zettel within a [[directory box|00001006010000]].
```
# curl -X POST --data $'title: Note\n\nImportant content.' http://127.0.0.1:23123/z
20210903211500
```

=== HTTP Status codes
; ''201''
: Zettel creation was successful, the body contains its [[zettel identifier|00001006050000]] (JSON object or plain text).
; ''400''
: Request was not valid. 
  There are several reasons for this.
  Most likely, the JSON was not formed according to above rules.
; ''403''
: You are not allowed to create a new zettel.

Changes to docs/manual/00001012053300.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
id: 00001012053300
title: Retrieve metadata and content of an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211004111804

The [[endpoint|00001012920000]] to work with metadata and content of a specific zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the zettel identifier (14 digits).

For example, to retrieve some data about this zettel you are currently viewing, just send a HTTP GET request to the endpoint ''/j/00001012053300''[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header].
If successful, the output is a JSON object:
```sh
# curl http://127.0.0.1:23123/j/00001012053300
{"id":"00001012053300","meta":{"title":"API: Retrieve data for an exisiting zettel","tags":"#api #manual #zettelstore","syntax":"zmk","role":"manual","copyright":"(c) 2020 by Detlef Stern <ds@zettelstore.de>","lang":"en","license":"CC BY-SA 4.0"},"content":"The endpoint to work with a specific zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the zettel identifier (14 digits).\n\nFor example, ...
```

Pretty-printed, this results in:
```
{
  "id": "00001012053300",
  "meta": {

|



|

|





|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
id: 00001012053300
title: API: Retrieve metadata and content of an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124180238

The [[endpoint|00001012920000]] to work with metadata and content of a specific zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the [[zettel identifier|00001006050000]].

For example, to retrieve some data about this zettel you are currently viewing, just send a HTTP GET request to the endpoint ''/j/00001012053300''[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header].
If successful, the output is a JSON object:
```sh
# curl http://127.0.0.1:23123/j/00001012053300
{"id":"00001012053300","meta":{"title":"API: Retrieve data for an exisiting zettel","tags":"#api #manual #zettelstore","syntax":"zmk","role":"manual","copyright":"(c) 2020 by Detlef Stern <ds@zettelstore.de>","lang":"en","license":"CC BY-SA 4.0"},"content":"The endpoint to work with a specific zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the [[zettel identifier|00001006050000}}.\n\nFor example, ...
```

Pretty-printed, this results in:
```
{
  "id": "00001012053300",
  "meta": {
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
76
77
78
Just change the [[endpoint|00001012920000]] to ''/z/{ID}''
Optionally, you may provide which parts of the zettel you are requesting.
In this case, add an additional query parameter ''_part=[[PART|00001012920800]]''.
Valid values are ""zettel"", ""[[meta|00001012053400]]"", and ""content"" (the default value).

````sh
# curl 'http://127.0.0.1:23123/z/00001012053300'
The [[endpoint|00001012920000]] to work with metadata and content of a specific zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the zettel identifier (14 digits).

For example, to retrieve some data about this zettel you are currently viewing, just send a HTTP GET request to the endpoint ''/j/00001012053300''[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header].
If successful, the output is a JSON object:
```sh
...
````

````sh
# curl 'http://127.0.0.1:23123/z/00001012053300?_part=zettel'
title: API: Retrieve metadata and content of an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk

The [[endpoint|00001012920000]] to work with metadata and content of a specific zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the zettel identifier (14 digits).

For example, to retrieve some data about this zettel you are currently viewing, just send a HTTP GET request to the endpoint
...
````

=== HTTP Status codes
; ''200''
: Retrieval was successful, the body contains an appropriate JSON object.
; ''400''
: Request was not valid. 
  There are several reasons for this.
  Maybe the zettel identifier did not consist of exactly 14 digits.
; ''403''
: You are not allowed to retrieve data of the given zettel.
; ''404''
: Zettel not found.
  You probably used a zettel identifier that is not used in the Zettelstore.







|














|











|





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
76
77
78
Just change the [[endpoint|00001012920000]] to ''/z/{ID}''
Optionally, you may provide which parts of the zettel you are requesting.
In this case, add an additional query parameter ''_part=[[PART|00001012920800]]''.
Valid values are ""zettel"", ""[[meta|00001012053400]]"", and ""content"" (the default value).

````sh
# curl 'http://127.0.0.1:23123/z/00001012053300'
The [[endpoint|00001012920000]] to work with metadata and content of a specific zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the [[zettel identifier|00001006050000]].

For example, to retrieve some data about this zettel you are currently viewing, just send a HTTP GET request to the endpoint ''/j/00001012053300''[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header].
If successful, the output is a JSON object:
```sh
...
````

````sh
# curl 'http://127.0.0.1:23123/z/00001012053300?_part=zettel'
title: API: Retrieve metadata and content of an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk

The [[endpoint|00001012920000]] to work with metadata and content of a specific zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the [[zettel identifier|00001006050000]].

For example, to retrieve some data about this zettel you are currently viewing, just send a HTTP GET request to the endpoint
...
````

=== HTTP Status codes
; ''200''
: Retrieval was successful, the body contains an appropriate JSON object.
; ''400''
: Request was not valid. 
  There are several reasons for this.
  Maybe the [[zettel identifier|00001006050000]] did not consists of exactly 14 digits.
; ''403''
: You are not allowed to retrieve data of the given zettel.
; ''404''
: Zettel not found.
  You probably used a zettel identifier that is not used in the Zettelstore.

Changes to docs/manual/00001012053400.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
id: 00001012053400
title: API: Retrieve metadata of an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211004112257

The [[endpoint|00001012920000]] to work with metadata of a specific zettel is ''/m/{ID}'', where ''{ID}'' is a placeholder for the zettel identifier (14 digits).

For example, to retrieve some data about this zettel you are currently viewing, just send a HTTP GET request to the endpoint ''/j/00001012053400''[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header].
If successful, the output is a JSON object:
```sh
# curl http://127.0.0.1:23123/m/00001012053400
{"meta":{"all-tags":"#api #manual #zettelstore","back":"00001012000000 00001012053300","backward":"00001012000000 00001012053300 00001012920000","box-number":"1","forward":"00001010040100 00001012050200 00001012920000 00001012920800","modified":"20211004111240","published":"20211004111240","role":"manual","syntax":"zmk","tags":"#api #manual #zettelstore","title":"API: Retrieve metadata of an existing zettel"}}
```





|

|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
id: 00001012053400
title: API: Retrieve metadata of an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124180327

The [[endpoint|00001012920000]] to work with metadata of a specific zettel is ''/m/{ID}'', where ''{ID}'' is a placeholder for the [[zettel identifier|00001006050000]].

For example, to retrieve some data about this zettel you are currently viewing, just send a HTTP GET request to the endpoint ''/j/00001012053400''[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header].
If successful, the output is a JSON object:
```sh
# curl http://127.0.0.1:23123/m/00001012053400
{"meta":{"all-tags":"#api #manual #zettelstore","back":"00001012000000 00001012053300","backward":"00001012000000 00001012053300 00001012920000","box-number":"1","forward":"00001010040100 00001012050200 00001012920000 00001012920800","modified":"20211004111240","published":"20211004111240","role":"manual","syntax":"zmk","tags":"#api #manual #zettelstore","title":"API: Retrieve metadata of an existing zettel"}}
```

Changes to docs/manual/00001012053500.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
id: 00001012053500
title: API: Retrieve evaluated metadata and content of an existing zettel in various encodings
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211028191044

The [[endpoint|00001012920000]] to work with evaluated metadata and content of a specific zettel is ''/v/{ID}'', where ''{ID}'' is a placeholder for the zettel identifier (14 digits).

For example, to retrieve some evaluated data about this zettel you are currently viewing, just send a HTTP GET request to the endpoint ''/v/00001012053500''[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header].
If successful, the output is a JSON object:
```sh
# curl http://127.0.0.1:23123/v/00001012053500
{"meta":{"title":[{"t":"Text","s":"API:"},{"t":"Space"},{"t":"Text","s":"Retrieve"},{"t":"Space"},{"t":"Text","s":"evaluated"},{"t":"Space"},{"t":"Text","s":"metadata"},{"t":"Space"},{"t":"Text","s":"and"},{"t":"Space"},{"t":"Text","s":"content"},{"t":"Space"},{"t":"Text","s":"of"},{"t":"Space"},{"t":"Text","s":"an"},{"t":"Space"},{"t":"Text","s":"existing"},{"t":"Space"},{"t":"Text","s":"zettel"},{"t":"Space"},{"t":"Text","s":"in"},{"t":"Space"}, ...
```





|

|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
id: 00001012053500
title: API: Retrieve evaluated metadata and content of an existing zettel in various encodings
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124180519

The [[endpoint|00001012920000]] to work with evaluated metadata and content of a specific zettel is ''/v/{ID}'', where ''{ID}'' is a placeholder for the [[zettel identifier|00001006050000]].

For example, to retrieve some evaluated data about this zettel you are currently viewing, just send a HTTP GET request to the endpoint ''/v/00001012053500''[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header].
If successful, the output is a JSON object:
```sh
# curl http://127.0.0.1:23123/v/00001012053500
{"meta":{"title":[{"t":"Text","s":"API:"},{"t":"Space"},{"t":"Text","s":"Retrieve"},{"t":"Space"},{"t":"Text","s":"evaluated"},{"t":"Space"},{"t":"Text","s":"metadata"},{"t":"Space"},{"t":"Text","s":"and"},{"t":"Space"},{"t":"Text","s":"content"},{"t":"Space"},{"t":"Text","s":"of"},{"t":"Space"},{"t":"Text","s":"an"},{"t":"Space"},{"t":"Text","s":"existing"},{"t":"Space"},{"t":"Text","s":"zettel"},{"t":"Space"},{"t":"Text","s":"in"},{"t":"Space"}, ...
```
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<meta name="zs-backward" content="00001012000000">
<meta name="zs-box-number" content="1">
<meta name="copyright" content="(c) 2020-2021 by Detlef Stern <ds@zettelstore.de>">
<meta name="zs-forward" content="00001010040100 00001012050200 00001012920000 00001012920800">
<meta name="zs-published" content="00001012053500">
</head>
<body>
<p>The <a href="00001012920000">endpoint</a> to work with evaluated metadata and content of a specific zettel is <span class="zs-monospace">/v/{ID}</span>, where <span class="zs-monospace">{ID}</span> is a placeholder for the zettel identifier (14 digits).</p>
...
```

You also can use the query parameter ''_part=[[PART|00001012920800]]'' to specify which parts of a zettel must be encoded.
In this case, its default value is ''content''.
```sh
# curl 'http://127.0.0.1:23123/v/00001012053500?_enc=html&_part=meta'







|







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<meta name="zs-backward" content="00001012000000">
<meta name="zs-box-number" content="1">
<meta name="copyright" content="(c) 2020-2021 by Detlef Stern <ds@zettelstore.de>">
<meta name="zs-forward" content="00001010040100 00001012050200 00001012920000 00001012920800">
<meta name="zs-published" content="00001012053500">
</head>
<body>
<p>The <a href="00001012920000">endpoint</a> to work with evaluated metadata and content of a specific zettel is <span class="zs-monospace">/v/{ID}</span>, where <span class="zs-monospace">{ID}</span> is a placeholder for the <a href="00001006050000">zettel identifier</a>.</p>
...
```

You also can use the query parameter ''_part=[[PART|00001012920800]]'' to specify which parts of a zettel must be encoded.
In this case, its default value is ''content''.
```sh
# curl 'http://127.0.0.1:23123/v/00001012053500?_enc=html&_part=meta'

Changes to docs/manual/00001012053600.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id: 00001012053600
title: API: Retrieve parsed metadata and content of an existing zettel in various encodings
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211103164337

The [[endpoint|00001012920000]] to work with parsed metadata and content of a specific zettel is ''/p/{ID}'', where ''{ID}'' is a placeholder for the zettel identifier (14 digits).

A __parsed__ zettel is basically an [[unevaluated|00001012053500]] zettel: the zettel is read and analyzed, but its content is not __evaluated__.
By using this endpoint, you are able to retrieve the structure of a zettel before it is evaluated.

For example, to retrieve some data about this zettel you are currently viewing, just send a HTTP GET request to the endpoint ''/v/00001012053600''[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header].
If successful, the output is a JSON object:
```sh
# curl http://127.0.0.1:23123/p/00001012053600
{"meta":{"title":[{"t":"Text","s":"Retrieve"},{"t":"Space"},{"t":"Text","s":"parsed"},{"t":"Space"},{"t":"Text","s":"metadata"},{"t":"Space"},{"t":"Text","s":"and"},{"t":"Space"},{"t":"Text","s":"content"},{"t":"Space"},{"t":"Text","s":"of"},{"t":"Space"},{"t":"Text","s":"an"},{"t":"Space"},{"t":"Text","s":"existing"},{"t":"Space"},{"t":"Text","s":"zettel"},{"t":"Space"},{"t":"Text","s":"in"},{"t":"Space"},{"t":"Text","s":"various"},{"t":"Space"},{"t":"Text","s":"encodings"}],"role":"manual","tags":["#api", ...
```

Similar to [[retrieving an encoded zettel|00001012053500]], you can specify an [[encoding|00001012920500]] and state which [[part|00001012920800]] of a zettel you are interested in.
The same default values applies to this endpoint.

=== HTTP Status codes
; ''200''





|

|








|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
id: 00001012053600
title: API: Retrieve parsed metadata and content of an existing zettel in various encodings
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124180746

The [[endpoint|00001012920000]] to work with parsed metadata and content of a specific zettel is ''/p/{ID}'', where ''{ID}'' is a placeholder for the [[zettel identifier|00001006050000]].

A __parsed__ zettel is basically an [[unevaluated|00001012053500]] zettel: the zettel is read and analyzed, but its content is not __evaluated__.
By using this endpoint, you are able to retrieve the structure of a zettel before it is evaluated.

For example, to retrieve some data about this zettel you are currently viewing, just send a HTTP GET request to the endpoint ''/v/00001012053600''[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header].
If successful, the output is a JSON object:
```sh
# curl http://127.0.0.1:23123/p/00001012053600
[{"t":"Para","i":[{"t":"Text","s":"The"},{"t":"Space"},{"t":"Link","q":"zettel","s":"00001012920000","i":[{"t":"Text","s":"endpoint"}]},{"t":"Space"},{"t":"Text","s":"to"},{"t":"Space"},{"t":"Text","s":"work"},{"t":"Space"},{"t":"Text","s":"with"},{"t":"Space"}, ...
```

Similar to [[retrieving an encoded zettel|00001012053500]], you can specify an [[encoding|00001012920500]] and state which [[part|00001012920800]] of a zettel you are interested in.
The same default values applies to this endpoint.

=== HTTP Status codes
; ''200''

Changes to docs/manual/00001012053700.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001012053700
title: API: Retrieve references of an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20210825225643

The web of zettel is one important value of a Zettelstore.
Many zettel references other zettel, embedded material, external/local material or, via citations, external literature.
By using the [[endpoint|00001012920000]] ''/l/{ID}'' you are able to retrieve these references.

````
# curl http://127.0.0.1:23123/l/00001012053700





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001012053700
title: API: Retrieve references of an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124180813

The web of zettel is one important value of a Zettelstore.
Many zettel references other zettel, embedded material, external/local material or, via citations, external literature.
By using the [[endpoint|00001012920000]] ''/l/{ID}'' you are able to retrieve these references.

````
# curl http://127.0.0.1:23123/l/00001012053700
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  },
  "embedded": {}
}
````
=== Kind
The following top-level JSON keys are returned:
; ''id''
: The zettel identifier for which the references were requested.
; ''linked''
: A JSON object that contains information about incoming and outgoing [[links|00001007040300#links]].
; ''embedded''
: A JSON object that contains information about referenced [[embedded material|00001007040300#embedded-material]].
; ''cite''
: A JSON list of [[citation keys|00001007040300#citation-key]] (as JSON strings).








|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  },
  "embedded": {}
}
````
=== Kind
The following top-level JSON keys are returned:
; ''id''
: The [[zettel identifier|00001006050000]] for which the references were requested.
; ''linked''
: A JSON object that contains information about incoming and outgoing [[links|00001007040300#links]].
; ''embedded''
: A JSON object that contains information about referenced [[embedded material|00001007040300#embedded-material]].
; ''cite''
: A JSON list of [[citation keys|00001007040300#citation-key]] (as JSON strings).

Changes to docs/manual/00001012053800.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
id: 00001012053800
title: API: Retrieve context of an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211103163027

The context of an origin zettel consists of those zettel that are somehow connected to the origin zettel.
Direct connections of an origin zettel to other zettel are visible via [[metadata values|00001006020000]], such as ''backward'', ''forward'' or other values with type [[identifier|00001006032000]] or [[set of identifier|00001006032500]].

The context is defined by a __direction__, a __depth__, and a __limit__:
* Direction: connections are directed.
  For example, the metadata value of ''backward'' lists all zettel that link to the current zettel, while ''formward'' list all zettel to which the current zettel links.
  When you are only interested in one direction, set the parameter ''dir'' either to the value ""backward"" or ""forward"".
  All other values, including a missing value, is interpreted as ""both"".
* Depth: a direct connection has depth 1, an indirect connection is the length of the shortest path between two zettel.
  You should limit the depth by using the parameter ''depth''.
  Its default value is ""5"".
  A value of ""0"" does disable any depth check.
* Limit: to set an upper bound for the returned context, you should use the parameter ''limit''.





|






|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
id: 00001012053800
title: API: Retrieve context of an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124180826

The context of an origin zettel consists of those zettel that are somehow connected to the origin zettel.
Direct connections of an origin zettel to other zettel are visible via [[metadata values|00001006020000]], such as ''backward'', ''forward'' or other values with type [[identifier|00001006032000]] or [[set of identifier|00001006032500]].

The context is defined by a __direction__, a __depth__, and a __limit__:
* Direction: connections are directed.
  For example, the metadata value of ''backward'' lists all zettel that link to the current zettel, while ''forward'' list all zettel to which the current zettel links to.
  When you are only interested in one direction, set the parameter ''dir'' either to the value ""backward"" or ""forward"".
  All other values, including a missing value, is interpreted as ""both"".
* Depth: a direct connection has depth 1, an indirect connection is the length of the shortest path between two zettel.
  You should limit the depth by using the parameter ''depth''.
  Its default value is ""5"".
  A value of ""0"" does disable any depth check.
* Limit: to set an upper bound for the returned context, you should use the parameter ''limit''.
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
    }
  ]
}
````
=== Keys
The following top-level JSON keys are returned:
; ''id''
: The zettel identifier for which the context was requested.
; ''meta'':
: The metadata of the zettel, encoded as a JSON object.
; ''list''
: A list of JSON objects with keys ''id'' and ''meta'' that contains the zettel of the context.

=== HTTP Status codes
; ''200''







|







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
    }
  ]
}
````
=== Keys
The following top-level JSON keys are returned:
; ''id''
: The [[zettel identifier|00001006050000]] for which the context was requested.
; ''meta'':
: The metadata of the zettel, encoded as a JSON object.
; ''list''
: A list of JSON objects with keys ''id'' and ''meta'' that contains the zettel of the context.

=== HTTP Status codes
; ''200''

Added docs/manual/00001012053900.zettel.



















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
id: 00001012053900
title: API: Retrieve unlinked references to an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124180858

The value of a personal Zettelstore is determined in part by explicit connections between related zettel.
If the number of zettel grow, some of these connections are missing.
There are various reasons for this.
Maybe, you forgot that a zettel exists.
Or you add a zettel later, but forgot that previous zettel already mention its title.

__Unlinked references__ are phrases in a zettel that mention the title of another, currently unlinked zettel.

To retrieve unlinked references to an existing zettel, use the [[endpoint|00001012920000]] ''/u/{ID}''.

````
# curl 'http://127.0.0.1:23123/u/00001007000000'
{"id": "00001007000000","meta": {...},"list": [{"id": "00001012070500","meta": {...}},{"id": "00001006020000","meta": {...}}]}
````
Formatted, this translates into:[^Metadata (key ''meta'') are hidden to make the overall structure easier to read.]
````json
{
  "id": "00001007000000",
  "meta": {...},
  "list": [
    {
      "id": "00001012070500",
      "meta": {...}
    },
    {
      "id": "00001006020000",
      "meta": {...}
    }
  ]
}
````

This call searches within all zettel whether the title of the specified zettel occurs there.
The other zettel must not link to the specified zettel.
The title must not occur within a link (e.g. to another zettel), in a [[heading|00001007030300]], in a [[citation|00001007040340]], and must have a uniform formatting.
The match must be exact, but is case-insensitive.

If the title of the specified zettel contains some extra character that probably reduce the number of found unlinked references,
you can specify the title phase to be searched for as a query parameter ''_phrase'':

````
# curl 'http://127.0.0.1:23123/u/00001007000000?phrase=markdown'
{"id": "00001007000000","meta": {...},"list": [{"id": "00001008010000","meta": {...}},{"id": "00001004020000","meta": {...}}]}
````

In addition, you are allowed to specify all query parameter to [[select zettel based on their metadata|00001012051810]], to [[limit the length of the returned list|00001012051830]], and to [[sort the returned list|00001012052000]]. You are allowed to limit the search only for those zettel with some [[specific content|00001012051840]].

=== Keys
The following top-level JSON keys are returned:
; ''id''
: The [[zettel identifier|00001006050000]] for which the unlinked references were requested.
; ''meta'':
: The metadata of the zettel, encoded as a JSON object.
; ''list''
: A list of JSON objects with keys ''id'' and ''meta'' that describe zettel with unlinked references.

=== HTTP Status codes
; ''200''
: Retrieval was successful, the body contains an appropriate JSON object.
; ''400''
: Request was not valid.
; ''403''
: You are not allowed to retrieve data of the given zettel.
; ''404''
: Zettel not found.
  You probably used a zettel identifier that is not used in the Zettelstore.

Changes to docs/manual/00001012054000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001012054000
title: API: Retrieve zettel order within an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20210825194515

Some zettel act as a ""table of contents"" for other zettel.
The [[initial zettel|00001000000000]] of this manual is one example, the [[general API description|00001012000000]] is another.
Every zettel with a certain internal structure can act as the ""table of contents"" for others.

What is a ""table of contents""?
Basically, it is just a list of references to other zettel.





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001012054000
title: API: Retrieve zettel order within an existing zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124180917

Some zettel act as a ""table of contents"" for other zettel.
The [[initial zettel|00001000000000]] of this manual is one example, the [[general API description|00001012000000]] is another.
Every zettel with a certain internal structure can act as the ""table of contents"" for others.

What is a ""table of contents""?
Basically, it is just a list of references to other zettel.
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
    }
  ]
}
````

The following top-level JSON keys are returned:
; ''id''
: The zettel identifier for which the references were requested.
; ''meta'':
: The metadata of the zettel, encoded as a JSON object.
; ''list''
: A list of JSON objects with keys ''id'' and ''meta'' that describe other zettel in the defined order.

=== HTTP Status codes
; ''200''







|







53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
    }
  ]
}
````

The following top-level JSON keys are returned:
; ''id''
: The [[zettel identifier|00001006050000]] for which the references were requested.
; ''meta'':
: The metadata of the zettel, encoded as a JSON object.
; ''list''
: A list of JSON objects with keys ''id'' and ''meta'' that describe other zettel in the defined order.

=== HTTP Status codes
; ''200''

Changes to docs/manual/00001012054200.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
id: 00001012054200
title: API: Update a zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20210905204628

Updating metadata and content of a zettel is technically quite similar to [[creating a new zettel|00001012053200]].
In both cases you must provide the data for the new or updated zettel in the body of the HTTP request.

One difference is the endpoint.
The [[endpoint|00001012920000]] to update a zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the zettel identifier (14 digits).
You must send a HTTP PUT request to that endpoint:

```
# curl -X PUT --data '{}' http://127.0.0.1:23123/j/00001012054200
```
This will put some empty content and metadata to the zettel you are currently reading.
As usual, some metadata will be calculated if it is empty.





|





|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
id: 00001012054200
title: API: Update a zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124180943

Updating metadata and content of a zettel is technically quite similar to [[creating a new zettel|00001012053200]].
In both cases you must provide the data for the new or updated zettel in the body of the HTTP request.

One difference is the endpoint.
The [[endpoint|00001012920000]] to update a zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the [[zettel identifier|00001006050000]].
You must send a HTTP PUT request to that endpoint:

```
# curl -X PUT --data '{}' http://127.0.0.1:23123/j/00001012054200
```
This will put some empty content and metadata to the zettel you are currently reading.
As usual, some metadata will be calculated if it is empty.

Changes to docs/manual/00001012054400.zettel.

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
28
id: 00001012054400
title: API: Rename a zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211004111301

Renaming a zettel is effectively just specifying a new identifier for the zettel.
Since more than one [[box|00001004011200]] might contain a zettel with the old identifier, the rename operation must success in every relevant box to be overall successful.
If the rename operation fails in one box, Zettelstore tries to rollback previous successful operations.

As a consequence, you cannot rename a zettel when its identifier is used in a read-only box.
This applies to all predefined zettel, for example.

The [[endpoint|00001012920000]] to rename a zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the zettel identifier (14 digits).
You must send a HTTP MOVE request to this endpoint, and you must specify the new zettel identifier as an URL, placed under the HTTP request header key ''Destination''.
```
# curl -X MOVE -H "Destination: 10000000000001" http://127.0.0.1:23123/j/00001000000000
```

Only the last 14 characters of the value of ''Destination'' are taken into account and those must form an unused zettel identifier.
If the value contains less than 14 characters that do not form an unused zettel identifier, the response will contain a HTTP status code ''400''.
All other characters, besides those 14 digits, are effectively ignored.
However, the value should form a valid URL that could be used later to [[read the content|00001012053300]] of the freshly renamed zettel.

[!plain]Alternatively, you can also use the [[endpoint|00001012920000]] ''/z/{ID}''.
Both endpoints behave identical.






|






|

|





|







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
28
id: 00001012054400
title: API: Rename a zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124181324

Renaming a zettel is effectively just specifying a new identifier for the zettel.
Since more than one [[box|00001004011200]] might contain a zettel with the old identifier, the rename operation must success in every relevant box to be overall successful.
If the rename operation fails in one box, Zettelstore tries to rollback previous successful operations.

As a consequence, you cannot rename a zettel when its identifier is used in a read-only box.
This applies to all [[predefined zettel|00001005090000]], for example.

The [[endpoint|00001012920000]] to rename a zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the [[zettel identifier|00001006050000]].
You must send a HTTP MOVE request to this endpoint, and you must specify the new zettel identifier as an URL, placed under the HTTP request header key ''Destination''.
```
# curl -X MOVE -H "Destination: 10000000000001" http://127.0.0.1:23123/j/00001000000000
```

Only the last 14 characters of the value of ''Destination'' are taken into account and those must form an unused [[zettel identifier|00001006050000]].
If the value contains less than 14 characters that do not form an unused zettel identifier, the response will contain a HTTP status code ''400''.
All other characters, besides those 14 digits, are effectively ignored.
However, the value should form a valid URL that could be used later to [[read the content|00001012053300]] of the freshly renamed zettel.

[!plain]Alternatively, you can also use the [[endpoint|00001012920000]] ''/z/{ID}''.
Both endpoints behave identical.

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
: You are not allowed to delete the given zettel.
  In most cases you have either not enough [[access rights|00001010070600]] or at least one box containing the given identifier operates in read-only mode.
; ''404''
: Zettel not found.
  You probably used a zettel identifier that is not used in the Zettelstore.

=== Rationale for the MOVE method
HTTP [[standardizes|https://www.rfc-editor.org/rfc/rfc7231.txt]] seven methods.
None of them is conceptually close to a rename operation.

Everyone is free to ""invent"" some new method to be used in HTTP.
To avoid a divergency, there is a [[methods registry|https://www.iana.org/assignments/http-methods/]] that tracks those extensions.
The [[HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)|https://www.rfc-editor.org/rfc/rfc4918.txt]] defines the method MOVE that is quite close to the desired rename operation.
In fact, some command line tools use a ""move"" method for renaming files.

Therefore, Zettelstore adopts somehow WebDAV's MOVE method and its use of the ''Destination'' HTTP header key.







|








36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
: You are not allowed to delete the given zettel.
  In most cases you have either not enough [[access rights|00001010070600]] or at least one box containing the given identifier operates in read-only mode.
; ''404''
: Zettel not found.
  You probably used a zettel identifier that is not used in the Zettelstore.

=== Rationale for the MOVE method
HTTP [[standardizes|https://www.rfc-editor.org/rfc/rfc7231.txt]] eight methods.
None of them is conceptually close to a rename operation.

Everyone is free to ""invent"" some new method to be used in HTTP.
To avoid a divergency, there is a [[methods registry|https://www.iana.org/assignments/http-methods/]] that tracks those extensions.
The [[HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)|https://www.rfc-editor.org/rfc/rfc4918.txt]] defines the method MOVE that is quite close to the desired rename operation.
In fact, some command line tools use a ""move"" method for renaming files.

Therefore, Zettelstore adopts somehow WebDAV's MOVE method and its use of the ''Destination'' HTTP header key.

Changes to docs/manual/00001012054600.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
id: 00001012054600
title: API: Delete a zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20210905204749

Deleting a zettel within the Zettelstore is executed on the first [[box|00001004011200]] that contains that zettel.
Zettel with the same identifier, but in subsequent boxes remain.
If the first box containing the zettel is read-only, deleting that zettel will fail, as well for a Zettelstore in [[read-only mode|00001004010000#read-only-mode]] or if authentication is enabled and the user has no [[access right|00001010070600]] to do so.

The [[endpoint|00001012920000]] to delete a zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the zettel identifier (14 digits).
You must send a HTTP DELETE request to this endpoint:
```
# curl -X DELETE http://127.0.0.1:23123/j/00001000000000
```

[!plain]Alternatively, you can also use the [[endpoint|00001012920000]] ''/z/{ID}''.
Both endpoints behave identical.





|



|

|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
id: 00001012054600
title: API: Delete a zettel
role: manual
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124181041

Deleting a zettel within the Zettelstore is executed on the first [[box|00001004011200]] that contains that zettel.
Zettel with the same identifier, but in subsequent boxes remain.
If the first box containing the zettel is read-only, deleting that zettel will fail, as well for a Zettelstore in [[read-only mode|00001004010000#read-only-mode]] or if [[authentication is enabled|00001010040100]] and the user has no [[access right|00001010070600]] to do so.

The [[endpoint|00001012920000]] to delete a zettel is ''/j/{ID}'', where ''{ID}'' is a placeholder for the [[zettel identifier|00001006050000]].
You must send a HTTP DELETE request to this endpoint:
```
# curl -X DELETE http://127.0.0.1:23123/j/00001000000000
```

[!plain]Alternatively, you can also use the [[endpoint|00001012920000]] ''/z/{ID}''.
Both endpoints behave identical.

Changes to docs/manual/00001012070500.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
id: 00001012070500
title: API: Encode Zettelmarkup inline material as HTML/Text
role: zettel
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211111145202

To encode [[Zettelmarkup inline material|00001007040000]] send a HTTP POST request to the [[endpoint|00001012920000]] ''/v''.
The POST body must contain a JSON encoded list of Zettelmarkup inline material to be encoded:

; ''first-zmk''
: Contains the first Zettelmarkup encoded material.
  This will be encoded as [[HTML|00001012920510]] and [[Text|00001012920519]].
; ''other-zmk''
: Contain more material.
  The list can be empty.
  These will be encoded in HTML only.
; ''lang''
: Specifies the language for HTML encoding.





|





|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
id: 00001012070500
title: API: Encode Zettelmarkup inline material as HTML/Text
role: zettel
tags: #api #manual #zettelstore
syntax: zmk
modified: 20211124175431

To encode [[Zettelmarkup inline material|00001007040000]] send a HTTP POST request to the [[endpoint|00001012920000]] ''/v''.
The POST body must contain a JSON encoded list of Zettelmarkup inline material to be encoded:

; ''first-zmk''
: Contains the first [[Zettelmarkup encoded|00001007000000]] inline-structured elements.
  This will be encoded as [[HTML|00001012920510]] and [[Text|00001012920519]].
; ''other-zmk''
: Contain more material.
  The list can be empty.
  These will be encoded in HTML only.
; ''lang''
: Specifies the language for HTML encoding.
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
: HTML encoding of ''first-zmk''
; ''first-text''
: Text encoding of ''first-zmk''
; ''other_html''
: HTML encoding of the corresponding value in ''other-zmk''.

Encoding takes place in the context of all other zettel in the Zettelstore.
For example, links and images are evaluated according to this context.

A simple example:
```sh
# curl -X POST --data '{"first-zmk":"hallo [[00000000000001]]"}' http://127.0.0.1:23123/v
{"first-html":"hallo <a href=\"00000000000001\">00000000000001</a>","first-text":"hallo ","other-html":null}

# curl -X POST --data '{"first-zmk":"hallo [[00000000000001]]","no-links":true}' http://127.0.0.1:23123/v







|







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
: HTML encoding of ''first-zmk''
; ''first-text''
: Text encoding of ''first-zmk''
; ''other_html''
: HTML encoding of the corresponding value in ''other-zmk''.

Encoding takes place in the context of all other zettel in the Zettelstore.
For example, [[links|00001007040310]] and images are evaluated according to this context.

A simple example:
```sh
# curl -X POST --data '{"first-zmk":"hallo [[00000000000001]]"}' http://127.0.0.1:23123/v
{"first-html":"hallo <a href=\"00000000000001\">00000000000001</a>","first-text":"hallo ","other-html":null}

# curl -X POST --data '{"first-zmk":"hallo [[00000000000001]]","no-links":true}' http://127.0.0.1:23123/v

Changes to docs/manual/00001012920000.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001012920000
title: Endpoints used by the API
role: manual
tags: #api #manual #reference #zettelstore
syntax: zmk
modified: 20211004111932

All API endpoints conform to the pattern ''[PREFIX]LETTER[/ZETTEL-ID]'', where:
; ''PREFIX''
: is the URL prefix (default: ''/''), configured via the ''url-prefix'' [[startup configuration|00001004010000]],
; ''LETTER''
: is a single letter that specifies the ressource type,
; ''ZETTEL-ID''





|







1
2
3
4
5
6
7
8
9
10
11
12
13
id: 00001012920000
title: Endpoints used by the API
role: manual
tags: #api #manual #reference #zettelstore
syntax: zmk
modified: 20211119134835

All API endpoints conform to the pattern ''[PREFIX]LETTER[/ZETTEL-ID]'', where:
; ''PREFIX''
: is the URL prefix (default: ''/''), configured via the ''url-prefix'' [[startup configuration|00001004010000]],
; ''LETTER''
: is a single letter that specifies the ressource type,
; ''ZETTEL-ID''
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
|       |  | MOVE: [[rename the zettel|00001012054400]]
| ''l'' |  | GET: [[list references|00001012053700]] | **L**inks
| ''m'' |  | GET: [[retrieve metadata|00001012053400]] | **M**etadata
| ''o'' |  | GET: [[list zettel order|00001012054000]] | **O**rder
| ''p'' |  | GET: [[retrieve parsed zettel|00001012053600]]| **P**arsed
| ''r'' | GET: [[list roles|00001012052600]] | | **R**oles
| ''t'' | GET: [[list tags|00001012052400]] || **T**ags

| ''v'' | POST: [[encode inlines|00001012070500]] | GET: [[retrieve evaluated zettel|00001012053500]] | E**v**aluated
| ''x'' |  | GET: [[list zettel context|00001012053800]] | Conte**x**t
| ''z'' | GET: [[list zettel|00001012051200#plain]] | GET: [[retrieve zettel|00001012053300#plain]] | **Z**ettel
|       | POST: [[create new zettel|00001012053200#plain]] | PUT: [[update a zettel|00001012054200#plain]]
|       |  | DELETE: [[delete zettel|00001012054600#plain]]
|       |  | MOVE: [[rename zettel|00001012054400#plain]]

The full URL will contain either the ''http'' oder ''https'' scheme, a host name, and an optional port number.

The API examples will assume the ''http'' schema, the local host ''127.0.0.1'', the default port ''23123'', and the default empty ''PREFIX''.
Therefore, all URLs in the API documentation will begin with ''http://127.0.0.1:23123''.







>











24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|       |  | MOVE: [[rename the zettel|00001012054400]]
| ''l'' |  | GET: [[list references|00001012053700]] | **L**inks
| ''m'' |  | GET: [[retrieve metadata|00001012053400]] | **M**etadata
| ''o'' |  | GET: [[list zettel order|00001012054000]] | **O**rder
| ''p'' |  | GET: [[retrieve parsed zettel|00001012053600]]| **P**arsed
| ''r'' | GET: [[list roles|00001012052600]] | | **R**oles
| ''t'' | GET: [[list tags|00001012052400]] || **T**ags
| ''u'' |  | GET [[unlinked references|00001012053900]] | **U**nlinked
| ''v'' | POST: [[encode inlines|00001012070500]] | GET: [[retrieve evaluated zettel|00001012053500]] | E**v**aluated
| ''x'' |  | GET: [[list zettel context|00001012053800]] | Conte**x**t
| ''z'' | GET: [[list zettel|00001012051200#plain]] | GET: [[retrieve zettel|00001012053300#plain]] | **Z**ettel
|       | POST: [[create new zettel|00001012053200#plain]] | PUT: [[update a zettel|00001012054200#plain]]
|       |  | DELETE: [[delete zettel|00001012054600#plain]]
|       |  | MOVE: [[rename zettel|00001012054400#plain]]

The full URL will contain either the ''http'' oder ''https'' scheme, a host name, and an optional port number.

The API examples will assume the ''http'' schema, the local host ''127.0.0.1'', the default port ''23123'', and the default empty ''PREFIX''.
Therefore, all URLs in the API documentation will begin with ''http://127.0.0.1:23123''.

Changes to docs/manual/00001012920500.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
id: 00001012920500
title: Encodings available by the API
role: manual
tags: #api #manual #reference #zettelstore
syntax: zmk
modified: 20210727120214

A zettel representation can be encoded in various formats for further processing.

* [[djson|00001012920503]] (default)
* [[html|00001012920510]]
* [[native|00001012920513]]
* [[text|00001012920519]]
* [[zmk|00001012920522]]

|



|








1
2
3
4
5
6
7
8
9
10
11
12
13
14
id: 00001012920500
title: Encodings available via the [[API|00001012000000]]
role: manual
tags: #api #manual #reference #zettelstore
syntax: zmk
modified: 20211124140517

A zettel representation can be encoded in various formats for further processing.

* [[djson|00001012920503]] (default)
* [[html|00001012920510]]
* [[native|00001012920513]]
* [[text|00001012920519]]
* [[zmk|00001012920522]]

Changes to docs/manual/00001012920503.zettel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
id: 00001012920503
title: DJSON Encoding
role: manual
tags: #api #manual #reference #zettelstore
syntax: zmk
modified: 20210727120128

A zettel representation that allows to process the syntactic structure of a zettel.
It is a JSON-based encoding format, but different to the structures returned by [[endpoint|00001012920000]] ''/j/{ID}''.

For an example, take a look at the JSON encoding of this page, which is available via the ""Info"" sub-page of this zettel: 

* [[//v/00001012920503?_enc=djson&_part=id]],
* [[//v/00001012920503?_enc=djson&_part=zettel]],
* [[//v/00001012920503?_enc=djson&_part=meta]],
* [[//v/00001012920503?_enc=djson&_part=content]].

If transferred via HTTP, the content type will be ''application/json''.

TODO: detailed description.





|






<







1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
id: 00001012920503
title: DJSON Encoding
role: manual
tags: #api #manual #reference #zettelstore
syntax: zmk
modified: 20211124134305

A zettel representation that allows to process the syntactic structure of a zettel.
It is a JSON-based encoding format, but different to the structures returned by [[endpoint|00001012920000]] ''/j/{ID}''.

For an example, take a look at the JSON encoding of this page, which is available via the ""Info"" sub-page of this zettel: 


* [[//v/00001012920503?_enc=djson&_part=zettel]],
* [[//v/00001012920503?_enc=djson&_part=meta]],
* [[//v/00001012920503?_enc=djson&_part=content]].

If transferred via HTTP, the content type will be ''application/json''.

TODO: detailed description.

Changes to docs/manual/00001012920522.zettel.

1
2
3
4
5
6
7
8
9
10
11
id: 00001012920522
title: Zmk Encoding
role: manual
tags: #api #manual #reference #zettelstore
syntax: zmk
modified: 20210726193136

A zettel representation that tries to recreate a [[Zettelmarkup|00001007000000]] representation of the zettel.
Useful if you want to convert [[other markup languages|00001008000000]] to Zettelmarkup (e.g. Markdown).

If transferred via HTTP, the content type will be ''text/plain''.





|


|


1
2
3
4
5
6
7
8
9
10
11
id: 00001012920522
title: Zmk Encoding
role: manual
tags: #api #manual #reference #zettelstore
syntax: zmk
modified: 20211124140857

A zettel representation that tries to recreate a [[Zettelmarkup|00001007000000]] representation of the zettel.
Useful if you want to convert [[other markup languages|00001008000000]] to Zettelmarkup (e.g. [[Markdown|00001008010000]]).

If transferred via HTTP, the content type will be ''text/plain''.

Changes to domain/id/id.go.

87
88
89
90
91
92
93
94
95
96
97
98


99
100
101
102
103
104
105
106
107
108
109
110
111































112
113
114
115
116
117
118
	zid, err := Parse(string(s))
	if err == nil {
		return zid
	}
	panic(err)
}

const digits = "0123456789"

// String converts the zettel identification to a string of 14 digits.
// Only defined for valid ids.
func (zid Zid) String() string {


	return string(zid.Bytes())
}

// Bytes converts the zettel identification to a byte slice of 14 digits.
// Only defined for valid ids.
func (zid Zid) Bytes() []byte {
	n := uint64(zid)
	result := make([]byte, 14)
	for i := 13; i >= 0; i-- {
		result[i] = digits[n%10]
		n /= 10
	}
	return result































}

// IsValid determines if zettel id is a valid one, e.g. consists of max. 14 digits.
func (zid Zid) IsValid() bool { return 0 < zid && zid <= maxZid }

// New returns a new zettel id based on the current time.
func New(withSeconds bool) Zid {







<
<



>
>
|





<
|
<
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







87
88
89
90
91
92
93


94
95
96
97
98
99
100
101
102
103
104

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
146
147
	zid, err := Parse(string(s))
	if err == nil {
		return zid
	}
	panic(err)
}



// String converts the zettel identification to a string of 14 digits.
// Only defined for valid ids.
func (zid Zid) String() string {
	var result [14]byte
	zid.toByteArray(&result)
	return string(result[:])
}

// Bytes converts the zettel identification to a byte slice of 14 digits.
// Only defined for valid ids.
func (zid Zid) Bytes() []byte {

	var result [14]byte

	zid.toByteArray(&result)
	return result[:]
}

// toByteArray converts the Zid into a fixed byte array, usable for printing.
//
// Based on idea by Daniel Lemire: "Converting integers to fix-digit representations quickly"
// https://lemire.me/blog/2021/11/18/converting-integers-to-fix-digit-representations-quickly/
func (zid Zid) toByteArray(result *[14]byte) {
	date := uint64(zid) / 1000000
	fullyear := date / 10000
	century := fullyear / 100
	year := fullyear % 100
	monthday := date % 10000
	month := monthday / 100
	day := monthday % 100
	time := uint64(zid) % 1000000
	hmtime := time / 100
	second := time % 100
	hour := hmtime / 100
	minute := hmtime % 100
	result[0] = byte(century/10) + '0'
	result[1] = byte(century%10) + '0'
	result[2] = byte(year/10) + '0'
	result[3] = byte(year%10) + '0'
	result[4] = byte(month/10) + '0'
	result[5] = byte(month%10) + '0'
	result[6] = byte(day/10) + '0'
	result[7] = byte(day%10) + '0'
	result[8] = byte(hour/10) + '0'
	result[9] = byte(hour%10) + '0'
	result[10] = byte(minute/10) + '0'
	result[11] = byte(minute%10) + '0'
	result[12] = byte(second/10) + '0'
	result[13] = byte(second%10) + '0'
}

// IsValid determines if zettel id is a valid one, e.g. consists of max. 14 digits.
func (zid Zid) IsValid() bool { return 0 < zid && zid <= maxZid }

// New returns a new zettel id based on the current time.
func New(withSeconds bool) Zid {

Changes to domain/id/id_test.go.

61
62
63
64
65
66
67





















	for i, sid := range invalidIDs {
		if zid, err := id.Parse(sid); err == nil {
			t.Errorf("i=%d: sid=%q is valid (zid=%s), but should not be", i, sid, zid)
		}
	}
}



























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

	for i, sid := range invalidIDs {
		if zid, err := id.Parse(sid); err == nil {
			t.Errorf("i=%d: sid=%q is valid (zid=%s), but should not be", i, sid, zid)
		}
	}
}

var sResult string // to disable compiler optimization in loop below

func BenchmarkString(b *testing.B) {
	var s string
	for n := 0; n < b.N; n++ {
		s = id.Zid(12345678901200).String()
	}
	sResult = s
}

var bResult []byte // to disable compiler optimization in loop below

func BenchmarkBytes(b *testing.B) {
	var bs []byte
	for n := 0; n < b.N; n++ {
		bs = id.Zid(12345678901200).Bytes()
	}
	bResult = bs
}

Changes to encoder/djsonenc/djsonenc.go.

382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
		v.b.WriteByte('"')
		v.b.WriteString(fragment)
		v.b.WriteByte('"')
	}
}

var mapFormatKind = map[ast.FormatKind]string{
	ast.FormatEmphDeprecated: "EmphD",
	ast.FormatEmph:           "Emph",
	ast.FormatStrong:         "Strong",
	ast.FormatMonospace:      "Mono",
	ast.FormatDelete:         "Delete",
	ast.FormatInsert:         "Insert",
	ast.FormatSuper:          "Super",
	ast.FormatSub:            "Sub",
	ast.FormatQuote:          "Quote",
	ast.FormatQuotation:      "Quotation",
	ast.FormatSmall:          "Small",
	ast.FormatSpan:           "Span",
}

var mapLiteralKind = map[ast.LiteralKind]string{
	ast.LiteralProg:    "Code",
	ast.LiteralKeyb:    "Input",
	ast.LiteralOutput:  "Output",
	ast.LiteralComment: "Comment",







<
|
|
|
|
|
|
|
|
|
<
|







382
383
384
385
386
387
388

389
390
391
392
393
394
395
396
397

398
399
400
401
402
403
404
405
		v.b.WriteByte('"')
		v.b.WriteString(fragment)
		v.b.WriteByte('"')
	}
}

var mapFormatKind = map[ast.FormatKind]string{

	ast.FormatEmph:      "Emph",
	ast.FormatStrong:    "Strong",
	ast.FormatMonospace: "Mono",
	ast.FormatDelete:    "Delete",
	ast.FormatInsert:    "Insert",
	ast.FormatSuper:     "Super",
	ast.FormatSub:       "Sub",
	ast.FormatQuote:     "Quote",
	ast.FormatQuotation: "Quotation",

	ast.FormatSpan:      "Span",
}

var mapLiteralKind = map[ast.LiteralKind]string{
	ast.LiteralProg:    "Code",
	ast.LiteralKeyb:    "Input",
	ast.LiteralOutput:  "Output",
	ast.LiteralComment: "Comment",

Changes to encoder/encoder_inline_test.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
			encoderHTML:   "<em>emph</em>",
			encoderNative: `Emph [Text "emph"]`,
			encoderText:   "emph",
			encoderZmk:    useZmk,
		},
	},
	{
		descr: "Emphasized formatting (deprecated)",
		zmk:   "//emphd//",
		expect: expectMap{
			encoderDJSON:  `[{"t":"EmphD","i":[{"t":"Text","s":"emphd"}]}]`,
			encoderHTML:   `<em class="zs-deprecated">emphd</em>`,
			encoderNative: `EmphD [Text "emphd"]`,
			encoderText:   "emphd",
			encoderZmk:    "__emphd__",
		},
	},
	{
		descr: "Strong formatting",
		zmk:   "**strong**",
		expect: expectMap{
			encoderDJSON:  `[{"t":"Strong","i":[{"t":"Text","s":"strong"}]}]`,
			encoderHTML:   "<strong>strong</strong>",
			encoderNative: `Strong [Text "strong"]`,
			encoderText:   "strong",







<
<
<
<
<
<
<
<
<
<
<







41
42
43
44
45
46
47











48
49
50
51
52
53
54
			encoderHTML:   "<em>emph</em>",
			encoderNative: `Emph [Text "emph"]`,
			encoderText:   "emph",
			encoderZmk:    useZmk,
		},
	},
	{











		descr: "Strong formatting",
		zmk:   "**strong**",
		expect: expectMap{
			encoderDJSON:  `[{"t":"Strong","i":[{"t":"Text","s":"strong"}]}]`,
			encoderHTML:   "<strong>strong</strong>",
			encoderNative: `Strong [Text "strong"]`,
			encoderText:   "strong",
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
			encoderHTML:   `<q>quotation</q>`,
			encoderNative: `Quotation [Text "quotation"]`,
			encoderText:   `quotation`,
			encoderZmk:    useZmk,
		},
	},
	{
		descr: "Small formatting",
		zmk:   `;;small;;`,
		expect: expectMap{
			encoderDJSON:  `[{"t":"Small","i":[{"t":"Text","s":"small"}]}]`,
			encoderHTML:   `<small>small</small>`,
			encoderNative: `Small [Text "small"]`,
			encoderText:   `small`,
			encoderZmk:    useZmk,
		},
	},
	{
		descr: "Span formatting",
		zmk:   `::span::`,
		expect: expectMap{
			encoderDJSON:  `[{"t":"Span","i":[{"t":"Text","s":"span"}]}]`,
			encoderHTML:   `<span>span</span>`,
			encoderNative: `Span [Text "span"]`,
			encoderText:   `span`,







<
<
<
<
<
<
<
<
<
<
<







151
152
153
154
155
156
157











158
159
160
161
162
163
164
			encoderHTML:   `<q>quotation</q>`,
			encoderNative: `Quotation [Text "quotation"]`,
			encoderText:   `quotation`,
			encoderZmk:    useZmk,
		},
	},
	{











		descr: "Span formatting",
		zmk:   `::span::`,
		expect: expectMap{
			encoderDJSON:  `[{"t":"Span","i":[{"t":"Text","s":"span"}]}]`,
			encoderHTML:   `<span>span</span>`,
			encoderNative: `Span [Text "span"]`,
			encoderText:   `span`,

Changes to encoder/htmlenc/inline.go.

155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
func (v *visitor) visitFormat(fn *ast.FormatNode) {
	v.lang.push(fn.Attrs)
	defer v.lang.pop()

	var code string
	attrs := fn.Attrs.Clone()
	switch fn.Kind {
	case ast.FormatEmphDeprecated:
		code, attrs = "em", attrs.AddClass("zs-deprecated")
	case ast.FormatEmph:
		code = "em"
	case ast.FormatStrong:
		code = "strong"
	case ast.FormatInsert:
		code = "ins"
	case ast.FormatDelete:
		code = "del"
	case ast.FormatSuper:
		code = "sup"
	case ast.FormatSub:
		code = "sub"
	case ast.FormatQuotation:
		code = "q"
	case ast.FormatSmall:
		code = "small"
	case ast.FormatSpan:
		v.writeSpan(fn.Inlines, processSpanAttributes(attrs))
		return
	case ast.FormatMonospace:
		code, attrs = "span", attrs.AddClass("zs-monospace")
	case ast.FormatQuote:
		v.visitQuotes(fn)







<
<














<
<







155
156
157
158
159
160
161


162
163
164
165
166
167
168
169
170
171
172
173
174
175


176
177
178
179
180
181
182
func (v *visitor) visitFormat(fn *ast.FormatNode) {
	v.lang.push(fn.Attrs)
	defer v.lang.pop()

	var code string
	attrs := fn.Attrs.Clone()
	switch fn.Kind {


	case ast.FormatEmph:
		code = "em"
	case ast.FormatStrong:
		code = "strong"
	case ast.FormatInsert:
		code = "ins"
	case ast.FormatDelete:
		code = "del"
	case ast.FormatSuper:
		code = "sup"
	case ast.FormatSub:
		code = "sub"
	case ast.FormatQuotation:
		code = "q"


	case ast.FormatSpan:
		v.writeSpan(fn.Inlines, processSpanAttributes(attrs))
		return
	case ast.FormatMonospace:
		code, attrs = "span", attrs.AddClass("zs-monospace")
	case ast.FormatQuote:
		v.visitQuotes(fn)

Changes to encoder/nativeenc/nativeenc.go.

477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
	if fragment := mn.Fragment; fragment != "" {
		v.b.WriteString(" #")
		v.writeEscaped(fragment)
	}
}

var mapFormatKind = map[ast.FormatKind][]byte{
	ast.FormatEmphDeprecated: []byte("EmphD"),
	ast.FormatEmph:           []byte("Emph"),
	ast.FormatStrong:         []byte("Strong"),
	ast.FormatInsert:         []byte("Insert"),
	ast.FormatMonospace:      []byte("Mono"),
	ast.FormatDelete:         []byte("Delete"),
	ast.FormatSuper:          []byte("Super"),
	ast.FormatSub:            []byte("Sub"),
	ast.FormatQuote:          []byte("Quote"),
	ast.FormatQuotation:      []byte("Quotation"),
	ast.FormatSmall:          []byte("Small"),
	ast.FormatSpan:           []byte("Span"),
}

var mapLiteralKind = map[ast.LiteralKind][]byte{
	ast.LiteralProg:    []byte("Code"),
	ast.LiteralKeyb:    []byte("Input"),
	ast.LiteralOutput:  []byte("Output"),
	ast.LiteralComment: []byte("Comment"),







<
|
|
|
|
|
|
|
|
|
<
|







477
478
479
480
481
482
483

484
485
486
487
488
489
490
491
492

493
494
495
496
497
498
499
500
	if fragment := mn.Fragment; fragment != "" {
		v.b.WriteString(" #")
		v.writeEscaped(fragment)
	}
}

var mapFormatKind = map[ast.FormatKind][]byte{

	ast.FormatEmph:      []byte("Emph"),
	ast.FormatStrong:    []byte("Strong"),
	ast.FormatInsert:    []byte("Insert"),
	ast.FormatMonospace: []byte("Mono"),
	ast.FormatDelete:    []byte("Delete"),
	ast.FormatSuper:     []byte("Super"),
	ast.FormatSub:       []byte("Sub"),
	ast.FormatQuote:     []byte("Quote"),
	ast.FormatQuotation: []byte("Quotation"),

	ast.FormatSpan:      []byte("Span"),
}

var mapLiteralKind = map[ast.LiteralKind][]byte{
	ast.LiteralProg:    []byte("Code"),
	ast.LiteralKeyb:    []byte("Input"),
	ast.LiteralOutput:  []byte("Output"),
	ast.LiteralComment: []byte("Comment"),

Changes to encoder/zmkenc/zmkenc.go.

308
309
310
311
312
313
314
315
316
317
318
319
320
321

322
323
324
325
326
327
328
329
330
		}
		ast.Walk(v, cell.Inlines)
	}
}

var escapeSeqs = map[string]bool{
	"\\":   true,
	"//":   true,
	"**":   true,
	"__":   true,
	"~~":   true,
	"^^":   true,
	",,":   true,
	"<<":   true,

	"\"\"": true,
	";;":   true,
	"::":   true,
	"''":   true,
	"``":   true,
	"++":   true,
	"==":   true,
}








|

<




>

<







308
309
310
311
312
313
314
315
316

317
318
319
320
321
322

323
324
325
326
327
328
329
		}
		ast.Walk(v, cell.Inlines)
	}
}

var escapeSeqs = map[string]bool{
	"\\":   true,
	"__":   true,
	"**":   true,

	"~~":   true,
	"^^":   true,
	",,":   true,
	"<<":   true,
	">>":   true,
	"\"\"": true,

	"::":   true,
	"''":   true,
	"``":   true,
	"++":   true,
	"==":   true,
}

398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
		ast.Walk(v, cn.Inlines)
	}
	v.b.WriteByte(']')
	v.visitAttributes(cn.Attrs)
}

var mapFormatKind = map[ast.FormatKind][]byte{
	ast.FormatEmphDeprecated: []byte("__"),
	ast.FormatEmph:           []byte("__"),
	ast.FormatStrong:         []byte("**"),
	ast.FormatInsert:         []byte(">>"),
	ast.FormatDelete:         []byte("~~"),
	ast.FormatSuper:          []byte("^^"),
	ast.FormatSub:            []byte(",,"),
	ast.FormatQuotation:      []byte("<<"),
	ast.FormatQuote:          []byte("\"\""),
	ast.FormatSmall:          []byte(";;"),
	ast.FormatSpan:           []byte("::"),
	ast.FormatMonospace:      []byte("''"),
}

func (v *visitor) visitFormat(fn *ast.FormatNode) {
	kind, ok := mapFormatKind[fn.Kind]
	if !ok {
		panic(fmt.Sprintf("Unknown format kind %d", fn.Kind))
	}







<
|
|
|
|
|
|
|
|
<
|
|







397
398
399
400
401
402
403

404
405
406
407
408
409
410
411

412
413
414
415
416
417
418
419
420
		ast.Walk(v, cn.Inlines)
	}
	v.b.WriteByte(']')
	v.visitAttributes(cn.Attrs)
}

var mapFormatKind = map[ast.FormatKind][]byte{

	ast.FormatEmph:      []byte("__"),
	ast.FormatStrong:    []byte("**"),
	ast.FormatInsert:    []byte(">>"),
	ast.FormatDelete:    []byte("~~"),
	ast.FormatSuper:     []byte("^^"),
	ast.FormatSub:       []byte(",,"),
	ast.FormatQuotation: []byte("<<"),
	ast.FormatQuote:     []byte("\"\""),

	ast.FormatSpan:      []byte("::"),
	ast.FormatMonospace: []byte("''"),
}

func (v *visitor) visitFormat(fn *ast.FormatNode) {
	kind, ok := mapFormatKind[fn.Kind]
	if !ok {
		panic(fmt.Sprintf("Unknown format kind %d", fn.Kind))
	}

Changes to evaluator/evaluator.go.

43
44
45
46
47
48
49





50
51
52
53
54
55
56
}

var emptyEnv Environment

// EvaluateZettel evaluates the given zettel in the given context, with the
// given ports, and the given environment.
func EvaluateZettel(ctx context.Context, port Port, env *Environment, rtConfig config.Config, zn *ast.ZettelNode) {





	evaluateNode(ctx, port, env, rtConfig, zn.Ast)
	cleaner.CleanBlockList(zn.Ast)
}

// EvaluateInline evaluates the given inline list in the given context, with
// the given ports, and the given environment.
func EvaluateInline(ctx context.Context, port Port, env *Environment, rtConfig config.Config, iln *ast.InlineListNode) {







>
>
>
>
>







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
}

var emptyEnv Environment

// EvaluateZettel evaluates the given zettel in the given context, with the
// given ports, and the given environment.
func EvaluateZettel(ctx context.Context, port Port, env *Environment, rtConfig config.Config, zn *ast.ZettelNode) {
	if zn.Syntax == api.ValueSyntaxNone {
		// AST is empty, evaluate to a description list of metadata.
		zn.Ast = evaluateMetadata(zn.Meta)
		return
	}
	evaluateNode(ctx, port, env, rtConfig, zn.Ast)
	cleaner.CleanBlockList(zn.Ast)
}

// EvaluateInline evaluates the given inline list in the given context, with
// the given ports, and the given environment.
func EvaluateInline(ctx context.Context, port Port, env *Environment, rtConfig config.Config, iln *ast.InlineListNode) {

Added evaluator/metadata.go.













































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

package evaluator

import (
	"zettelstore.de/z/ast"
	"zettelstore.de/z/domain/meta"
)

func evaluateMetadata(m *meta.Meta) *ast.BlockListNode {
	descrlist := &ast.DescriptionListNode{}
	for _, p := range m.Pairs(true) {
		descrlist.Descriptions = append(
			descrlist.Descriptions, getMetadataDescription(p.Key, p.Value))
	}
	return &ast.BlockListNode{List: []ast.BlockNode{descrlist}}
}

func getMetadataDescription(key, value string) ast.Description {
	return ast.Description{
		Term: ast.CreateInlineListNode(&ast.TextNode{Text: key}),
		Descriptions: []ast.DescriptionSlice{{
			&ast.ParaNode{
				Inlines: convertMetavalueToInlineList(value, meta.Type(key)),
			}},
		},
	}
}

func convertMetavalueToInlineList(value string, dt *meta.DescriptionType) *ast.InlineListNode {
	var sliceData []string
	if dt.IsSet {
		sliceData = meta.ListFromValue(value)
		if len(sliceData) == 0 {
			return &ast.InlineListNode{}
		}
	} else {
		sliceData = []string{value}
	}
	var makeLink bool
	switch dt {
	case meta.TypeID, meta.TypeIDSet:
		makeLink = true
	}

	result := make([]ast.InlineNode, 0, 2*len(sliceData)-1)
	for i, val := range sliceData {
		if i > 0 {
			result = append(result, &ast.SpaceNode{Lexeme: " "})
		}
		tn := &ast.TextNode{Text: val}
		if makeLink {
			result = append(result, &ast.LinkNode{
				Ref:     ast.ParseReference(val),
				Inlines: ast.CreateInlineListNode(tn),
			})
		} else {
			result = append(result, tn)
		}
	}
	return ast.CreateInlineListNode(result...)
}

Changes to go.mod.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module zettelstore.de/z

go 1.17

require (
	github.com/fsnotify/fsnotify v1.5.1
	github.com/pascaldekloe/jwt v1.10.0
	github.com/yuin/goldmark v1.4.3
	golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
	golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
	golang.org/x/text v0.3.7
	zettelstore.de/c v0.0.0-20211025140135-ccc3f543d0e9
)

require golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect







|



|



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module zettelstore.de/z

go 1.17

require (
	github.com/fsnotify/fsnotify v1.5.1
	github.com/pascaldekloe/jwt v1.10.0
	github.com/yuin/goldmark v1.4.4
	golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
	golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
	golang.org/x/text v0.3.7
	zettelstore.de/c v0.0.0-20211124181519-03d5d5692d84
)

require golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect

Changes to go.sum.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/pascaldekloe/jwt v1.10.0 h1:ktcIUV4TPvh404R5dIBEnPCsSwj0sqi3/0+XafE5gJs=
github.com/pascaldekloe/jwt v1.10.0/go.mod h1:TKhllgThT7TOP5rGr2zMLKEDZRAgJfBbtKyVeRsNB9A=
github.com/yuin/goldmark v1.4.3 h1:eTEYYWtQLjK7+WK45Tk81OTkp/0UvAyqUj8flU0nTO4=
github.com/yuin/goldmark v1.4.3/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
zettelstore.de/c v0.0.0-20211025140135-ccc3f543d0e9 h1:X6Kb1JO0SEFxNrXiELXJeDxIMhjv0l0UaXUnRkAofjY=
zettelstore.de/c v0.0.0-20211025140135-ccc3f543d0e9/go.mod h1:Hx/qzHCaQ8zzXEzBglBj/2aGkQpBQG81/4XztCIGJ84=




|
|














|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/pascaldekloe/jwt v1.10.0 h1:ktcIUV4TPvh404R5dIBEnPCsSwj0sqi3/0+XafE5gJs=
github.com/pascaldekloe/jwt v1.10.0/go.mod h1:TKhllgThT7TOP5rGr2zMLKEDZRAgJfBbtKyVeRsNB9A=
github.com/yuin/goldmark v1.4.4 h1:zNWRjYUW32G9KirMXYHQHVNFkXvMI7LpgNW2AgYAoIs=
github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
zettelstore.de/c v0.0.0-20211124181519-03d5d5692d84 h1:YyY7lluBM6UCYVMuE5MiLqfNmGas2PRCnSwnzzczNxM=
zettelstore.de/c v0.0.0-20211124181519-03d5d5692d84/go.mod h1:Hx/qzHCaQ8zzXEzBglBj/2aGkQpBQG81/4XztCIGJ84=

Changes to kernel/impl/cmd.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package impl provides the kernel implementation.
package impl

import (
	"fmt"
	"io"
	"os"
	"runtime/metrics"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package impl

import (
	"fmt"
	"io"
	"os"
	"runtime/metrics"
160
161
162
163
164
165
166

167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

183
184
185
186
187
188
189
				sess.println("echo is on")
			} else {
				sess.println("echo is off")
			}
			return true
		},
	},

	"env":        {"show environment values", cmdEnvironment},
	"get-config": {"show current configuration data", cmdGetConfig},
	"header": {
		"toggle table header",
		func(sess *cmdSession, cmd string, args []string) bool {
			sess.header = !sess.header
			if sess.header {
				sess.println("header are on")
			} else {
				sess.println("header are off")
			}
			return true
		},
	},
	"metrics":     {"show Go runtime metrics", cmdMetrics},
	"next-config": {"show next configuration data", cmdNextConfig},

	"restart":     {"restart service", cmdRestart},
	"services":    {"show available services", cmdServices},
	"set-config":  {"set next configuration data", cmdSetConfig},
	"shutdown": {
		"shutdown Zettelstore",
		func(sess *cmdSession, cmd string, args []string) bool { sess.kern.Shutdown(false); return false },
	},







>
|
|














>







159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
				sess.println("echo is on")
			} else {
				sess.println("echo is off")
			}
			return true
		},
	},
	"end-profile": {"stop profiling", cmdEndProfile},
	"env":         {"show environment values", cmdEnvironment},
	"get-config":  {"show current configuration data", cmdGetConfig},
	"header": {
		"toggle table header",
		func(sess *cmdSession, cmd string, args []string) bool {
			sess.header = !sess.header
			if sess.header {
				sess.println("header are on")
			} else {
				sess.println("header are off")
			}
			return true
		},
	},
	"metrics":     {"show Go runtime metrics", cmdMetrics},
	"next-config": {"show next configuration data", cmdNextConfig},
	"profile":     {"start profiling", cmdProfile},
	"restart":     {"restart service", cmdRestart},
	"services":    {"show available services", cmdServices},
	"set-config":  {"set next configuration data", cmdSetConfig},
	"shutdown": {
		"shutdown Zettelstore",
		func(sess *cmdSession, cmd string, args []string) bool { sess.kern.Shutdown(false); return false },
	},
385
386
387
388
389
390
391

























392
393
394
395
396
397
398
	srvD, ok := sess.kern.srvNames[args[0]]
	if !ok {
		sess.println("Unknown service", args[0])
		return 0, false
	}
	return srvD.srvnum, true
}


























func cmdMetrics(sess *cmdSession, _ string, _ []string) bool {
	var samples []metrics.Sample
	all := metrics.All()
	for _, d := range all {
		if d.Kind == metrics.KindFloat64Histogram {
			continue







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
	srvD, ok := sess.kern.srvNames[args[0]]
	if !ok {
		sess.println("Unknown service", args[0])
		return 0, false
	}
	return srvD.srvnum, true
}

func cmdProfile(sess *cmdSession, _ string, args []string) bool {
	var profileName string
	if len(args) < 1 {
		profileName = kernel.ProfileCPU
	} else {
		profileName = args[0]
	}
	var fileName string
	if len(args) < 2 {
		fileName = profileName + ".prof"
	} else {
		fileName = args[1]
	}
	if err := sess.kern.doStartProfiling(profileName, fileName); err != nil {
		sess.println("Error:", err.Error())
	}
	return true
}
func cmdEndProfile(sess *cmdSession, _ string, _ []string) bool {
	if err := sess.kern.doStopProfiling(); err != nil {
		sess.println("Error:", err.Error())
	}
	return true
}

func cmdMetrics(sess *cmdSession, _ string, _ []string) bool {
	var samples []metrics.Sample
	all := metrics.All()
	for _, d := range all {
		if d.Kind == metrics.KindFloat64Histogram {
			continue

Changes to kernel/impl/impl.go.

8
9
10
11
12
13
14

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
// under this license.
//-----------------------------------------------------------------------------

// Package impl provides the kernel implementation.
package impl

import (

	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/signal"

	"runtime/debug"

	"strconv"
	"sync"
	"syscall"

	"zettelstore.de/z/kernel"
)

// myKernel is the main internal kernel.
type myKernel struct {
	// started   bool
	wg        sync.WaitGroup
	mx        sync.RWMutex
	interrupt chan os.Signal






	core coreService
	cfg  configService
	auth authService
	box  boxService
	web  webService

	srvs     map[kernel.Service]serviceDescr







>






>

>














>
>
>
>
>







8
9
10
11
12
13
14
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
// under this license.
//-----------------------------------------------------------------------------

// Package impl provides the kernel implementation.
package impl

import (
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"runtime"
	"runtime/debug"
	"runtime/pprof"
	"strconv"
	"sync"
	"syscall"

	"zettelstore.de/z/kernel"
)

// myKernel is the main internal kernel.
type myKernel struct {
	// started   bool
	wg        sync.WaitGroup
	mx        sync.RWMutex
	interrupt chan os.Signal

	profileName string
	fileName    string
	profileFile *os.File
	profile     *pprof.Profile

	core coreService
	cfg  configService
	auth authService
	box  boxService
	web  webService

	srvs     map[kernel.Service]serviceDescr
143
144
145
146
147
148
149

150
151
152
153
154
155
156

func (kern *myKernel) doShutdown() {
	kern.StopService(kernel.CoreService) // Will stop all other services.
}

func (kern *myKernel) WaitForShutdown() {
	kern.wg.Wait()

}

// --- Shutdown operation ----------------------------------------------------

// Shutdown the service. Waits for all concurrent activity to stop.
func (kern *myKernel) Shutdown(silent bool) {
	kern.interrupt <- &shutdownSignal{silent: silent}







>







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

func (kern *myKernel) doShutdown() {
	kern.StopService(kernel.CoreService) // Will stop all other services.
}

func (kern *myKernel) WaitForShutdown() {
	kern.wg.Wait()
	kern.doStopProfiling()
}

// --- Shutdown operation ----------------------------------------------------

// Shutdown the service. Waits for all concurrent activity to stop.
func (kern *myKernel) Shutdown(silent bool) {
	kern.interrupt <- &shutdownSignal{silent: silent}
185
186
187
188
189
190
191


































































192
193
194
195
196
197
198
func (kern *myKernel) doLogRecover(name string, recoverInfo interface{}) bool {
	kern.Log(name, "recovered from:", recoverInfo)
	stack := debug.Stack()
	os.Stderr.Write(stack)
	kern.core.updateRecoverInfo(name, recoverInfo, stack)
	return true
}



































































// --- Service handling --------------------------------------------------

func (kern *myKernel) SetConfig(srvnum kernel.Service, key, value string) bool {
	kern.mx.Lock()
	defer kern.mx.Unlock()
	if srvD, ok := kern.srvs[srvnum]; ok {







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
func (kern *myKernel) doLogRecover(name string, recoverInfo interface{}) bool {
	kern.Log(name, "recovered from:", recoverInfo)
	stack := debug.Stack()
	os.Stderr.Write(stack)
	kern.core.updateRecoverInfo(name, recoverInfo, stack)
	return true
}

// --- Profiling ---------------------------------------------------------

var errProfileInWork = errors.New("already profiling")
var errProfileNotFound = errors.New("profile not found")

func (kern *myKernel) StartProfiling(profileName, fileName string) error {
	kern.mx.Lock()
	defer kern.mx.Unlock()
	return kern.doStartProfiling(profileName, fileName)
}
func (kern *myKernel) doStartProfiling(profileName, fileName string) error {
	if kern.profileName != "" {
		return errProfileInWork
	}
	if profileName == kernel.ProfileCPU {
		f, err := os.Create(fileName)
		if err != nil {
			return err
		}
		err = pprof.StartCPUProfile(f)
		if err != nil {
			f.Close()
			return err
		}
		kern.profileName = profileName
		kern.fileName = fileName
		kern.profileFile = f
		return err
	}
	profile := pprof.Lookup(profileName)
	if profile == nil {
		return errProfileNotFound
	}
	f, err := os.Create(fileName)
	if err != nil {
		return err
	}
	kern.profileName = profileName
	kern.fileName = fileName
	kern.profile = profile
	kern.profileFile = f
	runtime.GC() // get up-to-date statistics
	profile.WriteTo(f, 0)
	return nil
}

func (kern *myKernel) StopProfiling() error {
	kern.mx.Lock()
	defer kern.mx.Unlock()
	return kern.doStopProfiling()
}
func (kern *myKernel) doStopProfiling() error {
	if kern.profileName == "" {
		return nil // No profile started
	}
	if kern.profileName == kernel.ProfileCPU {
		pprof.StopCPUProfile()
	}
	err := kern.profileFile.Close()
	kern.profileName = ""
	kern.fileName = ""
	kern.profile = nil
	kern.profileFile = nil
	return err
}

// --- Service handling --------------------------------------------------

func (kern *myKernel) SetConfig(srvnum kernel.Service, key, value string) bool {
	kern.mx.Lock()
	defer kern.mx.Unlock()
	if srvD, ok := kern.srvs[srvnum]; ok {

Changes to kernel/impl/web.go.

140
141
142
143
144
145
146
147
148
149
	err := ws.srvw.Stop()
	ws.mxService.Lock()
	ws.srvw = nil
	ws.mxService.Unlock()
	return err
}

func (ws *webService) GetStatistics() []kernel.KeyValue {
	return nil
}







|


140
141
142
143
144
145
146
147
148
149
	err := ws.srvw.Stop()
	ws.mxService.Lock()
	ws.srvw = nil
	ws.mxService.Unlock()
	return err
}

func (*webService) GetStatistics() []kernel.KeyValue {
	return nil
}

Changes to kernel/kernel.go.

34
35
36
37
38
39
40













41
42
43
44
45
46
47
	Shutdown(silent bool)

	// Log some activity.
	Log(args ...interface{})

	// LogRecover outputs some information about the previous panic.
	LogRecover(name string, recoverInfo interface{}) bool














	// SetConfig stores a configuration value.
	SetConfig(srv Service, key, value string) bool

	// GetConfig returns a configuration value.
	GetConfig(srv Service, key string) interface{}








>
>
>
>
>
>
>
>
>
>
>
>
>







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
	Shutdown(silent bool)

	// Log some activity.
	Log(args ...interface{})

	// LogRecover outputs some information about the previous panic.
	LogRecover(name string, recoverInfo interface{}) bool

	// StartProfiling starts profiling the software according to a profile.
	// It is an error to start more than one profile.
	//
	// profileName is a valid profile (see runtime/pprof/Lookup()), or the
	// value "cpu" for profiling the CPI.
	// fileName is the name of the file where the results are written to.
	StartProfiling(profileName, fileName string) error

	// StopProfiling stops the current profiling and writes the result to
	// the file, which was named during StartProfiling().
	// It will always be called before the software stops its operations.
	StopProfiling() error

	// SetConfig stores a configuration value.
	SetConfig(srv Service, key, value string) bool

	// GetConfig returns a configuration value.
	GetConfig(srv Service, key string) interface{}

71
72
73
74
75
76
77






78
79
80
81
82
83
84
var Main Kernel

// Unit is a type with just one value.
type Unit struct{}

// ShutdownChan is a channel used to signal a system shutdown.
type ShutdownChan <-chan Unit







// Service specifies a service, e.g. web, ...
type Service uint8

// Constants for type Service.
const (
	_ Service = iota







>
>
>
>
>
>







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
var Main Kernel

// Unit is a type with just one value.
type Unit struct{}

// ShutdownChan is a channel used to signal a system shutdown.
type ShutdownChan <-chan Unit

// Constants for profile names.
const (
	ProfileCPU  = "CPU"
	ProfileHead = "heap"
)

// Service specifies a service, e.g. web, ...
type Service uint8

// Constants for type Service.
const (
	_ Service = iota

Changes to parser/none/none.go.

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
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package none provides a none-parser for meta data.
package none

import (
	"zettelstore.de/c/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/domain/meta"
	"zettelstore.de/z/input"
	"zettelstore.de/z/parser"
)

func init() {
	parser.Register(&parser.Info{
		Name:          api.ValueSyntaxNone,
		AltNames:      []string{},
		IsTextParser:  false,
		IsImageFormat: false,
		ParseBlocks:   parseBlocks,
		ParseInlines:  parseInlines,
	})
}

func parseBlocks(_ *input.Input, m *meta.Meta, _ string) *ast.BlockListNode {
	descrlist := &ast.DescriptionListNode{}
	for _, p := range m.Pairs(true) {
		descrlist.Descriptions = append(
			descrlist.Descriptions, getDescription(p.Key, p.Value))
	}
	return &ast.BlockListNode{List: []ast.BlockNode{descrlist}}
}

func getDescription(key, value string) ast.Description {
	return ast.Description{
		Term: ast.CreateInlineListNode(&ast.TextNode{Text: key}),
		Descriptions: []ast.DescriptionSlice{{
			&ast.ParaNode{
				Inlines: convertToInlineList(value, meta.Type(key)),
			}},
		},
	}
}

func convertToInlineList(value string, dt *meta.DescriptionType) *ast.InlineListNode {
	var sliceData []string
	if dt.IsSet {
		sliceData = meta.ListFromValue(value)
		if len(sliceData) == 0 {
			return &ast.InlineListNode{}
		}
	} else {
		sliceData = []string{value}
	}
	var makeLink bool
	switch dt {
	case meta.TypeID, meta.TypeIDSet:
		makeLink = true
	}

	result := make([]ast.InlineNode, 0, 2*len(sliceData)-1)
	for i, val := range sliceData {
		if i > 0 {
			result = append(result, &ast.SpaceNode{Lexeme: " "})
		}
		tn := &ast.TextNode{Text: val}
		if makeLink {
			result = append(result, &ast.LinkNode{
				Ref:     ast.ParseReference(val),
				Inlines: ast.CreateInlineListNode(tn),
			})
		} else {
			result = append(result, tn)
		}
	}
	return ast.CreateInlineListNode(result...)
}

func parseInlines(inp *input.Input, _ string) *ast.InlineListNode {
	inp.SkipToEOL()
	return ast.CreateInlineListNode(
		&ast.FormatNode{
			Kind:  ast.FormatSpan,
			Attrs: &ast.Attributes{Attrs: map[string]string{"class": "warning"}},
			Inlines: ast.CreateInlineListNodeFromWords(
				"parser.meta.ParseInlines:", "not", "possible", "("+string(inp.Src[0:inp.Pos])+")",
			),
		},
	)
}










|




















|
<
<
<
<
<
<
|


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


|
<
<
<
<
<
<
<
<

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
28
29
30
31
32






33
34
35













































36
37
38








39
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package none provides a none-parser, e.g. for zettel with just metadata.
package none

import (
	"zettelstore.de/c/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/domain/meta"
	"zettelstore.de/z/input"
	"zettelstore.de/z/parser"
)

func init() {
	parser.Register(&parser.Info{
		Name:          api.ValueSyntaxNone,
		AltNames:      []string{},
		IsTextParser:  false,
		IsImageFormat: false,
		ParseBlocks:   parseBlocks,
		ParseInlines:  parseInlines,
	})
}
func parseBlocks(*input.Input, *meta.Meta, string) *ast.BlockListNode {






	return &ast.BlockListNode{}
}














































func parseInlines(inp *input.Input, _ string) *ast.InlineListNode {
	inp.SkipToEOL()
	return &ast.InlineListNode{}








}

Changes to parser/parser.go.

127
128
129
130
131
132
133

134
135
	}
	return &ast.ZettelNode{
		Meta:    m,
		Content: zettel.Content,
		Zid:     m.Zid,
		InhMeta: inhMeta,
		Ast:     ParseBlocks(input.NewInput(zettel.Content.AsBytes()), parseMeta, syntax),

	}
}







>


127
128
129
130
131
132
133
134
135
136
	}
	return &ast.ZettelNode{
		Meta:    m,
		Content: zettel.Content,
		Zid:     m.Zid,
		InhMeta: inhMeta,
		Ast:     ParseBlocks(input.NewInput(zettel.Content.AsBytes()), parseMeta, syntax),
		Syntax:  syntax,
	}
}

Changes to parser/zettelmark/inline.go.

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
			if inp.Ch == '{' {
				in, success = cp.parseEmbed()
			}
		case '#':
			return cp.parseTag()
		case '%':
			in, success = cp.parseComment()
		case '/', '_', '*', '>', '~', '\'', '^', ',', '<', '"', ';', ':':
			in, success = cp.parseFormat()
		case '+', '`', '=', runeModGrave:
			in, success = cp.parseLiteral()
		case '\\':
			return cp.parseBackslash()
		case '-':
			in, success = cp.parseNdash()







|







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
			if inp.Ch == '{' {
				in, success = cp.parseEmbed()
			}
		case '#':
			return cp.parseTag()
		case '%':
			in, success = cp.parseComment()
		case '_', '*', '>', '~', '\'', '^', ',', '<', '"', ':':
			in, success = cp.parseFormat()
		case '+', '`', '=', runeModGrave:
			in, success = cp.parseLiteral()
		case '\\':
			return cp.parseBackslash()
		case '-':
			in, success = cp.parseNdash()
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
		return cp.parseTextBackslash()
	}
	for {
		inp.Next()
		switch inp.Ch {
		// The following case must contain all runes that occur in parseInline!
		// Plus the closing brackets ] and } and ) and the middle |
		case '/', input.EOS, '\n', '\r', ' ', '\t', '[', ']', '{', '}', '(', ')', '|', '#', '%', '_', '*', '>', '~', '\'', '^', ',', '<', '"', ';', ':', '+', '`', runeModGrave, '=', '\\', '-', '&':
			return &ast.TextNode{Text: string(inp.Src[pos:inp.Pos])}
		}
	}
}

func (cp *zmkP) parseTextBackslash() *ast.TextNode {
	cp.inp.Next()







|







96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
		return cp.parseTextBackslash()
	}
	for {
		inp.Next()
		switch inp.Ch {
		// The following case must contain all runes that occur in parseInline!
		// Plus the closing brackets ] and } and ) and the middle |
		case input.EOS, '\n', '\r', ' ', '\t', '[', ']', '{', '}', '(', ')', '|', '#', '%', '_', '*', '>', '~', '\'', '^', ',', '<', '"', ':', '+', '`', runeModGrave, '=', '\\', '-', '&':
			return &ast.TextNode{Text: string(inp.Src[pos:inp.Pos])}
		}
	}
}

func (cp *zmkP) parseTextBackslash() *ast.TextNode {
	cp.inp.Next()
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
			return &ast.LiteralNode{Kind: ast.LiteralComment, Text: string(inp.Src[pos:inp.Pos])}, true
		}
		inp.Next()
	}
}

var mapRuneFormat = map[rune]ast.FormatKind{
	'/':  ast.FormatEmphDeprecated,
	'_':  ast.FormatEmph,
	'*':  ast.FormatStrong,
	'>':  ast.FormatInsert,
	'~':  ast.FormatDelete,
	'\'': ast.FormatMonospace,
	'^':  ast.FormatSuper,
	',':  ast.FormatSub,
	'<':  ast.FormatQuotation,
	'"':  ast.FormatQuote,
	';':  ast.FormatSmall,
	':':  ast.FormatSpan,
}

func (cp *zmkP) parseFormat() (res ast.InlineNode, success bool) {
	inp := cp.inp
	fch := inp.Ch
	kind, ok := mapRuneFormat[fch]







<









<







403
404
405
406
407
408
409

410
411
412
413
414
415
416
417
418

419
420
421
422
423
424
425
			return &ast.LiteralNode{Kind: ast.LiteralComment, Text: string(inp.Src[pos:inp.Pos])}, true
		}
		inp.Next()
	}
}

var mapRuneFormat = map[rune]ast.FormatKind{

	'_':  ast.FormatEmph,
	'*':  ast.FormatStrong,
	'>':  ast.FormatInsert,
	'~':  ast.FormatDelete,
	'\'': ast.FormatMonospace,
	'^':  ast.FormatSuper,
	',':  ast.FormatSub,
	'<':  ast.FormatQuotation,
	'"':  ast.FormatQuote,

	':':  ast.FormatSpan,
}

func (cp *zmkP) parseFormat() (res ast.InlineNode, success bool) {
	inp := cp.inp
	fch := inp.Ch
	kind, ok := mapRuneFormat[fch]

Changes to parser/zettelmark/zettelmark_test.go.

300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
		{"100%", "(PARA 100%)"},
	})
}

func TestFormat(t *testing.T) {
	t.Parallel()
	// Not for Insert / '>', because collision with quoted list
	for _, ch := range []string{"_", "*", "~", "'", "^", ",", "<", "\"", ";", ":"} {
		checkTcs(t, replace(ch, TestCases{
			{"$", "(PARA $)"},
			{"$$", "(PARA $$)"},
			{"$$$", "(PARA $$$)"},
			{"$$$$", "(PARA {$})"},
		}))
	}
	for _, ch := range []string{"_", "*", ">", "~", "'", "^", ",", "<", "\"", ";", ":"} {
		checkTcs(t, replace(ch, TestCases{
			{"$$a$$", "(PARA {$ a})"},
			{"$$a$$$", "(PARA {$ a} $)"},
			{"$$$a$$", "(PARA {$ $a})"},
			{"$$$a$$$", "(PARA {$ $a} $)"},
			{"$\\$", "(PARA $$)"},
			{"$\\$$", "(PARA $$$)"},







|







|







300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
		{"100%", "(PARA 100%)"},
	})
}

func TestFormat(t *testing.T) {
	t.Parallel()
	// Not for Insert / '>', because collision with quoted list
	for _, ch := range []string{"_", "*", "~", "'", "^", ",", "<", "\"", ":"} {
		checkTcs(t, replace(ch, TestCases{
			{"$", "(PARA $)"},
			{"$$", "(PARA $$)"},
			{"$$$", "(PARA $$$)"},
			{"$$$$", "(PARA {$})"},
		}))
	}
	for _, ch := range []string{"_", "*", ">", "~", "'", "^", ",", "<", "\"", ":"} {
		checkTcs(t, replace(ch, TestCases{
			{"$$a$$", "(PARA {$ a})"},
			{"$$a$$$", "(PARA {$ a} $)"},
			{"$$$a$$", "(PARA {$ $a})"},
			{"$$$a$$$", "(PARA {$ $a} $)"},
			{"$\\$", "(PARA $$)"},
			{"$\\$$", "(PARA $$$)"},
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
	ast.FormatInsert:    '>',
	ast.FormatDelete:    '~',
	ast.FormatMonospace: '\'',
	ast.FormatSuper:     '^',
	ast.FormatSub:       ',',
	ast.FormatQuote:     '"',
	ast.FormatQuotation: '<',
	ast.FormatSmall:     ';',
	ast.FormatSpan:      ':',
}

var mapLiteralKind = map[ast.LiteralKind]rune{
	ast.LiteralProg:    '`',
	ast.LiteralKeyb:    '+',
	ast.LiteralOutput:  '=',







<







901
902
903
904
905
906
907

908
909
910
911
912
913
914
	ast.FormatInsert:    '>',
	ast.FormatDelete:    '~',
	ast.FormatMonospace: '\'',
	ast.FormatSuper:     '^',
	ast.FormatSub:       ',',
	ast.FormatQuote:     '"',
	ast.FormatQuotation: '<',

	ast.FormatSpan:      ':',
}

var mapLiteralKind = map[ast.LiteralKind]rune{
	ast.LiteralProg:    '`',
	ast.LiteralKeyb:    '+',
	ast.LiteralOutput:  '=',

Changes to search/print.go.

11
12
13
14
15
16
17

18
19
20






21
22
23



24
25
26
27
28
29
30
// Package search provides a zettel search.
package search

import (
	"io"
	"sort"
	"strconv"


	"zettelstore.de/c/api"
)







// Print the search to a writer.
func (s *Search) Print(w io.Writer) {



	if s.negate {
		io.WriteString(w, "NOT (")
	}
	space := false
	if len(s.search) > 0 {
		io.WriteString(w, "ANY")
		printSelectExprValues(w, s.search)







>



>
>
>
>
>
>



>
>
>







11
12
13
14
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
// Package search provides a zettel search.
package search

import (
	"io"
	"sort"
	"strconv"
	"strings"

	"zettelstore.de/c/api"
)

func (s *Search) String() string {
	var sb strings.Builder
	s.Print(&sb)
	return sb.String()
}

// Print the search to a writer.
func (s *Search) Print(w io.Writer) {
	if s == nil {
		return
	}
	if s.negate {
		io.WriteString(w, "NOT (")
	}
	space := false
	if len(s.search) > 0 {
		io.WriteString(w, "ANY")
		printSelectExprValues(w, s.search)
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
































114
115
116
117
118
119
		space = true
	}
	if s.negate {
		io.WriteString(w, ")")
		space = true
	}

	if ord := s.order; len(ord) > 0 {
		switch ord {
		case api.KeyID:
			// Ignore
		case RandomOrder:
			space = printSpace(w, space)
			io.WriteString(w, "RANDOM")
		default:
			space = printSpace(w, space)
			io.WriteString(w, "SORT ")
			io.WriteString(w, ord)
			if s.descending {
				io.WriteString(w, " DESC")
			}
		}
	}
	if off := s.offset; off > 0 {
		space = printSpace(w, space)
		io.WriteString(w, "OFFSET ")
		io.WriteString(w, strconv.Itoa(off))
	}
	if lim := s.limit; lim > 0 {
		_ = printSpace(w, space)
		io.WriteString(w, "LIMIT ")
		io.WriteString(w, strconv.Itoa(lim))
	}
}

func printSelectExprValues(w io.Writer, values []expValue) {
	if len(values) == 0 {
		io.WriteString(w, " MATCH ANY")
		return
	}

	for j, val := range values {
		if j > 0 {
			io.WriteString(w, " AND")
		}
		if val.negate {
			io.WriteString(w, " NOT")
		}
		switch val.op {
		case cmpDefault:
			io.WriteString(w, " MATCH ")
		case cmpEqual:
			io.WriteString(w, " HAS ")
		case cmpPrefix:
			io.WriteString(w, " PREFIX ")
		case cmpSuffix:
			io.WriteString(w, " SUFFIX ")
		case cmpContains:
			io.WriteString(w, " CONTAINS ")
		default:
			io.WriteString(w, " MaTcH ")
		}
		if val.value == "" {
			io.WriteString(w, "ANY")
		} else {
			io.WriteString(w, val.value)
		}
	}
}

































func printSpace(w io.Writer, space bool) bool {
	if space {
		io.WriteString(w, " ")
	}
	return true
}







<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
|
<
<
<
<
<
|
<
<



















|

















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|



54
55
56
57
58
59
60











61





62





63


64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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
		space = true
	}
	if s.negate {
		io.WriteString(w, ")")
		space = true
	}












	space = printOrder(w, s.order, s.descending, space)





	space = printPosInt(w, "OFFSET", s.offset, space)





	_ = printPosInt(w, "LIMIT", s.limit, space)


}

func printSelectExprValues(w io.Writer, values []expValue) {
	if len(values) == 0 {
		io.WriteString(w, " MATCH ANY")
		return
	}

	for j, val := range values {
		if j > 0 {
			io.WriteString(w, " AND")
		}
		if val.negate {
			io.WriteString(w, " NOT")
		}
		switch val.op {
		case cmpDefault:
			io.WriteString(w, " MATCH ")
		case cmpEqual:
			io.WriteString(w, " EQUAL ")
		case cmpPrefix:
			io.WriteString(w, " PREFIX ")
		case cmpSuffix:
			io.WriteString(w, " SUFFIX ")
		case cmpContains:
			io.WriteString(w, " CONTAINS ")
		default:
			io.WriteString(w, " MaTcH ")
		}
		if val.value == "" {
			io.WriteString(w, "ANY")
		} else {
			io.WriteString(w, val.value)
		}
	}
}

func printOrder(w io.Writer, order string, descending, withSpace bool) bool {
	if len(order) > 0 {
		switch order {
		case api.KeyID:
			// Ignore
		case RandomOrder:
			withSpace = printSpace(w, withSpace)
			io.WriteString(w, "RANDOM")
		default:
			withSpace = printSpace(w, withSpace)
			io.WriteString(w, "SORT ")
			io.WriteString(w, order)
			if descending {
				io.WriteString(w, " DESC")
			}
		}
	}
	return withSpace
}

func printPosInt(w io.Writer, key string, val int, space bool) bool {
	if val > 0 {
		space = printSpace(w, space)
		io.WriteString(w, key)
		w.Write(bsSpace)
		io.WriteString(w, strconv.Itoa(val))
	}
	return space
}

var bsSpace = []byte{' '}

func printSpace(w io.Writer, space bool) bool {
	if space {
		w.Write(bsSpace)
	}
	return true
}

Changes to search/search.go.

65
66
67
68
69
70
71

72

73

74

75

76

77


















78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

95
96
97
98

99
100
101
102
103
104
105

// RandomOrder is a pseudo metadata key that selects a random order.
const RandomOrder = "_random"

type compareOp uint8

const (

	cmpDefault compareOp = iota

	cmpEqual

	cmpPrefix

	cmpSuffix

	cmpContains

)



















type expValue struct {
	value  string
	op     compareOp
	negate bool
}

// AddExpr adds a match expression to the search.
func (s *Search) AddExpr(key, val string) *Search {
	val, negate, op := parseOp(strings.TrimSpace(val))
	if s == nil {
		s = new(Search)
	}
	s.mx.Lock()
	defer s.mx.Unlock()
	if key == "" {
		s.search = append(s.search, expValue{value: val, op: op, negate: negate})

	} else if s.tags == nil {
		s.tags = expTagValues{key: {{value: val, op: op, negate: negate}}}
	} else {
		s.tags[key] = append(s.tags[key], expValue{value: val, op: op, negate: negate})

	}
	return s
}

func parseOp(s string) (r string, negate bool, op compareOp) {
	if s == "" {
		return s, false, cmpDefault







>
|
>

>

>

>

>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

















>
|
|
|
|
>







65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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

// RandomOrder is a pseudo metadata key that selects a random order.
const RandomOrder = "_random"

type compareOp uint8

const (
	cmpUnknown compareOp = iota
	cmpDefault
	cmpNotDefault
	cmpEqual
	cmpNotEqual
	cmpPrefix
	cmpNoPrefix
	cmpSuffix
	cmpNoSuffix
	cmpContains
	cmpNotContains
)

var negateMap = map[compareOp]compareOp{
	cmpUnknown:     cmpUnknown,
	cmpDefault:     cmpNotDefault,
	cmpNotDefault:  cmpDefault,
	cmpEqual:       cmpNotEqual,
	cmpNotEqual:    cmpEqual,
	cmpPrefix:      cmpNoPrefix,
	cmpNoPrefix:    cmpPrefix,
	cmpSuffix:      cmpNoSuffix,
	cmpNoSuffix:    cmpSuffix,
	cmpContains:    cmpNotContains,
	cmpNotContains: cmpContains,
}

func (op compareOp) negate() compareOp {
	return negateMap[op]
}

type expValue struct {
	value  string
	op     compareOp
	negate bool
}

// AddExpr adds a match expression to the search.
func (s *Search) AddExpr(key, val string) *Search {
	val, negate, op := parseOp(strings.TrimSpace(val))
	if s == nil {
		s = new(Search)
	}
	s.mx.Lock()
	defer s.mx.Unlock()
	if key == "" {
		s.search = append(s.search, expValue{value: val, op: op, negate: negate})
	} else {
		if s.tags == nil {
			s.tags = expTagValues{key: {{value: val, op: op, negate: negate}}}
		} else {
			s.tags[key] = append(s.tags[key], expValue{value: val, op: op, negate: negate})
		}
	}
	return s
}

func parseOp(s string) (r string, negate bool, op compareOp) {
	if s == "" {
		return s, false, cmpDefault
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262

263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
	}
	return false
}

// CompileMatch returns a function to match meta data based on select specification.
func (s *Search) CompileMatch(searcher Searcher) MetaMatchFunc {
	if s == nil {
		return selectNone
	}
	s.mx.Lock()
	defer s.mx.Unlock()

	compMeta := compileSelect(s.tags)
	compSearch := compileFullSearch(searcher, s.search)
	if preMatch := s.preMatch; preMatch != nil {
		return compilePreMatch(preMatch, compMeta, compSearch, s.negate)
	}
	return compileNoPreMatch(compMeta, compSearch, s.negate)
}

func selectNone(*meta.Meta) bool { return true }


func compilePreMatch(preMatch, compMeta, compSearch MetaMatchFunc, negate bool) MetaMatchFunc {
	if compMeta == nil {
		if compSearch == nil {
			return preMatch
		}
		if negate {
			return func(m *meta.Meta) bool { return preMatch(m) && !compSearch(m) }
		}
		return func(m *meta.Meta) bool { return preMatch(m) && compSearch(m) }
	}
	if compSearch == nil {
		if negate {
			return func(m *meta.Meta) bool { return preMatch(m) && !compMeta(m) }
		}
		return func(m *meta.Meta) bool { return preMatch(m) && compMeta(m) }
	}
	if negate {
		return func(m *meta.Meta) bool { return preMatch(m) && (!compMeta(m) || !compSearch(m)) }
	}
	return func(m *meta.Meta) bool { return preMatch(m) && compMeta(m) && compSearch(m) }
}

func compileNoPreMatch(compMeta, compSearch MetaMatchFunc, negate bool) MetaMatchFunc {
	if compMeta == nil {
		if compSearch == nil {
			if negate {
				return func(m *meta.Meta) bool { return false }
			}
			return selectNone
		}
		if negate {
			return func(m *meta.Meta) bool { return !compSearch(m) }
		}
		return compSearch
	}
	if compSearch == nil {







|












|
>






<
|
|
<
<

<
|
|
|
<
<
<
<
<






|

|







268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295

296
297


298

299
300
301





302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
	}
	return false
}

// CompileMatch returns a function to match meta data based on select specification.
func (s *Search) CompileMatch(searcher Searcher) MetaMatchFunc {
	if s == nil {
		return selectAlways
	}
	s.mx.Lock()
	defer s.mx.Unlock()

	compMeta := compileSelect(s.tags)
	compSearch := compileFullSearch(searcher, s.search)
	if preMatch := s.preMatch; preMatch != nil {
		return compilePreMatch(preMatch, compMeta, compSearch, s.negate)
	}
	return compileNoPreMatch(compMeta, compSearch, s.negate)
}

func selectAlways(*meta.Meta) bool { return true }
func selectNever(*meta.Meta) bool  { return false }

func compilePreMatch(preMatch, compMeta, compSearch MetaMatchFunc, negate bool) MetaMatchFunc {
	if compMeta == nil {
		if compSearch == nil {
			return preMatch
		}

		return func(m *meta.Meta) bool { return preMatch(m) && (compSearch(m) != negate) }
	}


	if compSearch == nil {

		return func(m *meta.Meta) bool { return preMatch(m) && (compMeta(m) != negate) }
	}
	return func(m *meta.Meta) bool { return preMatch(m) && ((compMeta(m) && compSearch(m)) != negate) }





}

func compileNoPreMatch(compMeta, compSearch MetaMatchFunc, negate bool) MetaMatchFunc {
	if compMeta == nil {
		if compSearch == nil {
			if negate {
				return selectNever
			}
			return selectAlways
		}
		if negate {
			return func(m *meta.Meta) bool { return !compSearch(m) }
		}
		return compSearch
	}
	if compSearch == nil {
330
331
332
333
334
335
336
337








338
339
340
341
342
	}

	if s.offset > 0 {
		if s.offset > len(metaList) {
			return nil
		}
		metaList = metaList[s.offset:]
	}








	if s.limit > 0 && s.limit < len(metaList) {
		metaList = metaList[:s.limit]
	}
	return metaList
}








>
>
>
>
>
>
>
>

|



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
	}

	if s.offset > 0 {
		if s.offset > len(metaList) {
			return nil
		}
		metaList = metaList[s.offset:]
	}
	return s.Limit(metaList)
}

// Limit returns only s.GetLimit() elements of the given list.
func (s *Search) Limit(metaList []*meta.Meta) []*meta.Meta {
	if s == nil {
		return metaList
	}
	if s.limit > 0 && s.limit < len(metaList) {
		return metaList[:s.limit]
	}
	return metaList
}

Changes to search/select.go.

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
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
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package search provides a zettel search.
package search

import (

	"strings"

	"zettelstore.de/c/api"
	"zettelstore.de/z/domain/meta"
)

type matchFunc func(value string) bool

func matchNever(string) bool  { return false }
func matchAlways(string) bool { return true }

type matchSpec struct {
	key   string
	match matchFunc
}

// compileSelect calculates a selection func based on the given select criteria.
func compileSelect(tags expTagValues) MetaMatchFunc {
	posSpecs, negSpecs, nomatch := createSelectSpecs(tags)
	if len(posSpecs) > 0 || len(negSpecs) > 0 || len(nomatch) > 0 {
		return makeSearchMetaMatchFunc(posSpecs, negSpecs, nomatch)
	}
	return nil
}

func createSelectSpecs(tags map[string][]expValue) (posSpecs, negSpecs []matchSpec, nomatch []string) {
	posSpecs = make([]matchSpec, 0, len(tags))
	negSpecs = make([]matchSpec, 0, len(tags))
	for key, values := range tags {
		if !meta.KeyIsValid(key) {
			continue
		}
		if always, never := countEmptyValues(values); always+never > 0 {
			if never == 0 {
				posSpecs = append(posSpecs, matchSpec{key, matchAlways})
				continue
			}
			if always == 0 {
				negSpecs = append(negSpecs, matchSpec{key, nil})
				continue
			}
			// value must match always AND never, at the same time. This results in a no-match.










<



>


<



|

|
|



|




















|







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
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
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package search

import (
	"fmt"
	"strings"


	"zettelstore.de/z/domain/meta"
)

type matchValueFunc func(value string) bool

func matchValueNever(string) bool  { return false }
func matchValueAlways(string) bool { return true }

type matchSpec struct {
	key   string
	match matchValueFunc
}

// compileSelect calculates a selection func based on the given select criteria.
func compileSelect(tags expTagValues) MetaMatchFunc {
	posSpecs, negSpecs, nomatch := createSelectSpecs(tags)
	if len(posSpecs) > 0 || len(negSpecs) > 0 || len(nomatch) > 0 {
		return makeSearchMetaMatchFunc(posSpecs, negSpecs, nomatch)
	}
	return nil
}

func createSelectSpecs(tags map[string][]expValue) (posSpecs, negSpecs []matchSpec, nomatch []string) {
	posSpecs = make([]matchSpec, 0, len(tags))
	negSpecs = make([]matchSpec, 0, len(tags))
	for key, values := range tags {
		if !meta.KeyIsValid(key) {
			continue
		}
		if always, never := countEmptyValues(values); always+never > 0 {
			if never == 0 {
				posSpecs = append(posSpecs, matchSpec{key, matchValueAlways})
				continue
			}
			if always == 0 {
				negSpecs = append(negSpecs, matchSpec{key, nil})
				continue
			}
			// value must match always AND never, at the same time. This results in a no-match.
66
67
68
69
70
71
72
73
74
75
76
77
78
79

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
		}
	}
	return posSpecs, negSpecs, nomatch
}

func countEmptyValues(values []expValue) (always, never int) {
	for _, v := range values {
		if v.value != "" {
			continue
		}
		if v.negate {
			never++
		} else {
			always++

		}
	}
	return always, never
}

func createPosNegMatchFunc(key string, values []expValue) (posMatch, negMatch matchFunc) {
	posValues := make([]opValue, 0, len(values))
	negValues := make([]opValue, 0, len(values))
	for _, val := range values {
		if val.negate {
			negValues = append(negValues, opValue{value: val.value, op: val.op})
		} else {
			posValues = append(posValues, opValue{value: val.value, op: val.op})
		}
	}
	return createMatchFunc(key, posValues), createMatchFunc(key, negValues)
}

// opValue is an expValue, but w/o the field "negate"
type opValue struct {
	value string
	op    compareOp
}

func createMatchFunc(key string, values []opValue) matchFunc {
	if len(values) == 0 {
		return nil
	}
	switch meta.Type(key) {
	case meta.TypeBool:
		return createMatchBoolFunc(values)
	case meta.TypeCredential:
		return matchNever
	case meta.TypeID, meta.TypeTimestamp: // ID and timestamp use the same layout
		return createMatchIDFunc(values)
	case meta.TypeIDSet:
		return createMatchIDSetFunc(values)
	case meta.TypeTagSet:
		return createMatchTagSetFunc(values)
	case meta.TypeWord:
		return createMatchWordFunc(values)
	case meta.TypeWordSet:
		return createMatchWordSetFunc(values)
	}
	return createMatchStringFunc(values)
}






func createMatchBoolFunc(values []opValue) matchFunc {
	preValues := make([]bool, 0, len(values))
	for _, v := range values {









		preValues = append(preValues, meta.BoolValue(v.value))




	}

	return func(value string) bool {
		bValue := meta.BoolValue(value)
		for _, v := range preValues {
			if bValue != v {
				return false
			}
		}
		return true
	}
}

func createMatchIDFunc(values []opValue) matchFunc {

	return func(value string) bool {
		for _, v := range values {
			if !strings.HasPrefix(value, v.value) {
				return false
			}
		}
		return true
	}
}

func createMatchIDSetFunc(values []opValue) matchFunc {
	idValues := preprocessSet(sliceToLower(values))
	return func(value string) bool {
		ids := meta.ListFromValue(value)
		for _, neededIDs := range idValues {
			for _, neededID := range neededIDs {
				if !matchAllID(ids, neededID.value) {
					return false
				}
			}
		}
		return true
	}
}

func matchAllID(zettelIDs []string, neededID string) bool {
	for _, zt := range zettelIDs {
		if strings.HasPrefix(zt, neededID) {
			return true
		}
	}
	return false
}

func createMatchTagSetFunc(values []opValue) matchFunc {
	tagValues := processTagSet(preprocessSet(sliceToLower(values)))
	return func(value string) bool {
		tags := meta.ListFromValue(value)
		// Remove leading '#' from each tag
		for i, tag := range tags {
			tags[i] = meta.CleanTag(tag)
		}
		for _, neededTags := range tagValues {
			for _, neededTag := range neededTags {
				if !matchAllTag(tags, neededTag.value, neededTag.equal) {
					return false
				}
			}
		}
		return true
	}
}

type tagQueryValue struct {
	value string
	equal bool // not equal == prefix
}

func processTagSet(valueSet [][]opValue) [][]tagQueryValue {
	result := make([][]tagQueryValue, len(valueSet))
	for i, values := range valueSet {
		tags := make([]tagQueryValue, len(values))
		for j, val := range values {
			if tval := val.value; tval != "" && tval[0] == '#' {
				tval = meta.CleanTag(tval)
				tags[j] = tagQueryValue{value: tval, equal: true}
			} else {
				tags[j] = tagQueryValue{value: tval, equal: false}
			}
		}
		result[i] = tags
	}
	return result
}

func matchAllTag(zettelTags []string, neededTag string, equal bool) bool {
	if equal {
		for _, zt := range zettelTags {
			if zt == neededTag {
				return true
			}
		}
	} else {
		for _, zt := range zettelTags {
			if strings.HasPrefix(zt, neededTag) {
				return true
			}
		}
	}
	return false
}

func createMatchWordFunc(values []opValue) matchFunc {
	values = sliceToLower(values)
	return func(value string) bool {
		value = strings.ToLower(value)
		for _, v := range values {
			if value != v.value {
				return false
			}
		}
		return true
	}
}

func createMatchWordSetFunc(values []opValue) matchFunc {
	wordValues := preprocessSet(sliceToLower(values))
	return func(value string) bool {
		words := meta.ListFromValue(value)
		for _, neededWords := range wordValues {
			for _, neededWord := range neededWords {
				if !matchAllWord(words, neededWord.value) {
					return false
				}
			}
		}
		return true
	}
}

func createMatchStringFunc(values []opValue) matchFunc {
	values = sliceToLower(values)
	return func(value string) bool {
		value = strings.ToLower(value)
		for _, v := range values {
			if !strings.Contains(value, v.value) {
				return false
			}
		}
		return true
	}
}

func makeSearchMetaMatchFunc(posSpecs, negSpecs []matchSpec, nomatch []string) MetaMatchFunc {
	return func(m *meta.Meta) bool {
		for _, key := range nomatch {
			if _, ok := getMeta(m, key); ok {
				return false
			}
		}
		for _, s := range posSpecs {
			if value, ok := getMeta(m, s.key); !ok || !s.match(value) {
				return false
			}
		}
		for _, s := range negSpecs {
			if s.match == nil {
				if _, ok := m.Get(s.key); ok {
					return false
				}
			} else if value, ok := getMeta(m, s.key); !ok || s.match(value) {
				return false
			}
		}
		return true
	}
}

func getMeta(m *meta.Meta, key string) (string, bool) {
	if key == api.KeyTags {
		return m.Get(api.KeyAllTags)
	}
	return m.Get(key)
}

func sliceToLower(sl []opValue) []opValue {
	result := make([]opValue, 0, len(sl))
	for _, s := range sl {
		result = append(result, opValue{
			value: strings.ToLower(s.value),
			op:    s.op,
		})







|
<
<
|
|
|
|
>





|




|













|







|














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


|
|







|
>

|
|







|
|


|
|
|








<
<
<
<
<
<
<
<
<
|
|






|
|
|








<
<
<
<
<
|
|

|



|

|







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|


|
|







|
|


|
|
|








|
|


|
|







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







65
66
67
68
69
70
71
72


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186









187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205





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

















223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
































264
265
266
267
268
269
270
		}
	}
	return posSpecs, negSpecs, nomatch
}

func countEmptyValues(values []expValue) (always, never int) {
	for _, v := range values {
		if v.value == "" {


			if v.negate {
				never++
			} else {
				always++
			}
		}
	}
	return always, never
}

func createPosNegMatchFunc(key string, values []expValue) (posMatch, negMatch matchValueFunc) {
	posValues := make([]opValue, 0, len(values))
	negValues := make([]opValue, 0, len(values))
	for _, val := range values {
		if val.negate {
			negValues = append(negValues, opValue{value: val.value, op: val.op.negate()})
		} else {
			posValues = append(posValues, opValue{value: val.value, op: val.op})
		}
	}
	return createMatchFunc(key, posValues), createMatchFunc(key, negValues)
}

// opValue is an expValue, but w/o the field "negate"
type opValue struct {
	value string
	op    compareOp
}

func createMatchFunc(key string, values []opValue) matchValueFunc {
	if len(values) == 0 {
		return nil
	}
	switch meta.Type(key) {
	case meta.TypeBool:
		return createMatchBoolFunc(values)
	case meta.TypeCredential:
		return matchValueNever
	case meta.TypeID, meta.TypeTimestamp: // ID and timestamp use the same layout
		return createMatchIDFunc(values)
	case meta.TypeIDSet:
		return createMatchIDSetFunc(values)
	case meta.TypeTagSet:
		return createMatchTagSetFunc(values)
	case meta.TypeWord:
		return createMatchWordFunc(values)
	case meta.TypeWordSet:
		return createMatchWordSetFunc(values)
	}
	return createMatchStringFunc(values)
}

type boolPredicate func(bool) bool

func boolSame(value bool) bool   { return value }
func boolNegate(value bool) bool { return value }

func createMatchBoolFunc(values []opValue) matchValueFunc {
	preds := make([]boolPredicate, len(values))
	for i, v := range values {
		positiveTest := false
		switch v.op {
		case cmpDefault, cmpEqual, cmpPrefix, cmpSuffix, cmpContains:
			positiveTest = true
		case cmpNotDefault, cmpNotEqual, cmpNoPrefix, cmpNoSuffix, cmpNotContains:
			// positiveTest = false
		default:
			panic(fmt.Sprintf("Unknown compare operation %d", v.op))
		}
		bValue := meta.BoolValue(v.value)
		if positiveTest == bValue {
			preds[i] = boolSame
		} else {
			preds[i] = boolNegate
		}
	}
	return func(value string) bool {
		bValue := meta.BoolValue(value)
		for _, pred := range preds {
			if !pred(bValue) {
				return false
			}
		}
		return true
	}
}

func createMatchIDFunc(values []opValue) matchValueFunc {
	preds := valuesToStringPredicates(values, cmpPrefix)
	return func(value string) bool {
		for _, pred := range preds {
			if !pred(value) {
				return false
			}
		}
		return true
	}
}

func createMatchIDSetFunc(values []opValue) matchValueFunc {
	predList := valuesToStringSetPredicates(preprocessSet(values), cmpPrefix)
	return func(value string) bool {
		ids := meta.ListFromValue(value)
		for _, preds := range predList {
			for _, pred := range preds {
				if !pred(ids) {
					return false
				}
			}
		}
		return true
	}
}










func createMatchTagSetFunc(values []opValue) matchValueFunc {
	predList := valuesToStringSetPredicates(processTagSet(preprocessSet(sliceToLower(values))), cmpEqual)
	return func(value string) bool {
		tags := meta.ListFromValue(value)
		// Remove leading '#' from each tag
		for i, tag := range tags {
			tags[i] = meta.CleanTag(tag)
		}
		for _, preds := range predList {
			for _, pred := range preds {
				if !pred(tags) {
					return false
				}
			}
		}
		return true
	}
}






func processTagSet(valueSet [][]opValue) [][]opValue {
	result := make([][]opValue, len(valueSet))
	for i, values := range valueSet {
		tags := make([]opValue, len(values))
		for j, val := range values {
			if tval := val.value; tval != "" && tval[0] == '#' {
				tval = meta.CleanTag(tval)
				tags[j] = opValue{value: tval, op: resolveDefaultOp(val.op, cmpEqual)}
			} else {
				tags[j] = opValue{value: tval, op: resolveDefaultOp(val.op, cmpPrefix)}
			}
		}
		result[i] = tags
	}
	return result
}


















func createMatchWordFunc(values []opValue) matchValueFunc {
	preds := valuesToStringPredicates(sliceToLower(values), cmpEqual)
	return func(value string) bool {
		value = strings.ToLower(value)
		for _, pred := range preds {
			if !pred(value) {
				return false
			}
		}
		return true
	}
}

func createMatchWordSetFunc(values []opValue) matchValueFunc {
	predsList := valuesToStringSetPredicates(preprocessSet(sliceToLower(values)), cmpEqual)
	return func(value string) bool {
		words := meta.ListFromValue(value)
		for _, preds := range predsList {
			for _, pred := range preds {
				if !pred(words) {
					return false
				}
			}
		}
		return true
	}
}

func createMatchStringFunc(values []opValue) matchValueFunc {
	preds := valuesToStringPredicates(sliceToLower(values), cmpContains)
	return func(value string) bool {
		value = strings.ToLower(value)
		for _, pred := range preds {
			if !pred(value) {
				return false
			}
		}
		return true
	}
}

































func sliceToLower(sl []opValue) []opValue {
	result := make([]opValue, 0, len(sl))
	for _, s := range sl {
		result = append(result, opValue{
			value: strings.ToLower(s.value),
			op:    s.op,
		})
331
332
333
334
335
336
337

338


































339


























340

















































341
342




343







344
345






		if len(valueElems) > 0 {
			result = append(result, valueElems)
		}
	}
	return result
}


func matchAllWord(zettelWords []string, neededWord string) bool {


































	for _, zw := range zettelWords {


























		if zw == neededWord {

















































			return true
		}




	}







	return false
}













>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>
>
>
>
|
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
		if len(valueElems) > 0 {
			result = append(result, valueElems)
		}
	}
	return result
}

type stringPredicate func(string) bool

func valuesToStringPredicates(values []opValue, defOp compareOp) []stringPredicate {
	result := make([]stringPredicate, len(values))
	for i, v := range values {
		opVal := v.value // loop variable is used in closure --> save needed value
		switch op := resolveDefaultOp(v.op, defOp); op {
		case cmpEqual:
			result[i] = func(metaVal string) bool { return metaVal == opVal }
		case cmpNotEqual:
			result[i] = func(metaVal string) bool { return metaVal != opVal }
		case cmpPrefix:
			result[i] = func(metaVal string) bool { return strings.HasPrefix(metaVal, opVal) }
		case cmpNoPrefix:
			result[i] = func(metaVal string) bool { return !strings.HasPrefix(metaVal, opVal) }
		case cmpSuffix:
			result[i] = func(metaVal string) bool { return strings.HasSuffix(metaVal, opVal) }
		case cmpNoSuffix:
			result[i] = func(metaVal string) bool { return !strings.HasSuffix(metaVal, opVal) }
		case cmpContains:
			result[i] = func(metaVal string) bool { return strings.Contains(metaVal, opVal) }
		case cmpNotContains:
			result[i] = func(metaVal string) bool { return !strings.Contains(metaVal, opVal) }
		default:
			panic(fmt.Sprintf("Unknown compare operation %d/%d with value %q", op, v.op, opVal))
		}
	}
	return result
}

type stringSetPredicate func(value []string) bool

func valuesToStringSetPredicates(values [][]opValue, defOp compareOp) [][]stringSetPredicate {
	result := make([][]stringSetPredicate, len(values))
	for i, val := range values {
		elemPreds := make([]stringSetPredicate, len(val))
		for j, v := range val {
			opVal := v.value // loop variable is used in closure --> save needed value
			switch op := resolveDefaultOp(v.op, defOp); op {
			case cmpEqual:
				elemPreds[j] = makeStringSetPredicate(opVal, stringEqual, true)
			case cmpNotEqual:
				elemPreds[j] = makeStringSetPredicate(opVal, stringEqual, false)
			case cmpPrefix:
				elemPreds[j] = makeStringSetPredicate(opVal, strings.HasPrefix, true)
			case cmpNoPrefix:
				elemPreds[j] = makeStringSetPredicate(opVal, strings.HasPrefix, false)
			case cmpSuffix:
				elemPreds[j] = makeStringSetPredicate(opVal, strings.HasSuffix, true)
			case cmpNoSuffix:
				elemPreds[j] = makeStringSetPredicate(opVal, strings.HasSuffix, false)
			case cmpContains:
				elemPreds[j] = makeStringSetPredicate(opVal, strings.Contains, true)
			case cmpNotContains:
				elemPreds[j] = makeStringSetPredicate(opVal, strings.Contains, false)
			default:
				panic(fmt.Sprintf("Unknown compare operation %d/%d with value %q", op, v.op, opVal))
			}
		}
		result[i] = elemPreds
	}
	return result
}

func stringEqual(val1, val2 string) bool { return val1 == val2 }

type compareStringFunc func(val1, val2 string) bool

func makeStringSetPredicate(neededValue string, compare compareStringFunc, foundResult bool) stringSetPredicate {
	return func(metaVals []string) bool {
		for _, metaVal := range metaVals {
			if compare(metaVal, neededValue) {
				return foundResult
			}
		}
		return !foundResult
	}
}

func resolveDefaultOp(op, defOp compareOp) compareOp {
	if op == cmpDefault {
		return defOp
	}
	if op == cmpNotDefault {
		return defOp.negate()
	}
	return op
}

func makeSearchMetaMatchFunc(posSpecs, negSpecs []matchSpec, nomatch []string) MetaMatchFunc {
	if len(nomatch) == 0 {
		// Optimize for simple cases: only negative or only positive matching

		if len(posSpecs) == 0 {
			return func(m *meta.Meta) bool { return matchMetaNegSpecs(m, negSpecs) }
		}
		if len(negSpecs) == 0 {
			return func(m *meta.Meta) bool { return matchMetaPosSpecs(m, posSpecs) }
		}
	}
	return func(m *meta.Meta) bool {
		return matchMetaNoMatch(m, nomatch) &&
			matchMetaPosSpecs(m, posSpecs) &&
			matchMetaNegSpecs(m, negSpecs)
	}
}

func matchMetaNoMatch(m *meta.Meta, nomatch []string) bool {
	for _, key := range nomatch {
		if _, ok := m.Get(key); ok {
			return false
		}
	}
	return true
}
func matchMetaPosSpecs(m *meta.Meta, posSpecs []matchSpec) bool {
	for _, s := range posSpecs {
		if value, ok := m.Get(s.key); !ok || !s.match(value) {
			return false
		}
	}
	return true
}
func matchMetaNegSpecs(m *meta.Meta, negSpecs []matchSpec) bool {
	for _, s := range negSpecs {
		if s.match == nil {
			if _, ok := m.Get(s.key); ok {
				return false
			}
		} else if value, ok := m.Get(s.key); !ok || !s.match(value) {
			return false
		}
	}
	return true
}

Added search/select_test.go.





























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

package search_test

import (
	"testing"

	"zettelstore.de/c/api"
	"zettelstore.de/z/domain/id"
	"zettelstore.de/z/domain/meta"
	"zettelstore.de/z/search"
)

func TestMatchZidNegate(t *testing.T) {
	var s *search.Search
	s = s.AddExpr(api.KeyID, "!="+string(api.ZidVersion))
	s = s.AddExpr(api.KeyID, "!="+string(api.ZidLicense))
	matchFunc := s.CompileMatch(nil)

	testCases := []struct {
		zid api.ZettelID
		exp bool
	}{
		{api.ZidVersion, false},
		{api.ZidLicense, false},
		{api.ZidAuthors, true},
	}
	for i, tc := range testCases {
		m := meta.New(id.MustParse(tc.zid))
		if matchFunc(m) != tc.exp {
			if tc.exp {
				t.Errorf("%d: meta %v must match %q", i, m.Zid, s)
			} else {
				t.Errorf("%d: meta %v must not match %q", i, m.Zid, s)
			}
		}
	}
}

Changes to testdata/testbox/20211020185400.zettel.

1
2
3
4
5
6
7
8
id: 20211020185400
title: Self embed zettel
role: zettel
syntax: zmk
modified: 20211020185512

{{#mark}}
[!mark] ABC




|


|
1
2
3
4
5
6
7
8
id: 20211020185400
title: Self embed zettel
role: zettel
syntax: zmk
modified: 20211119150218

{{#mark}}
[!mark] Home

Changes to tests/client/client_test.go.

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
		}

	}
}

func TestListZettel(t *testing.T) {
	const (
		ownerZettel      = 46
		configRoleZettel = 27
		writerZettel     = ownerZettel - 23
		readerZettel     = ownerZettel - 23
		creatorZettel    = 10
		publicZettel     = 7
	)

	testdata := []struct {
		user string
		exp  int
	}{
		{"", publicZettel},







|
|


|
|







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
		}

	}
}

func TestListZettel(t *testing.T) {
	const (
		ownerZettel      = 47
		configRoleZettel = 29
		writerZettel     = ownerZettel - 23
		readerZettel     = ownerZettel - 23
		creatorZettel    = 7
		publicZettel     = 4
	)

	testdata := []struct {
		user string
		exp  int
	}{
		{"", publicZettel},
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
		return
	}
	expQ := "role MATCH configuration"
	if q != expQ {
		t.Errorf("Query should be %q, but is %q", expQ, q)
	}
	got := len(l)
	if got != 27 {
		t.Errorf("List of length %d expected, but got %d\n%v", configRoleZettel, got, l)
	}

	pl, err := c.ListZettel(context.Background(), url.Values{api.KeyRole: {api.ValueRoleConfiguration}})
	if err != nil {
		t.Error(err)
		return







|







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
		return
	}
	expQ := "role MATCH configuration"
	if q != expQ {
		t.Errorf("Query should be %q, but is %q", expQ, q)
	}
	got := len(l)
	if got != configRoleZettel {
		t.Errorf("List of length %d expected, but got %d\n%v", configRoleZettel, got, l)
	}

	pl, err := c.ListZettel(context.Background(), url.Values{api.KeyRole: {api.ValueRoleConfiguration}})
	if err != nil {
		t.Error(err)
		return
290
291
292
293
294
295
296



















297
298
299
300
301
302
303
	if got := len(zl.Linked.Local); got != 1 {
		t.Errorf("Expected 1 local link, got %d", got)
	}
	if got := len(zl.Linked.External); got != 4 {
		t.Errorf("Expected 4 external link, got %d", got)
	}
}




















func TestListTags(t *testing.T) {
	t.Parallel()
	c := getClient()
	c.SetAuth("owner", "owner")
	tm, err := c.ListTags(context.Background())
	if err != nil {







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
	if got := len(zl.Linked.Local); got != 1 {
		t.Errorf("Expected 1 local link, got %d", got)
	}
	if got := len(zl.Linked.External); got != 4 {
		t.Errorf("Expected 4 external link, got %d", got)
	}
}

func TestGetUnlinkedReferences(t *testing.T) {
	t.Parallel()
	c := getClient()
	c.SetAuth("owner", "owner")
	zl, err := c.GetUnlinkedReferences(context.Background(), api.ZidDefaultHome, nil)
	if err != nil {
		t.Error(err)
		return
	}
	if !checkZid(t, api.ZidDefaultHome, zl.ID) {
		return
	}
	l := zl.List
	if got := len(l); got != 1 {
		t.Errorf("Expected list of length 1, got %d", got)
		return
	}
}

func TestListTags(t *testing.T) {
	t.Parallel()
	c := getClient()
	c.SetAuth("owner", "owner")
	tm, err := c.ListTags(context.Background())
	if err != nil {

Changes to usecase/get_user.go.

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
	ctx = box.NoEnrichContext(ctx)

	// It is important to try first with the owner. First, because another user
	// could give herself the same ''ident''. Second, in most cases the owner
	// will authenticate.
	identMeta, err := uc.port.GetMeta(ctx, uc.authz.Owner())
	if err == nil && identMeta.GetDefault(api.KeyUserID, "") == ident {
		if role, ok := identMeta.Get(api.KeyRole); !ok ||
			role != api.ValueRoleUser {
			return nil, nil
		}
		return identMeta, nil
	}
	// Owner was not found or has another ident. Try via list search.
	var s *search.Search
	s = s.AddExpr(api.KeyRole, api.ValueRoleUser)
	s = s.AddExpr(api.KeyUserID, ident)
	metaList, err := uc.port.SelectMeta(ctx, s)
	if err != nil {
		return nil, err
	}
	if len(metaList) < 1 {
		return nil, nil







<
<
<
<




<







47
48
49
50
51
52
53




54
55
56
57

58
59
60
61
62
63
64
	ctx = box.NoEnrichContext(ctx)

	// It is important to try first with the owner. First, because another user
	// could give herself the same ''ident''. Second, in most cases the owner
	// will authenticate.
	identMeta, err := uc.port.GetMeta(ctx, uc.authz.Owner())
	if err == nil && identMeta.GetDefault(api.KeyUserID, "") == ident {




		return identMeta, nil
	}
	// Owner was not found or has another ident. Try via list search.
	var s *search.Search

	s = s.AddExpr(api.KeyUserID, ident)
	metaList, err := uc.port.SelectMeta(ctx, s)
	if err != nil {
		return nil, err
	}
	if len(metaList) < 1 {
		return nil, nil

Added usecase/unlinked_refs.go.





































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

package usecase

import (
	"context"
	"strings"
	"unicode"

	"zettelstore.de/c/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/config"
	"zettelstore.de/z/domain"
	"zettelstore.de/z/domain/id"
	"zettelstore.de/z/domain/meta"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/evaluator"
	"zettelstore.de/z/parser"
	"zettelstore.de/z/search"
)

// UnlinkedReferencesPort is the interface used by this use case.
type UnlinkedReferencesPort interface {
	GetMeta(ctx context.Context, zid id.Zid) (*meta.Meta, error)
	GetZettel(ctx context.Context, zid id.Zid) (domain.Zettel, error)
	SelectMeta(ctx context.Context, s *search.Search) ([]*meta.Meta, error)
}

// UnlinkedReferences is the data for this use case.
type UnlinkedReferences struct {
	port     UnlinkedReferencesPort
	rtConfig config.Config
	encText  encoder.Encoder
}

// NewUnlinkedReferences creates a new use case.
func NewUnlinkedReferences(port UnlinkedReferencesPort, rtConfig config.Config) UnlinkedReferences {
	return UnlinkedReferences{
		port:     port,
		rtConfig: rtConfig,
		encText:  encoder.Create(api.EncoderText, nil),
	}
}

// Run executes the usecase with already evaluated title value.
func (uc *UnlinkedReferences) Run(ctx context.Context, title string, s *search.Search) ([]*meta.Meta, error) {
	words := makeWords(title)
	if len(words) == 0 {
		return nil, nil
	}
	for _, word := range words {
		s = s.AddExpr("", "="+word)
	}

	// Limit applies to the filtering process, not to SelectMeta
	limit := s.GetLimit()
	s = s.SetLimit(0)

	candidates, err := uc.port.SelectMeta(ctx, s)
	if err != nil {
		return nil, err
	}
	s = s.SetLimit(limit) // Restore limit
	return s.Limit(uc.filterCandidates(ctx, candidates, words)), nil
}

func makeWords(text string) []string {
	return strings.FieldsFunc(text, func(r rune) bool {
		return unicode.In(r, unicode.C, unicode.P, unicode.Z)
	})
}

func (uc *UnlinkedReferences) filterCandidates(ctx context.Context, candidates []*meta.Meta, words []string) []*meta.Meta {
	result := make([]*meta.Meta, 0, len(candidates))
candLoop:
	for _, cand := range candidates {
		zettel, err := uc.port.GetZettel(ctx, cand.Zid)
		if err != nil {
			continue
		}
		v := unlinkedVisitor{
			words: words,
			found: false,
		}
		v.text = v.joinWords(words)

		for _, pair := range zettel.Meta.Pairs(false) {
			if meta.Type(pair.Key) != meta.TypeZettelmarkup {
				continue
			}
			iln := parser.ParseMetadata(pair.Value)
			evaluator.EvaluateInline(ctx, uc.port, nil, uc.rtConfig, iln)
			ast.Walk(&v, iln)
			if v.found {
				result = append(result, cand)
				continue candLoop
			}
		}

		syntax := zettel.Meta.GetDefault(api.KeySyntax, "")
		if !parser.IsTextParser(syntax) {
			continue
		}
		zn, err := parser.ParseZettel(zettel, syntax, nil), nil
		if err != nil {
			continue
		}
		evaluator.EvaluateZettel(ctx, uc.port, nil, uc.rtConfig, zn)
		ast.Walk(&v, zn.Ast)
		if v.found {
			result = append(result, cand)
		}
	}
	return result
}

func (*unlinkedVisitor) joinWords(words []string) string {
	return " " + strings.ToLower(strings.Join(words, " ")) + " "
}

type unlinkedVisitor struct {
	words []string
	text  string
	found bool
}

func (v *unlinkedVisitor) Visit(node ast.Node) ast.Visitor {
	switch n := node.(type) {
	case *ast.InlineListNode:
		v.checkWords(n)
		return nil
	case *ast.HeadingNode:
		return nil
	case *ast.LinkNode, *ast.EmbedNode, *ast.CiteNode:
		return nil
	}
	return v
}

func (v *unlinkedVisitor) checkWords(iln *ast.InlineListNode) {
	if len(iln.List) < 2*len(v.words)-1 {
		return
	}
	for _, text := range v.splitInlineTextList(iln) {
		if strings.Contains(text, v.text) {
			v.found = true
		}
	}
}

func (v *unlinkedVisitor) splitInlineTextList(iln *ast.InlineListNode) []string {
	var result []string
	var curList []string
	for _, in := range iln.List {
		switch n := in.(type) {
		case *ast.TextNode:
			curList = append(curList, makeWords(n.Text)...)
		case *ast.SpaceNode:
		default:
			if curList != nil {
				result = append(result, v.joinWords(curList))
				curList = nil
			}
		}
	}
	if curList != nil {
		result = append(result, v.joinWords(curList))
	}
	return result
}

Changes to web/adapter/api/get_eval_zettel.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package api provides api handlers for web requests.
package api

import (
	"net/http"

	"zettelstore.de/c/api"
	"zettelstore.de/z/ast"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package api

import (
	"net/http"

	"zettelstore.de/c/api"
	"zettelstore.de/z/ast"

Added web/adapter/api/get_unlinked_refs.go.

































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
76
77
78
79
80
//-----------------------------------------------------------------------------
// Copyright (c) 2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

package api

import (
	"net/http"
	"strings"

	"zettelstore.de/c/api"
	"zettelstore.de/z/domain/id"
	"zettelstore.de/z/encoder"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
)

// MakeListUnlinkedMetaHandler creates a new HTTP handler for the use case "list unlinked references".
func MakeListUnlinkedMetaHandler(
	getMeta usecase.GetMeta,
	unlinkedRefs usecase.UnlinkedReferences,
	evaluate *usecase.Evaluate,
) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		zid, err := id.Parse(r.URL.Path[1:])
		if err != nil {
			http.NotFound(w, r)
			return
		}
		ctx := r.Context()
		zm, err := getMeta.Run(ctx, zid)
		if err != nil {
			adapter.ReportUsecaseError(w, err)
			return
		}

		q := r.URL.Query()
		phrase := q.Get(api.QueryKeyPhrase)
		if phrase == "" {
			zmkTitle := zm.GetDefault(api.KeyTitle, "")
			ilnTitle := evaluate.RunMetadata(ctx, zmkTitle, nil)
			encdr := encoder.Create(api.EncoderText, nil)
			var b strings.Builder
			_, err = encdr.WriteInlines(&b, ilnTitle)
			if err == nil {
				phrase = b.String()
			}
		}

		metaList, err := unlinkedRefs.Run(
			ctx, phrase, adapter.AddUnlinkedRefsToSearch(adapter.GetSearch(q), zm))
		if err != nil {
			adapter.ReportUsecaseError(w, err)
			return
		}

		result := api.ZidMetaRelatedList{
			ID:   api.ZettelID(zid.String()),
			Meta: zm.Map(),
			List: make([]api.ZidMetaJSON, 0, len(metaList)),
		}
		for _, m := range metaList {
			result.List = append(result.List, api.ZidMetaJSON{
				ID:   api.ZettelID(m.Zid.String()),
				Meta: m.Map(),
			})
		}

		adapter.PrepareHeader(w, ctJSON)
		if err = encodeJSONData(w, result); err != nil {
			adapter.InternalServerError(w, "Write unlinked references JSON", err)
		}
	}
}

Changes to web/adapter/api/get_zettel_list.go.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// under this license.
//-----------------------------------------------------------------------------

// Package api provides api handlers for web requests.
package api

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

	"zettelstore.de/c/api"
	"zettelstore.de/z/config"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"







<







8
9
10
11
12
13
14

15
16
17
18
19
20
21
// under this license.
//-----------------------------------------------------------------------------

// Package api provides api handlers for web requests.
package api

import (

	"fmt"
	"net/http"

	"zettelstore.de/c/api"
	"zettelstore.de/z/config"
	"zettelstore.de/z/usecase"
	"zettelstore.de/z/web/adapter"
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
		s := adapter.GetSearch(q)
		metaList, err := listMeta.Run(ctx, s)
		if err != nil {
			adapter.ReportUsecaseError(w, err)
			return
		}

		var queryText bytes.Buffer
		if s != nil {
			s.Print(&queryText)
		}

		result := make([]api.ZidMetaJSON, 0, len(metaList))
		for _, m := range metaList {
			result = append(result, api.ZidMetaJSON{
				ID:   api.ZettelID(m.Zid.String()),
				Meta: m.Map(),
			})
		}

		adapter.PrepareHeader(w, ctJSON)
		err = encodeJSONData(w, api.ZettelListJSON{
			Query: queryText.String(),
			List:  result,
		})
		if err != nil {
			adapter.InternalServerError(w, "Write Zettel list JSON", err)
		}
	}
}







<
<
<
<
<










|







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
		s := adapter.GetSearch(q)
		metaList, err := listMeta.Run(ctx, s)
		if err != nil {
			adapter.ReportUsecaseError(w, err)
			return
		}






		result := make([]api.ZidMetaJSON, 0, len(metaList))
		for _, m := range metaList {
			result = append(result, api.ZidMetaJSON{
				ID:   api.ZettelID(m.Zid.String()),
				Meta: m.Map(),
			})
		}

		adapter.PrepareHeader(w, ctJSON)
		err = encodeJSONData(w, api.ZettelListJSON{
			Query: s.String(),
			List:  result,
		})
		if err != nil {
			adapter.InternalServerError(w, "Write Zettel list JSON", err)
		}
	}
}

Changes to web/adapter/request.go.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package adapter provides handlers for web requests.
package adapter

import (
	"log"
	"net/http"
	"net/url"
	"strconv"










<







1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package adapter

import (
	"log"
	"net/http"
	"net/url"
	"strconv"
157
158
159
160
161
162
163

















	case api.DirBackward:
		return usecase.ZettelContextBackward
	case api.DirForward:
		return usecase.ZettelContextForward
	}
	return usecase.ZettelContextBoth
}
























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
	case api.DirBackward:
		return usecase.ZettelContextBackward
	case api.DirForward:
		return usecase.ZettelContextForward
	}
	return usecase.ZettelContextBoth
}

// AddUnlinkedRefsToSearch inspects metadata and enhances the given search to ignore
// some zettel identifier.
func AddUnlinkedRefsToSearch(s *search.Search, m *meta.Meta) *search.Search {
	s = s.AddExpr(api.KeyID, "!="+m.Zid.String())
	for _, pair := range m.PairsRest(true) {
		switch meta.Type(pair.Key) {
		case meta.TypeID:
			s = s.AddExpr(api.KeyID, "!="+pair.Value)
		case meta.TypeIDSet:
			for _, value := range meta.ListFromValue(pair.Value) {
				s = s.AddExpr(api.KeyID, "!="+value)
			}
		}
	}
	return s
}

Changes to web/adapter/webui/get_info.go.

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
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------

// Package webui provides web-UI handlers for web requests.
package webui

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


	"zettelstore.de/c/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/collect"
	"zettelstore.de/z/config"
	"zettelstore.de/z/domain"










<








>







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
//-----------------------------------------------------------------------------
// Copyright (c) 2020-2021 Detlef Stern
//
// This file is part of zettelstore.
//
// Zettelstore 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.
//-----------------------------------------------------------------------------


package webui

import (
	"bytes"
	"context"
	"fmt"
	"net/http"
	"sort"
	"strings"

	"zettelstore.de/c/api"
	"zettelstore.de/z/ast"
	"zettelstore.de/z/box"
	"zettelstore.de/z/collect"
	"zettelstore.de/z/config"
	"zettelstore.de/z/domain"
43
44
45
46
47
48
49

50
51
52
53
54
55
56

// MakeGetInfoHandler creates a new HTTP handler for the use case "get zettel".
func (wui *WebUI) MakeGetInfoHandler(
	parseZettel usecase.ParseZettel,
	evaluate *usecase.Evaluate,
	getMeta usecase.GetMeta,
	getAllMeta usecase.GetAllMeta,

) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		q := r.URL.Query()
		if enc, encText := adapter.GetEncoding(r, q, api.EncoderHTML); enc != api.EncoderHTML {
			wui.reportError(ctx, w, adapter.NewErrBadRequest(
				fmt.Sprintf("Zettel info not available in encoding %q", encText)))







>







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

// MakeGetInfoHandler creates a new HTTP handler for the use case "get zettel".
func (wui *WebUI) MakeGetInfoHandler(
	parseZettel usecase.ParseZettel,
	evaluate *usecase.Evaluate,
	getMeta usecase.GetMeta,
	getAllMeta usecase.GetAllMeta,
	unlinkedRefs usecase.UnlinkedReferences,
) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		q := r.URL.Query()
		if enc, encText := adapter.GetEncoding(r, q, api.EncoderHTML); enc != api.EncoderHTML {
			wui.reportError(ctx, w, adapter.NewErrBadRequest(
				fmt.Sprintf("Zettel info not available in encoding %q", encText)))
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

		zn, err := parseZettel.Run(ctx, zid, q.Get(api.KeySyntax))
		if err != nil {
			wui.reportError(ctx, w, err)
			return
		}

		summary := collect.References(zn)
		locLinks, extLinks := splitLocExtLinks(append(summary.Links, summary.Embeds...))

		envEval := evaluator.Environment{
			GetTagRef: func(s string) *ast.Reference {
				return adapter.CreateTagReference(wui, 'h', api.EncodingHTML, s)
			},
			GetHostedRef: func(s string) *ast.Reference {
				return adapter.CreateHostedReference(wui, s)
			},







<
<
<







66
67
68
69
70
71
72



73
74
75
76
77
78
79

		zn, err := parseZettel.Run(ctx, zid, q.Get(api.KeySyntax))
		if err != nil {
			wui.reportError(ctx, w, err)
			return
		}




		envEval := evaluator.Environment{
			GetTagRef: func(s string) *ast.Reference {
				return adapter.CreateTagReference(wui, 'h', api.EncodingHTML, s)
			},
			GetHostedRef: func(s string) *ast.Reference {
				return adapter.CreateHostedReference(wui, s)
			},
98
99
100
101
102
103
104

















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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163



164
165
166
167
168
169
170
				getTextTitle,
				func(val string) *ast.InlineListNode {
					return evaluate.RunMetadata(ctx, val, &envEval)
				},
				&envHTML)
			metaData[i] = metaDataInfo{p.Key, buf.String()}
		}

















		shadowLinks := getShadowLinks(ctx, zid, getAllMeta)
		endnotes, err := encodeBlocks(&ast.BlockListNode{}, api.EncoderHTML, &envHTML)
		if err != nil {
			endnotes = ""
		}

		textTitle := wui.encodeTitleAsText(ctx, zn.InhMeta, evaluate)
		user := wui.getUser(ctx)
		canCreate := wui.canCreate(ctx, user)
		apiZid := api.ZettelID(zid.String())
		var base baseData
		wui.makeBaseData(ctx, lang, textTitle, user, &base)
		wui.renderTemplate(ctx, w, id.InfoTemplateZid, &base, struct {
			Zid            string
			WebURL         string
			ContextURL     string
			CanWrite       bool
			EditURL        string
			CanFolge       bool
			FolgeURL       string
			CanCopy        bool
			CopyURL        string
			CanRename      bool
			RenameURL      string
			CanDelete      bool
			DeleteURL      string
			MetaData       []metaDataInfo
			HasLinks       bool
			HasLocLinks    bool
			LocLinks       []localLink
			HasExtLinks    bool
			ExtLinks       []string
			ExtNewWindow   string



			EvalMatrix     []matrixLine
			ParseMatrix    []matrixLine
			HasShadowLinks bool
			ShadowLinks    []string
			Endnotes       string
		}{
			Zid:            zid.String(),
			WebURL:         wui.NewURLBuilder('h').SetZid(apiZid).String(),
			ContextURL:     wui.NewURLBuilder('k').SetZid(apiZid).String(),
			CanWrite:       wui.canWrite(ctx, user, zn.Meta, zn.Content),
			EditURL:        wui.NewURLBuilder('e').SetZid(apiZid).String(),
			CanFolge:       canCreate,
			FolgeURL:       wui.NewURLBuilder('f').SetZid(apiZid).String(),
			CanCopy:        canCreate && !zn.Content.IsBinary(),
			CopyURL:        wui.NewURLBuilder('c').SetZid(apiZid).String(),
			CanRename:      wui.canRename(ctx, user, zn.Meta),
			RenameURL:      wui.NewURLBuilder('b').SetZid(apiZid).String(),
			CanDelete:      wui.canDelete(ctx, user, zn.Meta),
			DeleteURL:      wui.NewURLBuilder('d').SetZid(apiZid).String(),
			MetaData:       metaData,
			HasLinks:       len(extLinks)+len(locLinks) > 0,
			HasLocLinks:    len(locLinks) > 0,
			LocLinks:       locLinks,
			HasExtLinks:    len(extLinks) > 0,
			ExtLinks:       extLinks,
			ExtNewWindow:   htmlAttrNewWindow(len(extLinks) > 0),



			EvalMatrix:     wui.infoAPIMatrix('v', zid),
			ParseMatrix:    wui.infoAPIMatrixPlain('p', zid),
			HasShadowLinks: len(shadowLinks) > 0,
			ShadowLinks:    shadowLinks,
			Endnotes:       endnotes,
		})
	}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






<




















<





>
>
>




















<





>
>
>







96
97
98
99
100
101
102
103
104
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

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
				getTextTitle,
				func(val string) *ast.InlineListNode {
					return evaluate.RunMetadata(ctx, val, &envEval)
				},
				&envHTML)
			metaData[i] = metaDataInfo{p.Key, buf.String()}
		}
		summary := collect.References(zn)
		locLinks, extLinks := splitLocExtLinks(append(summary.Links, summary.Embeds...))

		textTitle := wui.encodeTitleAsText(ctx, zn.InhMeta, evaluate)
		phrase := q.Get(api.QueryKeyPhrase)
		if phrase == "" {
			phrase = textTitle
		}
		phrase = strings.TrimSpace(phrase)
		unlinkedMeta, err := unlinkedRefs.Run(
			ctx, phrase, adapter.AddUnlinkedRefsToSearch(nil, zn.InhMeta))
		if err != nil {
			wui.reportError(ctx, w, err)
			return
		}
		unLinks := wui.buildHTMLMetaList(ctx, unlinkedMeta, evaluate)

		shadowLinks := getShadowLinks(ctx, zid, getAllMeta)
		endnotes, err := encodeBlocks(&ast.BlockListNode{}, api.EncoderHTML, &envHTML)
		if err != nil {
			endnotes = ""
		}


		user := wui.getUser(ctx)
		canCreate := wui.canCreate(ctx, user)
		apiZid := api.ZettelID(zid.String())
		var base baseData
		wui.makeBaseData(ctx, lang, textTitle, user, &base)
		wui.renderTemplate(ctx, w, id.InfoTemplateZid, &base, struct {
			Zid            string
			WebURL         string
			ContextURL     string
			CanWrite       bool
			EditURL        string
			CanFolge       bool
			FolgeURL       string
			CanCopy        bool
			CopyURL        string
			CanRename      bool
			RenameURL      string
			CanDelete      bool
			DeleteURL      string
			MetaData       []metaDataInfo

			HasLocLinks    bool
			LocLinks       []localLink
			HasExtLinks    bool
			ExtLinks       []string
			ExtNewWindow   string
			UnLinks        []simpleLink
			UnLinksPhrase  string
			QueryKeyPhrase string
			EvalMatrix     []matrixLine
			ParseMatrix    []matrixLine
			HasShadowLinks bool
			ShadowLinks    []string
			Endnotes       string
		}{
			Zid:            zid.String(),
			WebURL:         wui.NewURLBuilder('h').SetZid(apiZid).String(),
			ContextURL:     wui.NewURLBuilder('k').SetZid(apiZid).String(),
			CanWrite:       wui.canWrite(ctx, user, zn.Meta, zn.Content),
			EditURL:        wui.NewURLBuilder('e').SetZid(apiZid).String(),
			CanFolge:       canCreate,
			FolgeURL:       wui.NewURLBuilder('f').SetZid(apiZid).String(),
			CanCopy:        canCreate && !zn.Content.IsBinary(),
			CopyURL:        wui.NewURLBuilder('c').SetZid(apiZid).String(),
			CanRename:      wui.canRename(ctx, user, zn.Meta),
			RenameURL:      wui.NewURLBuilder('b').SetZid(apiZid).String(),
			CanDelete:      wui.canDelete(ctx, user, zn.Meta),
			DeleteURL:      wui.NewURLBuilder('d').SetZid(apiZid).String(),
			MetaData:       metaData,

			HasLocLinks:    len(locLinks) > 0,
			LocLinks:       locLinks,
			HasExtLinks:    len(extLinks) > 0,
			ExtLinks:       extLinks,
			ExtNewWindow:   htmlAttrNewWindow(len(extLinks) > 0),
			UnLinks:        unLinks,
			UnLinksPhrase:  phrase,
			QueryKeyPhrase: api.QueryKeyPhrase,
			EvalMatrix:     wui.infoAPIMatrix('v', zid),
			ParseMatrix:    wui.infoAPIMatrixPlain('p', zid),
			HasShadowLinks: len(shadowLinks) > 0,
			ShadowLinks:    shadowLinks,
			Endnotes:       endnotes,
		})
	}

Changes to web/adapter/webui/lists.go.

181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
			redirectFound(w, r, wui.NewURLBuilder('h'))
			return
		}

		title := wui.listTitleSearch("Search", s)
		wui.renderMetaList(
			ctx, w, title, s, func(s *search.Search) ([]*meta.Meta, error) {
				if !s.EnrichNeeded() {
					ctx = box.NoEnrichContext(ctx)
				}
				return ucSearch.Run(ctx, s)
			},
			evaluate,
		)
	}
}








<
<
<







181
182
183
184
185
186
187



188
189
190
191
192
193
194
			redirectFound(w, r, wui.NewURLBuilder('h'))
			return
		}

		title := wui.listTitleSearch("Search", s)
		wui.renderMetaList(
			ctx, w, title, s, func(s *search.Search) ([]*meta.Meta, error) {



				return ucSearch.Run(ctx, s)
			},
			evaluate,
		)
	}
}

Changes to www/changes.wiki.

1
2
3
4





















5
6
7




8
9
10
11
12
13
14
15
16
17
18
19
20
21
<title>Change Log</title>

<a name="0_2"></a>
<h2>Changes for Version 0.2 (pending)</h2>






















<a name="0_1"></a>
<h2>Changes for Version 0.1 (2021-11-11)</h2>




  *  Remove visual Zettelmarkup (italic, underline). Semantic Zettelmarkup
     (emphasize, insert) is still allowed, but got a different syntax. The new
     syntax for <ins>inserted text</ins> is <tt>&gt;&gt;inserted&gt;&gt;</tt>,
     while its previous syntax now denotes <em>emphasized text</em>:
     <tt>__emphasized__</tt>. The previous syntax for emphasized text is now
     deprecated: <tt>//deprecated emphasized//</tt>. Starting with
     Version&nbsp;0.0.17, the deprecated syntax will not be supported. The
     reason is the collision with URLs that also contain the characters
     <tt>//</tt>. The ZMK encoding of a zettel may help with the transition
     (<tt>/v/{ZettelID}?_part=zettel&amp;_enc=zmk</tt>, on the Info page of
     each zettel in the WebUI). Additionally, all deprecated uses of
     <tt>//</tt> will be rendered with a dashed box within the WebUI.
     (breaking: Zettelmarkup).
  *  API client software is now a [https://zettelstore.de/client/|separate]


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

|

>
>
>
>






|







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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<title>Change Log</title>

<a name="0_2_0"></a>
<h2>Changes for Version 0.2.0 (pending)</h2>
  *  Remove support for <tt>;;small text;;</tt> Zettelmarkup.
     (breaking: Zettelmarkup)
  *  Selecting zettel based on their metadata allows the same syntax as
     searching for zettel content. For example, you can list all zettel that
     have an identifier not ending with <tt>00</tt> by using the query
     <tt>id=!&lt;00</tt>.
     (minor: api, webui)
  *  Show unlinked references in info page of each zettel. Unlinked references
     are phrases within zettel content that might reference another zettel with
     the same title as the phase.
     (major: webui)
  *  Add endpoint <tt>/u/{ID}</tt> to retrieve unlinked references.
     (major: api)
  *  Remove support for <tt>//deprecated emphasized//</tt> Zettelmarkup.
     (minor: Zettelmarkup)
  *  Add options to profile the software. Profiling can be enabled at the
     command line or via the administrator console.
     (minor)
  *  Add computed zettel that lists all supported parser / recognized zettel
     syntaxes.
     (minor)

<a name="0_1"></a><a name="0_1_0"></a>
<h2>Changes for Version 0.1 (2021-11-11)</h2>
  *  v0.1.2 (2021-11-18) fixes a bug when selecting zettel from a list when
     more than one comparison is negated.
  *  v0.1.1 (2021-11-12) updates the documentation, mostly related to the
     deprecation of the <tt>//</tt> markup.
  *  Remove visual Zettelmarkup (italic, underline). Semantic Zettelmarkup
     (emphasize, insert) is still allowed, but got a different syntax. The new
     syntax for <ins>inserted text</ins> is <tt>&gt;&gt;inserted&gt;&gt;</tt>,
     while its previous syntax now denotes <em>emphasized text</em>:
     <tt>__emphasized__</tt>. The previous syntax for emphasized text is now
     deprecated: <tt>//deprecated emphasized//</tt>. Starting with
     Version&nbsp;0.2.0, the deprecated syntax will not be supported. The
     reason is the collision with URLs that also contain the characters
     <tt>//</tt>. The ZMK encoding of a zettel may help with the transition
     (<tt>/v/{ZettelID}?_part=zettel&amp;_enc=zmk</tt>, on the Info page of
     each zettel in the WebUI). Additionally, all deprecated uses of
     <tt>//</tt> will be rendered with a dashed box within the WebUI.
     (breaking: Zettelmarkup).
  *  API client software is now a [https://zettelstore.de/client/|separate]
48
49
50
51
52
53
54
55
56
57
58
59
60
61


62
63
64
65
66
67
68
     uncover zettel in overlay box.
     (minor: webui)
  *  Fix: do not allow control characters in JSON-based creating/updating API.
     Otherwise, the created / updated zettel might not be parseable by the
     software (but still by a human). In certain cases, even the WebUI might be
     affected.
     (minor: api, webui)
  *  Fix: when a very log word (longer than width of browser window) is given,
     still allow to scroll horizontally.
     (minor: webui)
  *  Separate repository for [https://zettelstore.de/contrib/|contributed]
     software. First entry is a software for creating a presentation by using
     zettel.
     (info)



<a name="0_0_15"></a>
<h2>Changes for Version 0.0.15 (2021-09-17)</h2>
  *  Move again endpoint characters for authentication to make room for future
     features. WebUI authentication moves from <tt>/a</tt> to <tt>/i</tt>
     (login) and <tt>/i?logout</tt> (logout). API authentication moves from
     <tt>/v</tt> to </tt>/a</tt>. JSON-based basic zettel handling moves from







|






>
>







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
     uncover zettel in overlay box.
     (minor: webui)
  *  Fix: do not allow control characters in JSON-based creating/updating API.
     Otherwise, the created / updated zettel might not be parseable by the
     software (but still by a human). In certain cases, even the WebUI might be
     affected.
     (minor: api, webui)
  *  Fix: when a very long word (longer than width of browser window) is given,
     still allow to scroll horizontally.
     (minor: webui)
  *  Separate repository for [https://zettelstore.de/contrib/|contributed]
     software. First entry is a software for creating a presentation by using
     zettel.
     (info)
  *  Many smaller bug fixes and inprovements, to the software and to the
     documentation.

<a name="0_0_15"></a>
<h2>Changes for Version 0.0.15 (2021-09-17)</h2>
  *  Move again endpoint characters for authentication to make room for future
     features. WebUI authentication moves from <tt>/a</tt> to <tt>/i</tt>
     (login) and <tt>/i?logout</tt> (logout). API authentication moves from
     <tt>/v</tt> to </tt>/a</tt>. JSON-based basic zettel handling moves from

Changes to www/download.wiki.

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
<title>Download</title>
<h1>Download of Zettelstore Software</h1>
<h2>Foreword</h2>
  *  Zettelstore is free/libre open source software, licensed under EUPL-1.2-or-later.
  *  The software is provided as-is.
  *  There is no guarantee that it will not damage your system.
  *  However, it is in use by the main developer since March 2020 without any damage.
  *  It may be useful for you. It is useful for me.
  *  Take a look at the [https://zettelstore.de/manual/|manual] to know how to start and use it.

<h2>ZIP-ped Executables</h2>
Build: <code>v0.1</code> (2021-11-11).

  *  [/uv/zettelstore-0.1-linux-amd64.zip|Linux] (amd64)
  *  [/uv/zettelstore-0.1-linux-arm.zip|Linux] (arm6, e.g. Raspberry Pi)
  *  [/uv/zettelstore-0.1-windows-amd64.zip|Windows] (amd64)
  *  [/uv/zettelstore-0.1-darwin-amd64.zip|macOS] (amd64)
  *  [/uv/zettelstore-0.1-darwin-arm64.zip|macOS] (arm64, aka Apple silicon)

Unzip the appropriate file, install and execute Zettelstore according to the manual.

<h2>Zettel for the manual</h2>
As a starter, you can download the zettel for the manual
[/uv/manual-0.1.zip|here]. Just unzip the contained files and put them into
your zettel folder or configure a file box to read the zettel directly from the
ZIP file.











|

|
|
|
|
|





|


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
<title>Download</title>
<h1>Download of Zettelstore Software</h1>
<h2>Foreword</h2>
  *  Zettelstore is free/libre open source software, licensed under EUPL-1.2-or-later.
  *  The software is provided as-is.
  *  There is no guarantee that it will not damage your system.
  *  However, it is in use by the main developer since March 2020 without any damage.
  *  It may be useful for you. It is useful for me.
  *  Take a look at the [https://zettelstore.de/manual/|manual] to know how to start and use it.

<h2>ZIP-ped Executables</h2>
Build: <code>v0.1.2</code> (2021-11-18).

  *  [/uv/zettelstore-0.1.2-linux-amd64.zip|Linux] (amd64)
  *  [/uv/zettelstore-0.1.2-linux-arm.zip|Linux] (arm6, e.g. Raspberry Pi)
  *  [/uv/zettelstore-0.1.2-windows-amd64.zip|Windows] (amd64)
  *  [/uv/zettelstore-0.1.2-darwin-amd64.zip|macOS] (amd64)
  *  [/uv/zettelstore-0.1.2-darwin-arm64.zip|macOS] (arm64, aka Apple silicon)

Unzip the appropriate file, install and execute Zettelstore according to the manual.

<h2>Zettel for the manual</h2>
As a starter, you can download the zettel for the manual
[/uv/manual-0.1.2.zip|here]. Just unzip the contained files and put them into
your zettel folder or configure a file box to read the zettel directly from the
ZIP file.

Changes to www/index.wiki.

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
access Zettelstore via its API more easily,
[https://zettelstore.de/contrib|Zettelstore Contrib] contains contributed
software, which often connects to Zettelstore via its API. Some of the software
packages may be experimental.

[https://twitter.com/zettelstore|Stay tuned]&hellip;
<hr>
<h3>Latest Release: 0.1 (2021-11-11)</h3>
  *  [./download.wiki|Download]
  *  [./changes.wiki#0_1|Change summary]
  *  [/timeline?p=v0.1&bt=v0.0.15&y=ci|Check-ins for version 0.1],
     [/vdiff?to=v0.1&from=v0.0.15|content diff]
  *  [/timeline?df=v0.1&y=ci|Check-ins derived from the 0.1 release],
     [/vdiff?from=v0.1&to=trunk|content diff]
  *  [./plan.wiki|Limitations and planned improvements]
  *  [/timeline?t=release|Timeline of all past releases]

<hr>
<h2>Build instructions</h2>
Just install [https://golang.org/dl/|Go] and some Go-based tools. Please read
the [./build.md|instructions] for details.

  *  [/dir?ci=trunk|Source code]
  *  [/download|Download the source code] as a tarball or a ZIP file
     (you must [/login|login] as user &quot;anonymous&quot;).







|


|
|













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
access Zettelstore via its API more easily,
[https://zettelstore.de/contrib|Zettelstore Contrib] contains contributed
software, which often connects to Zettelstore via its API. Some of the software
packages may be experimental.

[https://twitter.com/zettelstore|Stay tuned]&hellip;
<hr>
<h3>Latest Release: 0.1.2 (2021-11-18)</h3>
  *  [./download.wiki|Download]
  *  [./changes.wiki#0_1|Change summary]
  *  [/timeline?p=v0.1.2&bt=v0.0.15&y=ci|Check-ins for version 0.1.2],
     [/vdiff?to=v0.1.2&from=v0.0.15|content diff]
  *  [/timeline?df=v0.1&y=ci|Check-ins derived from the 0.1 release],
     [/vdiff?from=v0.1&to=trunk|content diff]
  *  [./plan.wiki|Limitations and planned improvements]
  *  [/timeline?t=release|Timeline of all past releases]

<hr>
<h2>Build instructions</h2>
Just install [https://golang.org/dl/|Go] and some Go-based tools. Please read
the [./build.md|instructions] for details.

  *  [/dir?ci=trunk|Source code]
  *  [/download|Download the source code] as a tarball or a ZIP file
     (you must [/login|login] as user &quot;anonymous&quot;).