Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From v0.16.0 To v0.17.0
|
2024-03-06
| ||
| 15:02 |
Increase version to 0.18.0-dev to begin next development cycle... (check-in: 51c141a192 user: stern tags: trunk) | |
|
2024-03-04
| ||
| 17:08 | Version 0.17.0 ... (check-in: c863ee5f61 user: stern tags: trunk, release, v0.17.0) | |
| 13:47 | Adapt to sx changes; add SPDX license identifiers ... (check-in: 5485ba3ce3 user: stern tags: trunk) | |
|
2023-12-28
| ||
| 16:41 | Fix sxn code that removed role-based customization, esp. for an additional action if role is "tag" ... (check-in: e721174596 user: t73fde tags: release-0.16) | |
|
2023-11-30
| ||
| 18:10 | Increase version to 0.17.0-dev to begin next development cycle ... (check-in: 9d654c5606 user: stern tags: trunk) | |
| 16:08 | Version 0.16.0 ... (check-in: a5afffaf5f user: stern tags: trunk, release, v0.16.0) | |
|
2023-11-29
| ||
| 16:53 | Add predefined role zettel ... (check-in: 514e400b28 user: stern tags: trunk) | |
Changes to .fossil-settings/ignore-glob.
1 2 | bin/* releases/* | < | 1 2 | bin/* releases/* |
Changes to Makefile.
1 2 3 4 5 6 7 8 9 | ## Copyright (c) 2020-present 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. | | | | | | | | | | 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 | ## Copyright (c) 2020-present 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. .PHONY: check relcheck api version build release clean check: go run tools/check/check.go relcheck: go run tools/check/check.go -r api: go run tools/testapi/testapi.go version: @echo $(shell go run tools/build/build.go version) build: go run tools/build/build.go build release: go run tools/build/build.go release clean: go run tools/clean/clean.go |
Changes to VERSION.
|
| | | 1 | 0.17.0 |
Changes to ast/ast.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package ast provides the abstract syntax tree for parsed zettel content. package ast import ( "net/url" |
| ︙ | ︙ |
Changes to ast/block.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 "zettelstore.de/client.fossil/attrs" // Definition of Block nodes. | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package ast import "zettelstore.de/client.fossil/attrs" // Definition of Block nodes. |
| ︙ | ︙ |
Changes to ast/inline.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "unicode/utf8" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package ast import ( "unicode/utf8" |
| ︙ | ︙ |
Changes to ast/ref.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" "strings" "zettelstore.de/z/zettel/id" ) // QueryPrefix is the prefix that denotes a query expression. | > > > > | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package ast
import (
"net/url"
"strings"
"zettelstore.de/client.fossil/api"
"zettelstore.de/z/zettel/id"
)
// QueryPrefix is the prefix that denotes a query expression.
const QueryPrefix = api.QueryPrefix
// ParseReference parses a string and returns a reference.
func ParseReference(s string) *Reference {
if invalidReference(s) {
return &Reference{URL: nil, Value: s, State: RefStateInvalid}
}
if strings.HasPrefix(s, QueryPrefix) {
|
| ︙ | ︙ |
Changes to ast/ref_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package ast_test import ( "testing" |
| ︙ | ︙ |
Changes to ast/walk.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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
| > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2021-present Detlef Stern
//-----------------------------------------------------------------------------
package ast
// Visitor is a visitor for walking the AST.
type Visitor interface {
Visit(node Node) Visitor
|
| ︙ | ︙ |
Changes to ast/walk_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package ast_test import ( "testing" |
| ︙ | ︙ | |||
57 58 59 60 61 62 63 |
Ref: &ast.Reference{Value: "http://zettelstore.de"},
Inlines: ast.CreateInlineSliceFromWords("URL", "text."),
},
),
}
v := benchVisitor{}
b.ResetTimer()
| | | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
Ref: &ast.Reference{Value: "http://zettelstore.de"},
Inlines: ast.CreateInlineSliceFromWords("URL", "text."),
},
),
}
v := benchVisitor{}
b.ResetTimer()
for range b.N {
ast.Walk(&v, &root)
}
}
type benchVisitor struct{}
func (bv *benchVisitor) Visit(ast.Node) ast.Visitor { return bv }
|
Changes to auth/auth.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 auth provides services for authentification / authorization. package auth import ( "time" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package auth provides services for authentification / authorization. package auth import ( "time" |
| ︙ | ︙ |
Changes to auth/cred/cred.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 cred provides some function for handling credentials. package cred import ( "bytes" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package cred provides some function for handling credentials. package cred import ( "bytes" |
| ︙ | ︙ |
Changes to auth/impl/digest.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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2023-present 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 (
"bytes"
"crypto"
"crypto/hmac"
"encoding/base64"
"zettelstore.de/sx.fossil"
"zettelstore.de/sx.fossil/sxreader"
)
var encoding = base64.RawURLEncoding
const digestAlg = crypto.SHA384
func sign(claim sx.Object, secret []byte) ([]byte, error) {
var buf bytes.Buffer
| > > > | > > > | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2023-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------
package impl
import (
"bytes"
"crypto"
"crypto/hmac"
"encoding/base64"
"zettelstore.de/sx.fossil"
"zettelstore.de/sx.fossil/sxreader"
)
var encoding = base64.RawURLEncoding
const digestAlg = crypto.SHA384
func sign(claim sx.Object, secret []byte) ([]byte, error) {
var buf bytes.Buffer
_, err := sx.Print(&buf, claim)
if err != nil {
return nil, err
}
token := make([]byte, encoding.EncodedLen(buf.Len()))
encoding.Encode(token, buf.Bytes())
digest := hmac.New(digestAlg.New, secret)
_, err = digest.Write(buf.Bytes())
if err != nil {
return nil, err
}
dig := digest.Sum(nil)
encDig := make([]byte, encoding.EncodedLen(len(dig)))
encoding.Encode(encDig, dig)
|
| ︙ | ︙ |
Changes to auth/impl/impl.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 services for authentification / authorization. package impl import ( "errors" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package impl provides services for authentification / authorization. package impl import ( "errors" |
| ︙ | ︙ | |||
133 134 135 136 137 138 139 |
return ErrTokenExpired
}
zid := id.Zid(vals[4].(sx.Int64))
if !zid.IsValid() {
return ErrNoZid
}
| | | 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
return ErrTokenExpired
}
zid := id.Zid(vals[4].(sx.Int64))
if !zid.IsValid() {
return ErrNoZid
}
tokenData.Ident = string(ident)
tokenData.Issued = issued
tokenData.Now = now
tokenData.Expires = expires
tokenData.Zid = zid
return nil
}
|
| ︙ | ︙ |
Changes to auth/policy/anon.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package policy import ( "zettelstore.de/z/auth" "zettelstore.de/z/config" |
| ︙ | ︙ |
Changes to auth/policy/box.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package policy import ( "context" |
| ︙ | ︙ |
Changes to auth/policy/default.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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/client.fossil/api" "zettelstore.de/z/auth" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package policy import ( "zettelstore.de/client.fossil/api" "zettelstore.de/z/auth" |
| ︙ | ︙ |
Changes to auth/policy/owner.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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/client.fossil/api" "zettelstore.de/z/auth" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package policy import ( "zettelstore.de/client.fossil/api" "zettelstore.de/z/auth" |
| ︙ | ︙ |
Changes to auth/policy/policy.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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/z/auth" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package policy provides some interfaces and implementation for authorizsation policies. package policy import ( "zettelstore.de/z/auth" |
| ︙ | ︙ |
Changes to auth/policy/policy_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package policy import ( "fmt" "testing" |
| ︙ | ︙ |
Changes to auth/policy/readonly.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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/zettel/meta"
type roPolicy struct{}
| > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package policy
import "zettelstore.de/z/zettel/meta"
type roPolicy struct{}
|
| ︙ | ︙ |
Changes to box/box.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package box provides a generic interface to zettel boxes. package box import ( "context" |
| ︙ | ︙ |
Changes to box/compbox/compbox.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package compbox provides zettel that have computed content. package compbox import ( "context" |
| ︙ | ︙ |
Changes to box/compbox/config.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package compbox import ( "bytes" |
| ︙ | ︙ |
Changes to box/compbox/keys.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package compbox import ( "bytes" "fmt" |
| ︙ | ︙ |
Changes to box/compbox/log.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package compbox import ( "bytes" |
| ︙ | ︙ |
Changes to box/compbox/manager.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package compbox import ( "bytes" "fmt" |
| ︙ | ︙ |
Changes to box/compbox/parser.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package compbox import ( "bytes" "fmt" |
| ︙ | ︙ |
Changes to box/compbox/version.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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/client.fossil/api" "zettelstore.de/z/kernel" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package compbox import ( "zettelstore.de/client.fossil/api" "zettelstore.de/z/kernel" |
| ︙ | ︙ |
Changes to box/constbox/base.css.
1 2 3 4 5 6 7 |
*,*::before,*::after {
box-sizing: border-box;
}
html {
font-size: 1rem;
font-family: serif;
scroll-behavior: smooth;
| > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/*-----------------------------------------------------------------------------
* Copyright (c) 2020-present 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.
*
* SPDX-License-Identifier: EUPL-1.2
* SPDX-FileCopyrightText: 2020-present Detlef Stern
*-----------------------------------------------------------------------------
*/
*,*::before,*::after {
box-sizing: border-box;
}
html {
font-size: 1rem;
font-family: serif;
scroll-behavior: smooth;
|
| ︙ | ︙ |
Changes to box/constbox/base.sxn.
1 2 3 4 5 6 7 8 9 10 | `(@@@@ (html ,@(if lang `((@ (lang ,lang)))) (head (meta (@ (charset "utf-8"))) (meta (@ (name "viewport") (content "width=device-width, initial-scale=1.0"))) (meta (@ (name "generator") (content "Zettelstore"))) (meta (@ (name "format-detection") (content "telephone=no"))) ,@META-HEADER (link (@ (rel "stylesheet") (href ,css-base-url))) (link (@ (rel "stylesheet") (href ,css-user-url))) | > > > > > > > > > > > > > | | 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 |
;;;----------------------------------------------------------------------------
;;; Copyright (c) 2023-present 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.
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------
`(@@@@
(html ,@(if lang `((@ (lang ,lang))))
(head
(meta (@ (charset "utf-8")))
(meta (@ (name "viewport") (content "width=device-width, initial-scale=1.0")))
(meta (@ (name "generator") (content "Zettelstore")))
(meta (@ (name "format-detection") (content "telephone=no")))
,@META-HEADER
(link (@ (rel "stylesheet") (href ,css-base-url)))
(link (@ (rel "stylesheet") (href ,css-user-url)))
,@(ROLE-DEFAULT-meta (current-binding))
(title ,title))
(body
(nav (@ (class "zs-menu"))
(a (@ (href ,home-url)) "Home")
,@(if with-auth
`((div (@ (class "zs-dropdown"))
(button "User")
|
| ︙ | ︙ | |||
35 36 37 38 39 40 41 |
,@(if new-zettel-links
`((div (@ (class "zs-dropdown"))
(button "New")
(nav (@ (class "zs-dropdown-content"))
,@(map wui-link new-zettel-links)
)))
)
| | > > | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
,@(if new-zettel-links
`((div (@ (class "zs-dropdown"))
(button "New")
(nav (@ (class "zs-dropdown-content"))
,@(map wui-link new-zettel-links)
)))
)
(search (form (@ (action ,search-url))
(input (@ (type "search") (inputmode "search") (name ,query-key-query)
(title "General search field, with same behaviour as search field in search result list")
(placeholder "Search..") (dir "auto")))))
)
(main (@ (class "content")) ,DETAIL)
,@(if FOOTER `((footer (hr) ,@FOOTER)))
,@(if debug-mode '((div (b "WARNING: Debug mode is enabled. DO NOT USE IN PRODUCTION!"))))
)))
|
Changes to box/constbox/constbox.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 constbox puts zettel inside the executable. package constbox import ( "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package constbox puts zettel inside the executable. package constbox import ( "context" |
| ︙ | ︙ | |||
174 175 176 177 178 179 180 |
zettel.NewContent(contentDependencies)},
id.BaseTemplateZid: {
constHeader{
api.KeyTitle: "Zettelstore Base HTML Template",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20230510155100",
| | | | | | | | | | > | | | 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 |
zettel.NewContent(contentDependencies)},
id.BaseTemplateZid: {
constHeader{
api.KeyTitle: "Zettelstore Base HTML Template",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20230510155100",
api.KeyModified: "20240219145300",
api.KeyVisibility: api.ValueVisibilityExpert,
},
zettel.NewContent(contentBaseSxn)},
id.LoginTemplateZid: {
constHeader{
api.KeyTitle: "Zettelstore Login Form HTML Template",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20200804111624",
api.KeyModified: "20240219145200",
api.KeyVisibility: api.ValueVisibilityExpert,
},
zettel.NewContent(contentLoginSxn)},
id.ZettelTemplateZid: {
constHeader{
api.KeyTitle: "Zettelstore Zettel HTML Template",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20230510155300",
api.KeyModified: "20240219145100",
api.KeyVisibility: api.ValueVisibilityExpert,
},
zettel.NewContent(contentZettelSxn)},
id.InfoTemplateZid: {
constHeader{
api.KeyTitle: "Zettelstore Info HTML Template",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20200804111624",
api.KeyModified: "20240219145200",
api.KeyVisibility: api.ValueVisibilityExpert,
},
zettel.NewContent(contentInfoSxn)},
id.FormTemplateZid: {
constHeader{
api.KeyTitle: "Zettelstore Form HTML Template",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20200804111624",
api.KeyModified: "20240219145200",
api.KeyVisibility: api.ValueVisibilityExpert,
},
zettel.NewContent(contentFormSxn)},
id.RenameTemplateZid: {
constHeader{
api.KeyTitle: "Zettelstore Rename Form HTML Template",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20200804111624",
api.KeyModified: "20240219145200",
api.KeyVisibility: api.ValueVisibilityExpert,
},
zettel.NewContent(contentRenameSxn)},
id.DeleteTemplateZid: {
constHeader{
api.KeyTitle: "Zettelstore Delete HTML Template",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20200804111624",
api.KeyModified: "20240219145200",
api.KeyVisibility: api.ValueVisibilityExpert,
},
zettel.NewContent(contentDeleteSxn)},
id.ListTemplateZid: {
constHeader{
api.KeyTitle: "Zettelstore List Zettel HTML Template",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20230704122100",
api.KeyModified: "20240219145200",
api.KeyVisibility: api.ValueVisibilityExpert,
},
zettel.NewContent(contentListZettelSxn)},
id.ErrorTemplateZid: {
constHeader{
api.KeyTitle: "Zettelstore Error HTML Template",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20210305133215",
api.KeyModified: "20240219145200",
api.KeyVisibility: api.ValueVisibilityExpert,
},
zettel.NewContent(contentErrorSxn)},
id.StartSxnZid: {
constHeader{
api.KeyTitle: "Zettelstore Sxn Start Code",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20230824160700",
api.KeyModified: "20240219145200",
api.KeyVisibility: api.ValueVisibilityExpert,
api.KeyPrecursor: string(api.ZidSxnBase),
},
zettel.NewContent(contentStartCodeSxn)},
id.BaseSxnZid: {
constHeader{
api.KeyTitle: "Zettelstore Sxn Base Code",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20230619132800",
api.KeyModified: "20240219144600",
api.KeyReadOnly: api.ValueTrue,
api.KeyVisibility: api.ValueVisibilityExpert,
api.KeyPrecursor: string(api.ZidSxnPrelude),
},
zettel.NewContent(contentBaseCodeSxn)},
id.PreludeSxnZid: {
constHeader{
api.KeyTitle: "Zettelstore Sxn Prelude",
api.KeyRole: api.ValueRoleConfiguration,
api.KeySyntax: meta.SyntaxSxn,
api.KeyCreated: "20231006181700",
api.KeyModified: "20240222121200",
api.KeyReadOnly: api.ValueTrue,
api.KeyVisibility: api.ValueVisibilityExpert,
},
zettel.NewContent(contentPreludeSxn)},
id.MustParse(api.ZidBaseCSS): {
constHeader{
api.KeyTitle: "Zettelstore Base CSS",
|
| ︙ | ︙ |
Changes to box/constbox/delete.sxn.
1 2 3 4 5 6 7 |
`(article
(header (h1 "Delete Zettel " ,zid))
(p "Do you really want to delete this zettel?")
,@(if shadowed-box
`((div (@ (class "zs-info"))
(h2 "Information")
(p "If you delete this zettel, the previously shadowed zettel from overlayed box " ,shadowed-box " becomes available.")
| > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
;;;----------------------------------------------------------------------------
;;; Copyright (c) 2023-present 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.
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------
`(article
(header (h1 "Delete Zettel " ,zid))
(p "Do you really want to delete this zettel?")
,@(if shadowed-box
`((div (@ (class "zs-info"))
(h2 "Information")
(p "If you delete this zettel, the previously shadowed zettel from overlayed box " ,shadowed-box " becomes available.")
|
| ︙ | ︙ |
Changes to box/constbox/error.sxn.
1 2 3 4 | `(article (header (h1 ,heading)) ,message ) | > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ;;;---------------------------------------------------------------------------- ;;; Copyright (c) 2023-present 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. ;;; ;;; SPDX-License-Identifier: EUPL-1.2 ;;; SPDX-FileCopyrightText: 2023-present Detlef Stern ;;;---------------------------------------------------------------------------- `(article (header (h1 ,heading)) ,message ) |
Changes to box/constbox/form.sxn.
1 2 3 4 5 |
`(article
(header (h1 ,heading))
(form (@ (action ,form-action-url) (method "POST") (enctype "multipart/form-data"))
(div
(label (@ (for "zs-title")) "Title " (a (@ (title "Main heading of this zettel.")) (@H "ⓘ")))
| > > > > > > > > > > > > > | > > > > | | > > | > > > > | | > > | 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 |
;;;----------------------------------------------------------------------------
;;; Copyright (c) 2023-present 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.
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------
`(article
(header (h1 ,heading))
(form (@ (action ,form-action-url) (method "POST") (enctype "multipart/form-data"))
(div
(label (@ (for "zs-title")) "Title " (a (@ (title "Main heading of this zettel.")) (@H "ⓘ")))
(input (@ (class "zs-input") (type "text") (id "zs-title") (name "title")
(title "Title of this zettel")
(placeholder "Title..") (value ,meta-title) (dir "auto") (autofocus))))
(div
(label (@ (for "zs-role")) "Role " (a (@ (title "One word, without spaces, to set the main role of this zettel.")) (@H "ⓘ")))
(input (@ (class "zs-input") (type "text") (pattern "\\w*") (id "zs-role") (name "role")
(title "One word, letters and digits, but no spaces, to set the main role of the zettel.")
(placeholder "role..") (value ,meta-role) (dir "auto")
,@(if role-data '((list "zs-role-data")))
))
,@(wui-datalist "zs-role-data" role-data)
)
(div
(label (@ (for "zs-tags")) "Tags " (a (@ (title "Tags must begin with an '#' sign. They are separated by spaces.")) (@H "ⓘ")))
(input (@ (class "zs-input") (type "text") (id "zs-tags") (name "tags")
(title "Tags/keywords to categorize the zettel. Each tags is a word that begins with a '#' character; they are separated by spaces")
(placeholder "#tag") (value ,meta-tags) (dir "auto"))))
(div
(label (@ (for "zs-meta")) "Metadata " (a (@ (title "Other metadata for this zettel. Each line contains a key/value pair, separated by a colon ':'.")) (@H "ⓘ")))
(textarea (@ (class "zs-input") (id "zs-meta") (name "meta") (rows "4")
(title "Additional metadata about the zettel")
(placeholder "metakey: metavalue") (dir "auto")) ,meta))
(div
(label (@ (for "zs-syntax")) "Syntax " (a (@ (title "Syntax of zettel content below, one word. Typically 'zmk' (for zettelmarkup).")) (@H "ⓘ")))
(input (@ (class "zs-input") (type "text") (pattern "\\w*") (id "zs-syntax") (name "syntax")
(title "Syntax/format of zettel content below, one word, letters and digits, no spaces.")
(placeholder "syntax..") (value ,meta-syntax) (dir "auto")
,@(if syntax-data '((list "zs-syntax-data")))
))
,@(wui-datalist "zs-syntax-data" syntax-data)
)
,@(if (bound? 'content)
`((div
(label (@ (for "zs-content")) "Content " (a (@ (title "Content for this zettel, according to above syntax.")) (@H "ⓘ")))
(textarea (@ (class "zs-input zs-content") (id "zs-content") (name "content") (rows "20")
(title "Zettel content, according to the given syntax")
(placeholder "Zettel content..") (dir "auto")) ,content)
))
)
(div
(input (@ (class "zs-primary") (type "submit") (value "Submit")))
(input (@ (class "zs-secondary") (type "submit") (value "Save") (formaction "?save")))
(input (@ (class "zs-upload") (type "file") (id "zs-file") (name "file")))
))
|
| ︙ | ︙ |
Changes to box/constbox/info.sxn.
1 2 3 4 5 6 |
`(article
(header (h1 "Information for Zettel " ,zid)
(p
(a (@ (href ,web-url)) "Web")
(@H " · ") (a (@ (href ,context-url)) "Context")
,@(if (bound? 'edit-url) `((@H " · ") (a (@ (href ,edit-url)) "Edit")))
| > > > > > > > > > > > > > > | | | 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) 2023-present 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.
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------
`(article
(header (h1 "Information for Zettel " ,zid)
(p
(a (@ (href ,web-url)) "Web")
(@H " · ") (a (@ (href ,context-url)) "Context")
(@H " / ") (a (@ (href ,context-full-url)) "Full")
,@(if (bound? 'edit-url) `((@H " · ") (a (@ (href ,edit-url)) "Edit")))
,@(ROLE-DEFAULT-actions (current-binding))
,@(if (bound? 'reindex-url) `((@H " · ") (a (@ (href ,reindex-url)) "Reindex")))
,@(if (bound? 'rename-url) `((@H " · ") (a (@ (href ,rename-url)) "Rename")))
,@(if (bound? 'delete-url) `((@H " · ") (a (@ (href ,delete-url)) "Delete")))
)
)
(h2 "Interpreted Metadata")
(table ,@(map wui-info-meta-table-row metadata))
(h2 "References")
,@(if local-links `((h3 "Local") (ul ,@(map wui-valid-link local-links))))
,@(if query-links `((h3 "Queries") (ul ,@(map wui-item-link query-links))))
,@(if ext-links `((h3 "External") (ul ,@(map wui-item-popup-link ext-links))))
(h3 "Unlinked")
,@unlinked-content
(form
|
| ︙ | ︙ |
Changes to box/constbox/listzettel.sxn.
1 2 | `(article (header (h1 ,heading)) | > > > > > > > > > > > > > | > > | > | > | | | > > | 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 |
;;;----------------------------------------------------------------------------
;;; Copyright (c) 2023-present 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.
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------
`(article
(header (h1 ,heading))
(search (form (@ (action ,search-url))
(input (@ (class "zs-input") (type "search") (inputmode "search") (name ,query-key-query)
(title "Contains the search that leads to the list below. You're allowed to modify it")
(placeholder "Search..") (value ,query-value) (dir "auto")))))
,@(if (bound? 'tag-zettel)
`((p (@ (class "zs-meta-zettel")) "Tag zettel: " ,@tag-zettel))
)
,@(if (bound? 'create-tag-zettel)
`((p (@ (class "zs-meta-zettel")) "Create tag zettel: " ,@create-tag-zettel))
)
,@(if (bound? 'role-zettel)
`((p (@ (class "zs-meta-zettel")) "Role zettel: " ,@role-zettel))
)
,@(if (bound? 'create-role-zettel)
`((p (@ (class "zs-meta-zettel")) "Create role zettel: " ,@create-role-zettel))
)
,@content
,@endnotes
(form (@ (action ,(if (bound? 'create-url) create-url)))
,(if (bound? 'data-url)
`(@L "Other encodings"
,(if (> num-entries 3) `(@L " of these " ,num-entries " entries: ") ": ")
(a (@ (href ,data-url)) "data")
", "
(a (@ (href ,plain-url)) "plain")
)
)
,@(if (bound? 'create-url)
`((input (@ (type "hidden") (name ,query-key-query) (value ,query-value)))
(input (@ (type "hidden") (name ,query-key-seed) (value ,seed)))
(input (@ (class "zs-primary") (type "submit") (value "Save As Zettel")))
)
)
)
)
|
Changes to box/constbox/login.sxn.
1 2 3 4 5 6 7 |
`(article
(header (h1 "Login"))
,@(if retry '((div (@ (class "zs-indication zs-error")) "Wrong user name / password. Try again.")))
(form (@ (method "POST") (action ""))
(div
(label (@ (for "username")) "User name:")
(input (@ (class "zs-input") (type "text") (id "username") (name "username") (placeholder "Your user name..") (autofocus))))
| > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
;;;----------------------------------------------------------------------------
;;; Copyright (c) 2023-present 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.
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------
`(article
(header (h1 "Login"))
,@(if retry '((div (@ (class "zs-indication zs-error")) "Wrong user name / password. Try again.")))
(form (@ (method "POST") (action ""))
(div
(label (@ (for "username")) "User name:")
(input (@ (class "zs-input") (type "text") (id "username") (name "username") (placeholder "Your user name..") (autofocus))))
|
| ︙ | ︙ |
Changes to box/constbox/prelude.sxn.
1 2 3 4 5 6 7 8 9 10 | ;;;---------------------------------------------------------------------------- ;;; Copyright (c) 2023-present 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. ;;;---------------------------------------------------------------------------- | > > > | | < < < < > > > > > > > > > > > > > > > > > > > > | 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 |
;;;----------------------------------------------------------------------------
;;; Copyright (c) 2023-present 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.
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------
;;; This zettel contains sxn definitions that are independent of specific
;;; subsystems, such as WebUI, API, or other. It just contains generic code to
;;; be used in all places.
;; Constants NIL and T
(defconst NIL ())
(defconst T 'T)
;; defunconst macro to define functions that are bound as a constant.
;;
;; (defunconst NAME ARGS EXPR ...)
(defmacro defunconst (name args . body)
`(begin (defun ,name ,args ,@body) (defconst ,name ,name)))
;; not macro
(defmacro not (x) `(if ,x NIL T))
;; not= macro, to negate an equivalence
(defmacro not= args `(not (= ,@args)))
;; let macro
;;
;; (let (BINDING ...) EXPR ...), where BINDING is a list of two elements
;; (SYMBOL EXPR)
(defmacro let (bindings . body)
`((lambda ,(map car bindings) ,@body) ,@(map cadr bindings)))
;; let* macro
;;
;; (let* (BINDING ...) EXPR ...), where SYMBOL may occur in later bindings.
(defmacro let* (bindings . body)
(if (null? bindings)
`((lambda () ,@body))
`((lambda (,(caar bindings))
(let* ,(cdr bindings) ,@body))
,(cadar bindings))))
;; cond macro
;;
;; (cond ((COND EXPR) ...))
(defmacro cond clauses
(if (null? clauses)
()
(let* ((clause (car clauses))
(the-cond (car clause)))
(if (= the-cond T)
(cadr clause)
`(if ,the-cond
,(cadr clause)
(cond ,@(cdr clauses)))))))
;; and macro
;;
;; (and EXPR ...)
(defmacro and args
(cond ((null? args) T)
((null? (cdr args)) (car args))
(T `(if ,(car args) (and ,@(cdr args))))))
|
| ︙ | ︙ |
Changes to box/constbox/rename.sxn.
1 2 3 4 5 6 7 |
`(article
(header (h1 "Rename Zettel " ,zid))
(p "Do you really want to rename this zettel?")
,@(if incoming
`((div (@ (class "zs-warning"))
(h2 "Warning!")
(p "If you rename this zettel, incoming references from the following zettel will become invalid.")
| > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
;;;----------------------------------------------------------------------------
;;; Copyright (c) 2023-present 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.
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------
`(article
(header (h1 "Rename Zettel " ,zid))
(p "Do you really want to rename this zettel?")
,@(if incoming
`((div (@ (class "zs-warning"))
(h2 "Warning!")
(p "If you rename this zettel, incoming references from the following zettel will become invalid.")
|
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
(ul ,@(map wui-item useless))
))
)
(form (@ (method "POST"))
(input (@ (type "hidden") (id "curzid") (name "curzid") (value ,zid)))
(div
(label (@ (for "newzid")) "New zettel id")
| > > > | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
(ul ,@(map wui-item useless))
))
)
(form (@ (method "POST"))
(input (@ (type "hidden") (id "curzid") (name "curzid") (value ,zid)))
(div
(label (@ (for "newzid")) "New zettel id")
(input (@ (class "zs-input") (type "text") (inputmode "numeric") (id "newzid") (name "newzid")
(pattern "\\d{14}")
(title "New zettel identifier, must be unique")
(placeholder "ZID..") (value ,zid) (autofocus))))
(div (input (@ (class "zs-primary") (type "submit") (value "Rename"))))
)
,(wui-meta-desc metapairs)
)
|
Changes to box/constbox/start.sxn.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ;;;---------------------------------------------------------------------------- ;;; Copyright (c) 2023-present 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. ;;;---------------------------------------------------------------------------- ;;; This zettel is the start of the loading sequence for Sx code used in the ;;; Zettelstore. Via the precursor metadata, dependend zettel are evaluated ;;; before this zettel. You must always depend, directly or indirectly on the ;;; "Zettelstore Sxn Base Code" zettel. It provides the base definitions. | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ;;;---------------------------------------------------------------------------- ;;; Copyright (c) 2023-present 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. ;;; ;;; SPDX-License-Identifier: EUPL-1.2 ;;; SPDX-FileCopyrightText: 2023-present Detlef Stern ;;;---------------------------------------------------------------------------- ;;; This zettel is the start of the loading sequence for Sx code used in the ;;; Zettelstore. Via the precursor metadata, dependend zettel are evaluated ;;; before this zettel. You must always depend, directly or indirectly on the ;;; "Zettelstore Sxn Base Code" zettel. It provides the base definitions. |
Changes to box/constbox/wuicode.sxn.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ;;;---------------------------------------------------------------------------- ;;; Copyright (c) 2023-present 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. ;;;---------------------------------------------------------------------------- ;; Contains WebUI specific code, but not related to a specific template. ;; wui-list-item returns the argument as a HTML list item. (defunconst wui-item (s) `(li ,s)) | > > > | | | | | 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 |
;;;----------------------------------------------------------------------------
;;; Copyright (c) 2023-present 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.
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------
;; Contains WebUI specific code, but not related to a specific template.
;; wui-list-item returns the argument as a HTML list item.
(defunconst wui-item (s) `(li ,s))
;; wui-info-meta-table-row takes a pair and translates it into a HTML table row
;; with two columns.
(defunconst wui-info-meta-table-row (p)
`(tr (td (@ (class zs-info-meta-key)) ,(car p)) (td (@ (class zs-info-meta-value)) ,(cdr p))))
;; wui-valid-link translates a local link into a HTML link. A link is a pair
;; (valid . url). If valid is not truish, only the invalid url is returned.
(defunconst wui-valid-link (l)
(if (car l)
`(li (a (@ (href ,(cdr l))) ,(cdr l)))
`(li ,(cdr l))))
|
| ︙ | ︙ | |||
71 72 73 74 75 76 77 | ;; identifier. It is used in the base template to update the metadata of the ;; HTML page to include some role specific CSS code. ;; Referenced in function "ROLE-DEFAULT-meta". (defvar CSS-ROLE-map '()) ;; ROLE-DEFAULT-meta returns some metadata for the base template. Any role ;; specific code should include the returned list of this function. | | | | | | | | | | | | | | | > > > > > > > > > > > | | | | | > > > > > | | 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 |
;; identifier. It is used in the base template to update the metadata of the
;; HTML page to include some role specific CSS code.
;; Referenced in function "ROLE-DEFAULT-meta".
(defvar CSS-ROLE-map '())
;; ROLE-DEFAULT-meta returns some metadata for the base template. Any role
;; specific code should include the returned list of this function.
(defun ROLE-DEFAULT-meta (binding)
`(,@(let* ((meta-role (binding-lookup 'meta-role binding))
(entry (assoc CSS-ROLE-map meta-role)))
(if (pair? entry)
`((link (@ (rel "stylesheet") (href ,(zid-content-path (cdr entry))))))
)
)
)
)
;; ACTION-SEPARATOR defines a HTML value that separates actions links.
(defvar ACTION-SEPARATOR '(@H " · "))
;; ROLE-DEFAULT-actions returns the default text for actions.
(defun ROLE-DEFAULT-actions (binding)
`(,@(let ((copy-url (binding-lookup 'copy-url binding)))
(if (defined? copy-url) `((@H " · ") (a (@ (href ,copy-url)) "Copy"))))
,@(let ((version-url (binding-lookup 'version-url binding)))
(if (defined? version-url) `((@H " · ") (a (@ (href ,version-url)) "Version"))))
,@(let ((child-url (binding-lookup 'child-url binding)))
(if (defined? child-url) `((@H " · ") (a (@ (href ,child-url)) "Child"))))
,@(let ((folge-url (binding-lookup 'folge-url binding)))
(if (defined? folge-url) `((@H " · ") (a (@ (href ,folge-url)) "Folge"))))
)
)
;; ROLE-tag-actions returns an additional action "Zettel" for zettel with role "tag".
(defun ROLE-tag-actions (binding)
`(,@(ROLE-DEFAULT-actions binding)
,@(let ((title (binding-lookup 'title binding)))
(if (and (defined? title) title)
`(,ACTION-SEPARATOR (a (@ (href ,(query->url (concat "tags:" title)))) "Zettel"))
)
)
)
)
;; ROLE-role-actions returns an additional action "Zettel" for zettel with role "role".
(defun ROLE-role-actions (binding)
`(,@(ROLE-DEFAULT-actions binding)
,@(let ((title (binding-lookup 'title binding)))
(if (and (defined? title) title)
`(,ACTION-SEPARATOR (a (@ (href ,(query->url (concat "role:" title)))) "Zettel"))
)
)
)
)
;; ROLE-DEFAULT-heading returns the default text for headings, below the
;; references of a zettel. In most cases it should be called from an
;; overwriting function.
(defun ROLE-DEFAULT-heading (binding)
`(,@(let ((meta-url (binding-lookup 'meta-url binding)))
(if (defined? meta-url) `((br) "URL: " ,(url-to-html meta-url))))
,@(let ((urls (binding-lookup 'urls binding)))
(if (defined? urls)
(map (lambda (u) `(@L (br) ,(car u) ": " ,(url-to-html (cdr u)))) urls)
)
)
,@(let ((meta-author (binding-lookup 'meta-author binding)))
(if (and (defined? meta-author) meta-author) `((br) "By " ,meta-author)))
)
)
|
Changes to box/constbox/zettel.sxn.
1 2 3 4 5 6 7 8 9 10 11 12 |
`(article
(header
(h1 ,heading)
(div (@ (class "zs-meta"))
,@(if (bound? 'edit-url) `((a (@ (href ,edit-url)) "Edit") (@H " · ")))
,zid (@H " · ")
(a (@ (href ,info-url)) "Info") (@H " · ")
"(" ,@(if (bound? 'role-url) `((a (@ (href ,role-url)) ,meta-role)))
,@(if (and (bound? 'folge-role-url) (bound? 'meta-folge-role))
`((@H " → ") (a (@ (href ,folge-role-url)) ,meta-folge-role)))
")"
,@(if tag-refs `((@H " · ") ,@tag-refs))
| > > > > > > > > > > > > > | | | 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) 2023-present 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.
;;;
;;; SPDX-License-Identifier: EUPL-1.2
;;; SPDX-FileCopyrightText: 2023-present Detlef Stern
;;;----------------------------------------------------------------------------
`(article
(header
(h1 ,heading)
(div (@ (class "zs-meta"))
,@(if (bound? 'edit-url) `((a (@ (href ,edit-url)) "Edit") (@H " · ")))
,zid (@H " · ")
(a (@ (href ,info-url)) "Info") (@H " · ")
"(" ,@(if (bound? 'role-url) `((a (@ (href ,role-url)) ,meta-role)))
,@(if (and (bound? 'folge-role-url) (bound? 'meta-folge-role))
`((@H " → ") (a (@ (href ,folge-role-url)) ,meta-folge-role)))
")"
,@(if tag-refs `((@H " · ") ,@tag-refs))
,@(ROLE-DEFAULT-actions (current-binding))
,@(if predecessor-refs `((br) "Predecessor: " ,predecessor-refs))
,@(if precursor-refs `((br) "Precursor: " ,precursor-refs))
,@(if superior-refs `((br) "Superior: " ,superior-refs))
,@(ROLE-DEFAULT-heading (current-binding))
)
)
,@content
,endnotes
,@(if (or folge-links subordinate-links back-links successor-links)
`((nav
,@(if folge-links `((details (@ (,folge-open)) (summary "Folgezettel") (ul ,@(map wui-item-link folge-links)))))
|
| ︙ | ︙ |
Changes to box/dirbox/dirbox.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package dirbox provides a directory-based zettel box. package dirbox import ( "context" |
| ︙ | ︙ | |||
85 86 87 88 89 90 91 |
_ notifyTypeSpec = iota
dirNotifyAny
dirNotifySimple
dirNotifyFS
)
func getDirSrvInfo(log *logger.Logger, notifyType string) notifyTypeSpec {
| | | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
_ notifyTypeSpec = iota
dirNotifyAny
dirNotifySimple
dirNotifyFS
)
func getDirSrvInfo(log *logger.Logger, notifyType string) notifyTypeSpec {
for range 2 {
switch notifyType {
case kernel.BoxDirTypeNotify:
return dirNotifyFS
case kernel.BoxDirTypeSimple:
return dirNotifySimple
default:
notifyType = kernel.Main.GetConfig(kernel.BoxService, kernel.BoxDefaultDirType).(string)
|
| ︙ | ︙ | |||
147 148 149 150 151 152 153 |
return box.StartStateStopped
}
func (dp *dirBox) Start(context.Context) error {
dp.mxCmds.Lock()
defer dp.mxCmds.Unlock()
dp.fCmds = make([]chan fileCmd, 0, dp.fSrvs)
| | | | 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 |
return box.StartStateStopped
}
func (dp *dirBox) Start(context.Context) error {
dp.mxCmds.Lock()
defer dp.mxCmds.Unlock()
dp.fCmds = make([]chan fileCmd, 0, dp.fSrvs)
for i := range dp.fSrvs {
cc := make(chan fileCmd)
go fileService(i, dp.log.Clone().Str("sub", "file").Uint("fn", uint64(i)).Child(), dp.dir, cc)
dp.fCmds = append(dp.fCmds, cc)
}
var notifier notify.Notifier
var err error
switch dp.notifySpec {
case dirNotifySimple:
notifier, err = notify.NewSimpleDirNotifier(dp.log.Clone().Str("notify", "simple").Child(), dp.dir)
default:
notifier, err = notify.NewFSDirNotifier(dp.log.Clone().Str("notify", "fs").Child(), dp.dir)
}
if err != nil {
dp.log.Error().Err(err).Msg("Unable to create directory supervisor")
dp.stopFileServices()
return err
}
dp.dirSrv = notify.NewDirService(
dp,
dp.log.Clone().Str("sub", "dirsrv").Child(),
notifier,
|
| ︙ | ︙ |
Changes to box/dirbox/dirbox_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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 "testing"
func TestIsPrime(t *testing.T) {
| > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2021-present Detlef Stern
//-----------------------------------------------------------------------------
package dirbox
import "testing"
func TestIsPrime(t *testing.T) {
|
| ︙ | ︙ | |||
28 29 30 31 32 33 34 |
if got != tc.exp {
t.Errorf("isPrime(%d)=%v, but got %v", tc.n, tc.exp, got)
}
}
}
func TestMakePrime(t *testing.T) {
| | | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
if got != tc.exp {
t.Errorf("isPrime(%d)=%v, but got %v", tc.n, tc.exp, got)
}
}
}
func TestMakePrime(t *testing.T) {
for i := range uint32(1500) {
np := makePrime(i)
if np < i {
t.Errorf("makePrime(%d) < %d", i, np)
continue
}
if !isPrime(np) {
t.Errorf("makePrime(%d) == %d is not prime", i, np)
|
| ︙ | ︙ |
Changes to box/dirbox/service.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "context" "io" "os" "path/filepath" "time" | > > > > | | | | | | | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package dirbox
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"time"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/box/filebox"
"zettelstore.de/z/box/notify"
"zettelstore.de/z/kernel"
"zettelstore.de/z/logger"
"zettelstore.de/z/zettel"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
func fileService(i uint32, log *logger.Logger, dirPath string, cmds <-chan fileCmd) {
// Something may panic. Ensure a running service.
defer func() {
if ri := recover(); ri != nil {
kernel.Main.LogRecover("FileService", ri)
go fileService(i, log, dirPath, cmds)
}
}()
log.Debug().Uint("i", uint64(i)).Str("dirpath", dirPath).Msg("File service started")
for cmd := range cmds {
cmd.run(dirPath)
}
log.Debug().Uint("i", uint64(i)).Str("dirpath", dirPath).Msg("File service stopped")
}
type fileCmd interface {
run(string)
}
const serviceTimeout = 5 * time.Second // must be shorter than the web servers timeout values for reading+writing.
// COMMAND: srvGetMeta ----------------------------------------
//
// Retrieves the meta data from a zettel.
|
| ︙ | ︙ | |||
71 72 73 74 75 76 77 |
rc chan<- resGetMeta
}
type resGetMeta struct {
meta *meta.Meta
err error
}
| | | < | | 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 |
rc chan<- resGetMeta
}
type resGetMeta struct {
meta *meta.Meta
err error
}
func (cmd *fileGetMeta) run(dirPath string) {
var m *meta.Meta
var err error
entry := cmd.entry
zid := entry.Zid
if metaName := entry.MetaName; metaName == "" {
contentName := entry.ContentName
contentExt := entry.ContentExt
if contentName == "" || contentExt == "" {
err = fmt.Errorf("no meta, no content in getMeta, zid=%v", zid)
} else if entry.HasMetaInContent() {
m, _, err = parseMetaContentFile(zid, filepath.Join(dirPath, contentName))
} else {
m = filebox.CalcDefaultMeta(zid, contentExt)
}
} else {
m, err = parseMetaFile(zid, filepath.Join(dirPath, metaName))
}
|
| ︙ | ︙ | |||
124 125 126 127 128 129 130 |
}
type resGetMetaContent struct {
meta *meta.Meta
content []byte
err error
}
| | | < | | 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 |
}
type resGetMetaContent struct {
meta *meta.Meta
content []byte
err error
}
func (cmd *fileGetMetaContent) run(dirPath string) {
var m *meta.Meta
var content []byte
var err error
entry := cmd.entry
zid := entry.Zid
contentName := entry.ContentName
contentExt := entry.ContentExt
contentPath := filepath.Join(dirPath, contentName)
if metaName := entry.MetaName; metaName == "" {
if contentName == "" || contentExt == "" {
err = fmt.Errorf("no meta, no content in getMetaContent, zid=%v", zid)
} else if entry.HasMetaInContent() {
m, content, err = parseMetaContentFile(zid, contentPath)
} else {
m = filebox.CalcDefaultMeta(zid, contentExt)
content, err = os.ReadFile(contentPath)
}
} else {
m, err = parseMetaFile(zid, filepath.Join(dirPath, metaName))
|
| ︙ | ︙ | |||
184 185 186 187 188 189 190 |
type fileSetZettel struct {
entry *notify.DirEntry
zettel zettel.Zettel
rc chan<- resSetZettel
}
type resSetZettel = error
| | > | | | | | | | | | > | | 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 |
type fileSetZettel struct {
entry *notify.DirEntry
zettel zettel.Zettel
rc chan<- resSetZettel
}
type resSetZettel = error
func (cmd *fileSetZettel) run(dirPath string) {
var err error
entry := cmd.entry
zid := entry.Zid
contentName := entry.ContentName
m := cmd.zettel.Meta
content := cmd.zettel.Content.AsBytes()
metaName := entry.MetaName
if metaName == "" {
if contentName == "" {
err = fmt.Errorf("no meta, no content in setZettel, zid=%v", zid)
} else {
contentPath := filepath.Join(dirPath, contentName)
if entry.HasMetaInContent() {
err = writeZettelFile(contentPath, m, content)
cmd.rc <- err
return
}
err = writeFileContent(contentPath, content)
}
cmd.rc <- err
return
}
err = writeMetaFile(filepath.Join(dirPath, metaName), m)
if err == nil && contentName != "" {
err = writeFileContent(filepath.Join(dirPath, contentName), content)
}
cmd.rc <- err
}
func writeMetaFile(metaPath string, m *meta.Meta) error {
|
| ︙ | ︙ | |||
233 234 235 236 237 238 239 |
}
func writeZettelFile(contentPath string, m *meta.Meta, content []byte) error {
zettelFile, err := openFileWrite(contentPath)
if err != nil {
return err
}
| < | < | 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
}
func writeZettelFile(contentPath string, m *meta.Meta, content []byte) error {
zettelFile, err := openFileWrite(contentPath)
if err != nil {
return err
}
err = writeMetaHeader(zettelFile, m)
if err == nil {
_, err = zettelFile.Write(content)
}
if err1 := zettelFile.Close(); err == nil {
err = err1
}
return err
|
| ︙ | ︙ | |||
296 297 298 299 300 301 302 |
type fileDeleteZettel struct {
entry *notify.DirEntry
rc chan<- resDeleteZettel
}
type resDeleteZettel = error
| | | | | > | 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 |
type fileDeleteZettel struct {
entry *notify.DirEntry
rc chan<- resDeleteZettel
}
type resDeleteZettel = error
func (cmd *fileDeleteZettel) run(dirPath string) {
var err error
entry := cmd.entry
contentName := entry.ContentName
contentPath := filepath.Join(dirPath, contentName)
if metaName := entry.MetaName; metaName == "" {
if contentName == "" {
err = fmt.Errorf("no meta, no content in deleteZettel, zid=%v", entry.Zid)
} else {
err = os.Remove(contentPath)
}
} else {
if contentName != "" {
err = os.Remove(contentPath)
}
err1 := os.Remove(filepath.Join(dirPath, metaName))
if err == nil {
err = err1
|
| ︙ | ︙ |
Changes to box/filebox/filebox.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "errors" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package filebox provides boxes that are stored in a file. package filebox import ( "errors" |
| ︙ | ︙ |
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-present 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" "strings" | > > > > | | | | 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 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package filebox import ( "archive/zip" "context" "fmt" "io" "strings" "zettelstore.de/client.fossil/input" "zettelstore.de/z/box" "zettelstore.de/z/box/notify" "zettelstore.de/z/logger" "zettelstore.de/z/query" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) |
| ︙ | ︙ | |||
96 97 98 99 100 101 102 |
var m *meta.Meta
var src []byte
var inMeta bool
contentName := entry.ContentName
if metaName := entry.MetaName; metaName == "" {
if contentName == "" {
| | > | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
var m *meta.Meta
var src []byte
var inMeta bool
contentName := entry.ContentName
if metaName := entry.MetaName; metaName == "" {
if contentName == "" {
err = fmt.Errorf("no meta, no content in getZettel, zid=%v", zid)
return zettel.Zettel{}, err
}
src, err = readZipFileContent(reader, entry.ContentName)
if err != nil {
return zettel.Zettel{}, err
}
if entry.HasMetaInContent() {
inp := input.NewInput(src)
|
| ︙ | ︙ | |||
205 206 207 208 209 210 211 |
func (zb *zipBox) readZipMeta(reader *zip.ReadCloser, zid id.Zid, entry *notify.DirEntry) (m *meta.Meta, err error) {
var inMeta bool
if metaName := entry.MetaName; metaName == "" {
contentName := entry.ContentName
contentExt := entry.ContentExt
if contentName == "" || contentExt == "" {
| | < | | 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
func (zb *zipBox) readZipMeta(reader *zip.ReadCloser, zid id.Zid, entry *notify.DirEntry) (m *meta.Meta, err error) {
var inMeta bool
if metaName := entry.MetaName; metaName == "" {
contentName := entry.ContentName
contentExt := entry.ContentExt
if contentName == "" || contentExt == "" {
err = fmt.Errorf("no meta, no content in getMeta, zid=%v", zid)
} else if entry.HasMetaInContent() {
m, err = readZipMetaFile(reader, zid, contentName)
} else {
m = CalcDefaultMeta(zid, contentExt)
}
} else {
m, err = readZipMetaFile(reader, zid, metaName)
}
|
| ︙ | ︙ |
Changes to box/helper.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) 2021-present 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 (
"net/url"
"strconv"
"time"
"zettelstore.de/z/zettel/id"
)
// GetNewZid calculates a new and unused zettel identifier, based on the current date and time.
func GetNewZid(testZid func(id.Zid) (bool, error)) (id.Zid, error) {
withSeconds := false
| > > > | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2021-present Detlef Stern
//-----------------------------------------------------------------------------
package box
import (
"net/url"
"strconv"
"time"
"zettelstore.de/z/zettel/id"
)
// GetNewZid calculates a new and unused zettel identifier, based on the current date and time.
func GetNewZid(testZid func(id.Zid) (bool, error)) (id.Zid, error) {
withSeconds := false
for range 90 { // Must be completed within 9 seconds (less than web/server.writeTimeout)
zid := id.New(withSeconds)
found, err := testZid(zid)
if err != nil {
return id.Invalid, err
}
if found {
return zid, nil
|
| ︙ | ︙ |
Changes to box/manager/anteroom.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package manager import ( "sync" |
| ︙ | ︙ |
Changes to box/manager/anteroom_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package manager import ( "testing" |
| ︙ | ︙ |
Changes to box/manager/box.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" "errors" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package manager import ( "context" "errors" |
| ︙ | ︙ | |||
26 27 28 29 30 31 32 |
// Location returns some information where the box is located.
func (mgr *Manager) Location() string {
if len(mgr.boxes) <= 2 {
return "NONE"
}
var sb strings.Builder
| | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
// Location returns some information where the box is located.
func (mgr *Manager) Location() string {
if len(mgr.boxes) <= 2 {
return "NONE"
}
var sb strings.Builder
for i := range len(mgr.boxes) - 2 {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(mgr.boxes[i].Location())
}
return sb.String()
}
|
| ︙ | ︙ | |||
263 264 265 266 267 268 269 |
}
mgr.mgrMx.RLock()
defer mgr.mgrMx.RUnlock()
for i, p := range mgr.boxes {
err := p.RenameZettel(ctx, curZid, newZid)
var errZNF box.ErrZettelNotFound
if err != nil && !errors.As(err, &errZNF) {
| | | 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
}
mgr.mgrMx.RLock()
defer mgr.mgrMx.RUnlock()
for i, p := range mgr.boxes {
err := p.RenameZettel(ctx, curZid, newZid)
var errZNF box.ErrZettelNotFound
if err != nil && !errors.As(err, &errZNF) {
for j := range i {
mgr.boxes[j].RenameZettel(ctx, newZid, curZid)
}
return err
}
}
mgr.idxRenameZettel(ctx, curZid, newZid)
return nil
|
| ︙ | ︙ |
Changes to box/manager/collect.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package manager import ( "strings" |
| ︙ | ︙ |
Changes to box/manager/enrich.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package manager import ( "context" "strconv" |
| ︙ | ︙ |
Changes to box/manager/indexer.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" "fmt" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package manager import ( "context" "fmt" |
| ︙ | ︙ |
Changes to box/manager/manager.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" "io" "net/url" "sync" "time" | > > > < | | 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 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package manager coordinates the various boxes and indexes of a Zettelstore. package manager import ( "context" "io" "net/url" "sync" "time" "zettelstore.de/z/auth" "zettelstore.de/z/box" "zettelstore.de/z/box/manager/mapstore" "zettelstore.de/z/box/manager/store" "zettelstore.de/z/config" "zettelstore.de/z/kernel" "zettelstore.de/z/logger" "zettelstore.de/z/strfun" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" |
| ︙ | ︙ | |||
75 76 77 78 79 80 81 |
func Register(scheme string, create createFunc) {
if _, ok := registry[scheme]; ok {
panic(scheme)
}
registry[scheme] = create
}
| < < < | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
func Register(scheme string, create createFunc) {
if _, ok := registry[scheme]; ok {
panic(scheme)
}
registry[scheme] = create
}
// Manager is a coordinating box.
type Manager struct {
mgrLog *logger.Logger
stateMx sync.RWMutex
state box.StartState
mgrMx sync.RWMutex
rtConfig config.Config
|
| ︙ | ︙ | |||
135 136 137 138 139 140 141 |
mgr := &Manager{
mgrLog: boxLog.Clone().Str("box", "manager").Child(),
rtConfig: rtConfig,
infos: make(chan box.UpdateInfo, len(boxURIs)*10),
propertyKeys: propertyKeys,
idxLog: boxLog.Clone().Str("box", "index").Child(),
| | | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
mgr := &Manager{
mgrLog: boxLog.Clone().Str("box", "manager").Child(),
rtConfig: rtConfig,
infos: make(chan box.UpdateInfo, len(boxURIs)*10),
propertyKeys: propertyKeys,
idxLog: boxLog.Clone().Str("box", "index").Child(),
idxStore: createIdxStore(rtConfig),
idxAr: newAnteroomQueue(1000),
idxReady: make(chan struct{}, 1),
}
cdata := ConnectData{Number: 1, Config: rtConfig, Enricher: mgr, Notify: mgr.infos}
boxes := make([]box.ManagedBox, 0, len(boxURIs)+2)
for _, uri := range boxURIs {
p, err := Connect(uri, authManager, &cdata)
|
| ︙ | ︙ | |||
165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
return nil, err
}
cdata.Number++
boxes = append(boxes, constbox, compbox)
mgr.boxes = boxes
return mgr, nil
}
// RegisterObserver registers an observer that will be notified
// if a zettel was found to be changed.
func (mgr *Manager) RegisterObserver(f box.UpdateFunc) {
if f != nil {
mgr.mxObserver.Lock()
mgr.observers = append(mgr.observers, f)
| > > > > | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
return nil, err
}
cdata.Number++
boxes = append(boxes, constbox, compbox)
mgr.boxes = boxes
return mgr, nil
}
func createIdxStore(_ config.Config) store.Store {
return mapstore.New()
}
// RegisterObserver registers an observer that will be notified
// if a zettel was found to be changed.
func (mgr *Manager) RegisterObserver(f box.UpdateFunc) {
if f != nil {
mgr.mxObserver.Lock()
mgr.observers = append(mgr.observers, f)
|
| ︙ | ︙ | |||
248 249 250 251 252 253 254 | case box.OnReady: return case box.OnReload: mgr.idxAr.Reset() case box.OnZettel: mgr.idxAr.EnqueueZettel(zid) default: | | | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
case box.OnReady:
return
case box.OnReload:
mgr.idxAr.Reset()
case box.OnZettel:
mgr.idxAr.EnqueueZettel(zid)
default:
mgr.mgrLog.Error().Uint("reason", uint64(reason)).Zid(zid).Msg("Unknown notification reason")
return
}
select {
case mgr.idxReady <- struct{}{}:
default:
}
}
|
| ︙ | ︙ | |||
311 312 313 314 315 316 317 |
func (mgr *Manager) waitBoxesAreStarted() {
const waitTime = 10 * time.Millisecond
const waitLoop = int(1 * time.Second / waitTime)
for i := 1; !mgr.allBoxesStarted(); i++ {
if i%waitLoop == 0 {
if time.Duration(i)*waitTime > time.Minute {
| | | 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
func (mgr *Manager) waitBoxesAreStarted() {
const waitTime = 10 * time.Millisecond
const waitLoop = int(1 * time.Second / waitTime)
for i := 1; !mgr.allBoxesStarted(); i++ {
if i%waitLoop == 0 {
if time.Duration(i)*waitTime > time.Minute {
mgr.mgrLog.Info().Msg("Waiting for more than one minute to start")
} else {
mgr.mgrLog.Trace().Msg("Wait for boxes to start")
}
}
time.Sleep(waitTime)
}
}
|
| ︙ | ︙ | |||
369 370 371 372 373 374 375 |
// ReIndex data of the given zettel.
func (mgr *Manager) ReIndex(_ context.Context, zid id.Zid) error {
mgr.mgrLog.Debug().Msg("ReIndex")
if mgr.State() != box.StartStateStarted {
return box.ErrStopped
}
| | | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
// ReIndex data of the given zettel.
func (mgr *Manager) ReIndex(_ context.Context, zid id.Zid) error {
mgr.mgrLog.Debug().Msg("ReIndex")
if mgr.State() != box.StartStateStarted {
return box.ErrStopped
}
mgr.infos <- box.UpdateInfo{Reason: box.OnZettel, Zid: zid}
return nil
}
// ReadStats populates st with box statistics.
func (mgr *Manager) ReadStats(st *box.Stats) {
mgr.mgrLog.Debug().Msg("ReadStats")
mgr.mgrMx.RLock()
|
| ︙ | ︙ |
Added box/manager/mapstore/mapstore.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 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 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 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 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 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 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 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2021-present Detlef Stern
//-----------------------------------------------------------------------------
// Package mapstore stored the index in main memory via a Go map.
package mapstore
import (
"context"
"fmt"
"io"
"sort"
"strings"
"sync"
"zettelstore.de/client.fossil/api"
"zettelstore.de/client.fossil/maps"
"zettelstore.de/z/box"
"zettelstore.de/z/box/manager/store"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
type zettelData struct {
meta *meta.Meta // a local copy of the metadata, without computed keys
dead id.Slice // list of dead references in this zettel
forward id.Slice // list of forward references in this zettel
backward id.Slice // list of zettel that reference with zettel
otherRefs map[string]bidiRefs
words []string // list of words of this zettel
urls []string // list of urls of this zettel
}
type bidiRefs struct {
forward id.Slice
backward id.Slice
}
type stringRefs map[string]id.Slice
type memStore struct {
mx sync.RWMutex
intern map[string]string // map to intern strings
idx map[id.Zid]*zettelData
dead map[id.Zid]id.Slice // map dead refs where they occur
words stringRefs
urls stringRefs
// Stats
mxStats sync.Mutex
updates uint64
}
// New returns a new memory-based index store.
func New() store.Store {
return &memStore{
intern: make(map[string]string, 1024),
idx: make(map[id.Zid]*zettelData),
dead: make(map[id.Zid]id.Slice),
words: make(stringRefs),
urls: make(stringRefs),
}
}
func (ms *memStore) GetMeta(_ context.Context, zid id.Zid) (*meta.Meta, error) {
ms.mx.RLock()
defer ms.mx.RUnlock()
if zi, found := ms.idx[zid]; found && zi.meta != nil {
// zi.meta is nil, if zettel was referenced, but is not indexed yet.
return zi.meta.Clone(), nil
}
return nil, box.ErrZettelNotFound{Zid: zid}
}
func (ms *memStore) Enrich(_ context.Context, m *meta.Meta) {
if ms.doEnrich(m) {
ms.mxStats.Lock()
ms.updates++
ms.mxStats.Unlock()
}
}
func (ms *memStore) doEnrich(m *meta.Meta) bool {
ms.mx.RLock()
defer ms.mx.RUnlock()
zi, ok := ms.idx[m.Zid]
if !ok {
return false
}
var updated bool
if len(zi.dead) > 0 {
m.Set(api.KeyDead, zi.dead.String())
updated = true
}
back := removeOtherMetaRefs(m, zi.backward.Clone())
if len(zi.backward) > 0 {
m.Set(api.KeyBackward, zi.backward.String())
updated = true
}
if len(zi.forward) > 0 {
m.Set(api.KeyForward, zi.forward.String())
back = remRefs(back, zi.forward)
updated = true
}
for k, refs := range zi.otherRefs {
if len(refs.backward) > 0 {
m.Set(k, refs.backward.String())
back = remRefs(back, refs.backward)
updated = true
}
}
if len(back) > 0 {
m.Set(api.KeyBack, back.String())
updated = true
}
return updated
}
// SearchEqual returns all zettel that contains the given exact word.
// The word must be normalized through Unicode NKFD, trimmed and not empty.
func (ms *memStore) SearchEqual(word string) id.Set {
ms.mx.RLock()
defer ms.mx.RUnlock()
result := id.NewSet()
if refs, ok := ms.words[word]; ok {
result.CopySlice(refs)
}
if refs, ok := ms.urls[word]; ok {
result.CopySlice(refs)
}
zid, err := id.Parse(word)
if err != nil {
return result
}
zi, ok := ms.idx[zid]
if !ok {
return result
}
addBackwardZids(result, zid, zi)
return result
}
// SearchPrefix returns all zettel that have a word with the given prefix.
// The prefix must be normalized through Unicode NKFD, trimmed and not empty.
func (ms *memStore) SearchPrefix(prefix string) id.Set {
ms.mx.RLock()
defer ms.mx.RUnlock()
result := ms.selectWithPred(prefix, strings.HasPrefix)
l := len(prefix)
if l > 14 {
return result
}
maxZid, err := id.Parse(prefix + "99999999999999"[:14-l])
if err != nil {
return result
}
var minZid id.Zid
if l < 14 && prefix == "0000000000000"[:l] {
minZid = id.Zid(1)
} else {
minZid, err = id.Parse(prefix + "00000000000000"[:14-l])
if err != nil {
return result
}
}
for zid, zi := range ms.idx {
if minZid <= zid && zid <= maxZid {
addBackwardZids(result, zid, zi)
}
}
return result
}
// SearchSuffix returns all zettel that have a word with the given suffix.
// The suffix must be normalized through Unicode NKFD, trimmed and not empty.
func (ms *memStore) SearchSuffix(suffix string) id.Set {
ms.mx.RLock()
defer ms.mx.RUnlock()
result := ms.selectWithPred(suffix, strings.HasSuffix)
l := len(suffix)
if l > 14 {
return result
}
val, err := id.ParseUint(suffix)
if err != nil {
return result
}
modulo := uint64(1)
for range l {
modulo *= 10
}
for zid, zi := range ms.idx {
if uint64(zid)%modulo == val {
addBackwardZids(result, zid, zi)
}
}
return result
}
// SearchContains returns all zettel that contains the given string.
// The string must be normalized through Unicode NKFD, trimmed and not empty.
func (ms *memStore) SearchContains(s string) id.Set {
ms.mx.RLock()
defer ms.mx.RUnlock()
result := ms.selectWithPred(s, strings.Contains)
if len(s) > 14 {
return result
}
if _, err := id.ParseUint(s); err != nil {
return result
}
for zid, zi := range ms.idx {
if strings.Contains(zid.String(), s) {
addBackwardZids(result, zid, zi)
}
}
return result
}
func (ms *memStore) selectWithPred(s string, pred func(string, string) bool) id.Set {
// Must only be called if ms.mx is read-locked!
result := id.NewSet()
for word, refs := range ms.words {
if !pred(word, s) {
continue
}
result.CopySlice(refs)
}
for u, refs := range ms.urls {
if !pred(u, s) {
continue
}
result.CopySlice(refs)
}
return result
}
func addBackwardZids(result id.Set, zid id.Zid, zi *zettelData) {
// Must only be called if ms.mx is read-locked!
result.Add(zid)
result.CopySlice(zi.backward)
for _, mref := range zi.otherRefs {
result.CopySlice(mref.backward)
}
}
func removeOtherMetaRefs(m *meta.Meta, back id.Slice) id.Slice {
for _, p := range m.PairsRest() {
switch meta.Type(p.Key) {
case meta.TypeID:
if zid, err := id.Parse(p.Value); err == nil {
back = remRef(back, zid)
}
case meta.TypeIDSet:
for _, val := range meta.ListFromValue(p.Value) {
if zid, err := id.Parse(val); err == nil {
back = remRef(back, zid)
}
}
}
}
return back
}
func (ms *memStore) UpdateReferences(_ context.Context, zidx *store.ZettelIndex) id.Set {
ms.mx.Lock()
defer ms.mx.Unlock()
m := ms.makeMeta(zidx)
zi, ziExist := ms.idx[zidx.Zid]
if !ziExist || zi == nil {
zi = &zettelData{}
ziExist = false
}
// Is this zettel an old dead reference mentioned in other zettel?
var toCheck id.Set
if refs, ok := ms.dead[zidx.Zid]; ok {
// These must be checked later again
toCheck = id.NewSet(refs...)
delete(ms.dead, zidx.Zid)
}
zi.meta = m
ms.updateDeadReferences(zidx, zi)
ids := ms.updateForwardBackwardReferences(zidx, zi)
toCheck = toCheck.Copy(ids)
ids = ms.updateMetadataReferences(zidx, zi)
toCheck = toCheck.Copy(ids)
zi.words = updateStrings(zidx.Zid, ms.words, zi.words, zidx.GetWords())
zi.urls = updateStrings(zidx.Zid, ms.urls, zi.urls, zidx.GetUrls())
// Check if zi must be inserted into ms.idx
if !ziExist {
ms.idx[zidx.Zid] = zi
}
return toCheck
}
var internableKeys = map[string]bool{
api.KeyRole: true,
api.KeySyntax: true,
api.KeyFolgeRole: true,
api.KeyLang: true,
api.KeyReadOnly: true,
}
func isInternableValue(key string) bool {
if internableKeys[key] {
return true
}
return strings.HasSuffix(key, meta.SuffixKeyRole)
}
func (ms *memStore) internString(s string) string {
if is, found := ms.intern[s]; found {
return is
}
ms.intern[s] = s
return s
}
func (ms *memStore) makeMeta(zidx *store.ZettelIndex) *meta.Meta {
origM := zidx.GetMeta()
copyM := meta.New(origM.Zid)
for _, p := range origM.Pairs() {
key := ms.internString(p.Key)
if isInternableValue(key) {
copyM.Set(key, ms.internString(p.Value))
} else if key == api.KeyBoxNumber || !meta.IsComputed(key) {
copyM.Set(key, p.Value)
}
}
return copyM
}
func (ms *memStore) updateDeadReferences(zidx *store.ZettelIndex, zi *zettelData) {
// Must only be called if ms.mx is write-locked!
drefs := zidx.GetDeadRefs()
newRefs, remRefs := refsDiff(drefs, zi.dead)
zi.dead = drefs
for _, ref := range remRefs {
ms.dead[ref] = remRef(ms.dead[ref], zidx.Zid)
}
for _, ref := range newRefs {
ms.dead[ref] = addRef(ms.dead[ref], zidx.Zid)
}
}
func (ms *memStore) updateForwardBackwardReferences(zidx *store.ZettelIndex, zi *zettelData) id.Set {
// Must only be called if ms.mx is write-locked!
brefs := zidx.GetBackRefs()
newRefs, remRefs := refsDiff(brefs, zi.forward)
zi.forward = brefs
var toCheck id.Set
for _, ref := range remRefs {
bzi := ms.getOrCreateEntry(ref)
bzi.backward = remRef(bzi.backward, zidx.Zid)
if bzi.meta == nil {
toCheck = toCheck.Add(ref)
}
}
for _, ref := range newRefs {
bzi := ms.getOrCreateEntry(ref)
bzi.backward = addRef(bzi.backward, zidx.Zid)
if bzi.meta == nil {
toCheck = toCheck.Add(ref)
}
}
return toCheck
}
func (ms *memStore) updateMetadataReferences(zidx *store.ZettelIndex, zi *zettelData) id.Set {
// Must only be called if ms.mx is write-locked!
inverseRefs := zidx.GetInverseRefs()
for key, mr := range zi.otherRefs {
if _, ok := inverseRefs[key]; ok {
continue
}
ms.removeInverseMeta(zidx.Zid, key, mr.forward)
}
if zi.otherRefs == nil {
zi.otherRefs = make(map[string]bidiRefs)
}
var toCheck id.Set
for key, mrefs := range inverseRefs {
mr := zi.otherRefs[key]
newRefs, remRefs := refsDiff(mrefs, mr.forward)
mr.forward = mrefs
zi.otherRefs[key] = mr
for _, ref := range newRefs {
bzi := ms.getOrCreateEntry(ref)
if bzi.otherRefs == nil {
bzi.otherRefs = make(map[string]bidiRefs)
}
bmr := bzi.otherRefs[key]
bmr.backward = addRef(bmr.backward, zidx.Zid)
bzi.otherRefs[key] = bmr
if bzi.meta == nil {
toCheck = toCheck.Add(ref)
}
}
ms.removeInverseMeta(zidx.Zid, key, remRefs)
}
return toCheck
}
func updateStrings(zid id.Zid, srefs stringRefs, prev []string, next store.WordSet) []string {
newWords, removeWords := next.Diff(prev)
for _, word := range newWords {
if refs, ok := srefs[word]; ok {
srefs[word] = addRef(refs, zid)
continue
}
srefs[word] = id.Slice{zid}
}
for _, word := range removeWords {
refs, ok := srefs[word]
if !ok {
continue
}
refs2 := remRef(refs, zid)
if len(refs2) == 0 {
delete(srefs, word)
continue
}
srefs[word] = refs2
}
return next.Words()
}
func (ms *memStore) getOrCreateEntry(zid id.Zid) *zettelData {
// Must only be called if ms.mx is write-locked!
if zi, ok := ms.idx[zid]; ok {
return zi
}
zi := &zettelData{}
ms.idx[zid] = zi
return zi
}
func (ms *memStore) RenameZettel(_ context.Context, curZid, newZid id.Zid) id.Set {
ms.mx.Lock()
defer ms.mx.Unlock()
curZi, curFound := ms.idx[curZid]
_, newFound := ms.idx[newZid]
if !curFound || newFound {
return nil
}
newZi := &zettelData{
meta: copyMeta(curZi.meta, newZid),
dead: ms.copyDeadReferences(curZi.dead),
forward: ms.copyForward(curZi.forward, newZid),
backward: nil, // will be done through tocheck
otherRefs: nil, // TODO: check if this will be done through toCheck
words: copyStrings(ms.words, curZi.words, newZid),
urls: copyStrings(ms.urls, curZi.urls, newZid),
}
ms.idx[newZid] = newZi
toCheck := ms.doDeleteZettel(curZid)
toCheck = toCheck.CopySlice(ms.dead[newZid])
delete(ms.dead, newZid)
toCheck = toCheck.Add(newZid) // should update otherRefs
return toCheck
}
func copyMeta(m *meta.Meta, newZid id.Zid) *meta.Meta {
result := m.Clone()
result.Zid = newZid
return result
}
func (ms *memStore) copyDeadReferences(curDead id.Slice) id.Slice {
// Must only be called if ms.mx is write-locked!
if l := len(curDead); l > 0 {
result := make(id.Slice, l)
for i, ref := range curDead {
result[i] = ref
ms.dead[ref] = addRef(ms.dead[ref], ref)
}
return result
}
return nil
}
func (ms *memStore) copyForward(curForward id.Slice, newZid id.Zid) id.Slice {
// Must only be called if ms.mx is write-locked!
if l := len(curForward); l > 0 {
result := make(id.Slice, l)
for i, ref := range curForward {
result[i] = ref
if fzi, found := ms.idx[ref]; found {
fzi.backward = addRef(fzi.backward, newZid)
}
}
return result
}
return nil
}
func copyStrings(msStringMap stringRefs, curStrings []string, newZid id.Zid) []string {
// Must only be called if ms.mx is write-locked!
if l := len(curStrings); l > 0 {
result := make([]string, l)
for i, s := range curStrings {
result[i] = s
msStringMap[s] = addRef(msStringMap[s], newZid)
}
return result
}
return nil
}
func (ms *memStore) DeleteZettel(_ context.Context, zid id.Zid) id.Set {
ms.mx.Lock()
defer ms.mx.Unlock()
return ms.doDeleteZettel(zid)
}
func (ms *memStore) doDeleteZettel(zid id.Zid) id.Set {
// Must only be called if ms.mx is write-locked!
zi, ok := ms.idx[zid]
if !ok {
return nil
}
ms.deleteDeadSources(zid, zi)
toCheck := ms.deleteForwardBackward(zid, zi)
for key, mrefs := range zi.otherRefs {
ms.removeInverseMeta(zid, key, mrefs.forward)
}
deleteStrings(ms.words, zi.words, zid)
deleteStrings(ms.urls, zi.urls, zid)
delete(ms.idx, zid)
return toCheck
}
func (ms *memStore) deleteDeadSources(zid id.Zid, zi *zettelData) {
// Must only be called if ms.mx is write-locked!
for _, ref := range zi.dead {
if drefs, ok := ms.dead[ref]; ok {
drefs = remRef(drefs, zid)
if len(drefs) > 0 {
ms.dead[ref] = drefs
} else {
delete(ms.dead, ref)
}
}
}
}
func (ms *memStore) deleteForwardBackward(zid id.Zid, zi *zettelData) id.Set {
// Must only be called if ms.mx is write-locked!
for _, ref := range zi.forward {
if fzi, ok := ms.idx[ref]; ok {
fzi.backward = remRef(fzi.backward, zid)
}
}
var toCheck id.Set
for _, ref := range zi.backward {
if bzi, ok := ms.idx[ref]; ok {
bzi.forward = remRef(bzi.forward, zid)
toCheck = toCheck.Add(ref)
}
}
return toCheck
}
func (ms *memStore) removeInverseMeta(zid id.Zid, key string, forward id.Slice) {
// Must only be called if ms.mx is write-locked!
for _, ref := range forward {
bzi, ok := ms.idx[ref]
if !ok || bzi.otherRefs == nil {
continue
}
bmr, ok := bzi.otherRefs[key]
if !ok {
continue
}
bmr.backward = remRef(bmr.backward, zid)
if len(bmr.backward) > 0 || len(bmr.forward) > 0 {
bzi.otherRefs[key] = bmr
} else {
delete(bzi.otherRefs, key)
if len(bzi.otherRefs) == 0 {
bzi.otherRefs = nil
}
}
}
}
func deleteStrings(msStringMap stringRefs, curStrings []string, zid id.Zid) {
// Must only be called if ms.mx is write-locked!
for _, word := range curStrings {
refs, ok := msStringMap[word]
if !ok {
continue
}
refs2 := remRef(refs, zid)
if len(refs2) == 0 {
delete(msStringMap, word)
continue
}
msStringMap[word] = refs2
}
}
func (ms *memStore) ReadStats(st *store.Stats) {
ms.mx.RLock()
st.Zettel = len(ms.idx)
st.Words = uint64(len(ms.words))
st.Urls = uint64(len(ms.urls))
ms.mx.RUnlock()
ms.mxStats.Lock()
st.Updates = ms.updates
ms.mxStats.Unlock()
}
func (ms *memStore) Dump(w io.Writer) {
ms.mx.RLock()
defer ms.mx.RUnlock()
io.WriteString(w, "=== Dump\n")
ms.dumpIndex(w)
ms.dumpDead(w)
dumpStringRefs(w, "Words", "", "", ms.words)
dumpStringRefs(w, "URLs", "[[", "]]", ms.urls)
}
func (ms *memStore) dumpIndex(w io.Writer) {
if len(ms.idx) == 0 {
return
}
io.WriteString(w, "==== Zettel Index\n")
zids := make(id.Slice, 0, len(ms.idx))
for id := range ms.idx {
zids = append(zids, id)
}
zids.Sort()
for _, id := range zids {
fmt.Fprintln(w, "=====", id)
zi := ms.idx[id]
if len(zi.dead) > 0 {
fmt.Fprintln(w, "* Dead:", zi.dead)
}
dumpZids(w, "* Forward:", zi.forward)
dumpZids(w, "* Backward:", zi.backward)
for k, fb := range zi.otherRefs {
fmt.Fprintln(w, "* Meta", k)
dumpZids(w, "** Forward:", fb.forward)
dumpZids(w, "** Backward:", fb.backward)
}
dumpStrings(w, "* Words", "", "", zi.words)
dumpStrings(w, "* URLs", "[[", "]]", zi.urls)
}
}
func (ms *memStore) dumpDead(w io.Writer) {
if len(ms.dead) == 0 {
return
}
fmt.Fprintf(w, "==== Dead References\n")
zids := make(id.Slice, 0, len(ms.dead))
for id := range ms.dead {
zids = append(zids, id)
}
zids.Sort()
for _, id := range zids {
fmt.Fprintln(w, ";", id)
fmt.Fprintln(w, ":", ms.dead[id])
}
}
func dumpZids(w io.Writer, prefix string, zids id.Slice) {
if len(zids) > 0 {
io.WriteString(w, prefix)
for _, zid := range zids {
io.WriteString(w, " ")
w.Write(zid.Bytes())
}
fmt.Fprintln(w)
}
}
func dumpStrings(w io.Writer, title, preString, postString string, slice []string) {
if len(slice) > 0 {
sl := make([]string, len(slice))
copy(sl, slice)
sort.Strings(sl)
fmt.Fprintln(w, title)
for _, s := range sl {
fmt.Fprintf(w, "** %s%s%s\n", preString, s, postString)
}
}
}
func dumpStringRefs(w io.Writer, title, preString, postString string, srefs stringRefs) {
if len(srefs) == 0 {
return
}
fmt.Fprintln(w, "====", title)
for _, s := range maps.Keys(srefs) {
fmt.Fprintf(w, "; %s%s%s\n", preString, s, postString)
fmt.Fprintln(w, ":", srefs[s])
}
}
|
Added box/manager/mapstore/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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2021-present Detlef Stern
//-----------------------------------------------------------------------------
package mapstore
import (
"slices"
"zettelstore.de/z/zettel/id"
)
func refsDiff(refsN, refsO id.Slice) (newRefs, remRefs id.Slice) {
npos, opos := 0, 0
for npos < len(refsN) && opos < len(refsO) {
rn, ro := refsN[npos], refsO[opos]
if rn == ro {
npos++
opos++
continue
}
if rn < ro {
newRefs = append(newRefs, rn)
npos++
continue
}
remRefs = append(remRefs, ro)
opos++
}
if npos < len(refsN) {
newRefs = append(newRefs, refsN[npos:]...)
}
if opos < len(refsO) {
remRefs = append(remRefs, refsO[opos:]...)
}
return newRefs, remRefs
}
func addRef(refs id.Slice, ref id.Zid) id.Slice {
hi := len(refs)
for lo := 0; lo < hi; {
m := lo + (hi-lo)/2
if r := refs[m]; r == ref {
return refs
} else if r < ref {
lo = m + 1
} else {
hi = m
}
}
refs = slices.Insert(refs, hi, ref)
return refs
}
func remRefs(refs, rem id.Slice) id.Slice {
if len(refs) == 0 || len(rem) == 0 {
return refs
}
result := make(id.Slice, 0, len(refs))
rpos, dpos := 0, 0
for rpos < len(refs) && dpos < len(rem) {
rr, dr := refs[rpos], rem[dpos]
if rr < dr {
result = append(result, rr)
rpos++
continue
}
if dr < rr {
dpos++
continue
}
rpos++
dpos++
}
if rpos < len(refs) {
result = append(result, refs[rpos:]...)
}
return result
}
func remRef(refs id.Slice, ref id.Zid) id.Slice {
hi := len(refs)
for lo := 0; lo < hi; {
m := lo + (hi-lo)/2
if r := refs[m]; r == ref {
copy(refs[m:], refs[m+1:])
refs = refs[:len(refs)-1]
return refs
} else if r < ref {
lo = m + 1
} else {
hi = m
}
}
return refs
}
|
Added box/manager/mapstore/refs_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 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2021-present Detlef Stern
//-----------------------------------------------------------------------------
package mapstore
import (
"testing"
"zettelstore.de/z/zettel/id"
)
func assertRefs(t *testing.T, i int, got, exp id.Slice) {
t.Helper()
if got == nil && exp != nil {
t.Errorf("%d: got nil, but expected %v", i, exp)
return
}
if got != nil && exp == nil {
t.Errorf("%d: expected nil, but got %v", i, got)
return
}
if len(got) != len(exp) {
t.Errorf("%d: expected len(%v)==%d, but got len(%v)==%d", i, exp, len(exp), got, len(got))
return
}
for p, n := range exp {
if got := got[p]; got != id.Zid(n) {
t.Errorf("%d: pos %d: expected %d, but got %d", i, p, n, got)
}
}
}
func TestRefsDiff(t *testing.T) {
t.Parallel()
testcases := []struct {
in1, in2 id.Slice
exp1, exp2 id.Slice
}{
{nil, nil, nil, nil},
{id.Slice{1}, nil, id.Slice{1}, nil},
{nil, id.Slice{1}, nil, id.Slice{1}},
{id.Slice{1}, id.Slice{1}, nil, nil},
{id.Slice{1, 2}, id.Slice{1}, id.Slice{2}, nil},
{id.Slice{1, 2}, id.Slice{1, 3}, id.Slice{2}, id.Slice{3}},
{id.Slice{1, 4}, id.Slice{1, 3}, id.Slice{4}, id.Slice{3}},
}
for i, tc := range testcases {
got1, got2 := refsDiff(tc.in1, tc.in2)
assertRefs(t, i, got1, tc.exp1)
assertRefs(t, i, got2, tc.exp2)
}
}
func TestAddRef(t *testing.T) {
t.Parallel()
testcases := []struct {
ref id.Slice
zid uint
exp id.Slice
}{
{nil, 5, id.Slice{5}},
{id.Slice{1}, 5, id.Slice{1, 5}},
{id.Slice{10}, 5, id.Slice{5, 10}},
{id.Slice{5}, 5, id.Slice{5}},
{id.Slice{1, 10}, 5, id.Slice{1, 5, 10}},
{id.Slice{1, 5, 10}, 5, id.Slice{1, 5, 10}},
}
for i, tc := range testcases {
got := addRef(tc.ref, id.Zid(tc.zid))
assertRefs(t, i, got, tc.exp)
}
}
func TestRemRefs(t *testing.T) {
t.Parallel()
testcases := []struct {
in1, in2 id.Slice
exp id.Slice
}{
{nil, nil, nil},
{nil, id.Slice{}, nil},
{id.Slice{}, nil, id.Slice{}},
{id.Slice{}, id.Slice{}, id.Slice{}},
{id.Slice{1}, id.Slice{5}, id.Slice{1}},
{id.Slice{10}, id.Slice{5}, id.Slice{10}},
{id.Slice{1, 5}, id.Slice{5}, id.Slice{1}},
{id.Slice{5, 10}, id.Slice{5}, id.Slice{10}},
{id.Slice{1, 10}, id.Slice{5}, id.Slice{1, 10}},
{id.Slice{1}, id.Slice{2, 5}, id.Slice{1}},
{id.Slice{10}, id.Slice{2, 5}, id.Slice{10}},
{id.Slice{1, 5}, id.Slice{2, 5}, id.Slice{1}},
{id.Slice{5, 10}, id.Slice{2, 5}, id.Slice{10}},
{id.Slice{1, 2, 5}, id.Slice{2, 5}, id.Slice{1}},
{id.Slice{2, 5, 10}, id.Slice{2, 5}, id.Slice{10}},
{id.Slice{1, 10}, id.Slice{2, 5}, id.Slice{1, 10}},
{id.Slice{1}, id.Slice{5, 9}, id.Slice{1}},
{id.Slice{10}, id.Slice{5, 9}, id.Slice{10}},
{id.Slice{1, 5}, id.Slice{5, 9}, id.Slice{1}},
{id.Slice{5, 10}, id.Slice{5, 9}, id.Slice{10}},
{id.Slice{1, 5, 9}, id.Slice{5, 9}, id.Slice{1}},
{id.Slice{5, 9, 10}, id.Slice{5, 9}, id.Slice{10}},
{id.Slice{1, 10}, id.Slice{5, 9}, id.Slice{1, 10}},
}
for i, tc := range testcases {
got := remRefs(tc.in1, tc.in2)
assertRefs(t, i, got, tc.exp)
}
}
func TestRemRef(t *testing.T) {
t.Parallel()
testcases := []struct {
ref id.Slice
zid uint
exp id.Slice
}{
{nil, 5, nil},
{id.Slice{}, 5, id.Slice{}},
{id.Slice{5}, 5, id.Slice{}},
{id.Slice{1}, 5, id.Slice{1}},
{id.Slice{10}, 5, id.Slice{10}},
{id.Slice{1, 5}, 5, id.Slice{1}},
{id.Slice{5, 10}, 5, id.Slice{10}},
{id.Slice{1, 5, 10}, 5, id.Slice{1, 10}},
}
for i, tc := range testcases {
got := remRef(tc.ref, id.Zid(tc.zid))
assertRefs(t, i, got, tc.exp)
}
}
|
Deleted box/manager/memstore/memstore.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted box/manager/memstore/refs.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted box/manager/memstore/refs_test.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to box/manager/store/store.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package store contains general index data for storing a zettel index. package store import ( "context" |
| ︙ | ︙ |
Changes to box/manager/store/wordset.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package store // WordSet contains the set of all words, with the count of their occurrences. type WordSet map[string]int |
| ︙ | ︙ |
Changes to box/manager/store/wordset_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package store_test import ( "sort" "testing" |
| ︙ | ︙ |
Changes to box/manager/store/zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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/zettel/id" "zettelstore.de/z/zettel/meta" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package store import ( "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" |
| ︙ | ︙ |
Changes to box/membox/membox.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 membox stores zettel volatile in main memory. package membox import ( "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package membox stores zettel volatile in main memory. package membox import ( "context" |
| ︙ | ︙ |
Changes to box/notify/directory.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 notify import ( "errors" "fmt" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package notify import ( "errors" "fmt" |
| ︙ | ︙ | |||
253 254 255 256 257 258 259 |
return nil, false
}
switch ev.Op {
case Error:
newEntries = nil
if state != DsMissing {
| | | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
return nil, false
}
switch ev.Op {
case Error:
newEntries = nil
if state != DsMissing {
ds.log.Error().Err(ev.Err).Msg("Notifier confused")
}
case Make:
newEntries = make(entrySet)
case List:
if ev.Name == "" {
zids := getNewZids(newEntries)
ds.mx.Lock()
|
| ︙ | ︙ | |||
294 295 296 297 298 299 300 |
ds.mx.Lock()
zid := ds.onDeleteFileEvent(ds.entries, ev.Name)
ds.mx.Unlock()
if zid != id.Invalid {
ds.notifyChange(zid)
}
default:
| | | 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
ds.mx.Lock()
zid := ds.onDeleteFileEvent(ds.entries, ev.Name)
ds.mx.Unlock()
if zid != id.Invalid {
ds.notifyChange(zid)
}
default:
ds.log.Error().Str("event", fmt.Sprintf("%v", ev)).Msg("Unknown zettel notification event")
}
return newEntries, true
}
func getNewZids(entries entrySet) id.Slice {
zids := make(id.Slice, 0, len(entries))
for zid := range entries {
|
| ︙ | ︙ | |||
369 370 371 372 373 374 375 |
zid := seekZid(name)
if zid == id.Invalid {
return id.Invalid
}
entry := fetchdirEntry(entries, zid)
dupName1, dupName2 := ds.updateEntry(entry, name)
if dupName1 != "" {
| | | | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 |
zid := seekZid(name)
if zid == id.Invalid {
return id.Invalid
}
entry := fetchdirEntry(entries, zid)
dupName1, dupName2 := ds.updateEntry(entry, name)
if dupName1 != "" {
ds.log.Info().Str("name", dupName1).Msg("Duplicate content (is ignored)")
if dupName2 != "" {
ds.log.Info().Str("name", dupName2).Msg("Duplicate content (is ignored)")
}
return id.Invalid
}
return zid
}
func (ds *DirService) onDeleteFileEvent(entries entrySet, name string) id.Zid {
|
| ︙ | ︙ |
Changes to box/notify/directory_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 notify import ( "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package notify import ( "testing" |
| ︙ | ︙ |
Changes to box/notify/entry.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 notify import ( "path/filepath" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package notify import ( "path/filepath" |
| ︙ | ︙ | |||
55 56 57 58 59 60 61 |
if contentName := e.ContentName; contentName != "" {
if !extIsMetaAndContent(e.ContentExt) && e.MetaName == "" {
e.MetaName = e.calcBaseName(contentName)
}
return
}
| | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
if contentName := e.ContentName; contentName != "" {
if !extIsMetaAndContent(e.ContentExt) && e.MetaName == "" {
e.MetaName = e.calcBaseName(contentName)
}
return
}
syntax := m.GetDefault(api.KeySyntax, meta.DefaultSyntax)
ext := calcContentExt(syntax, m.YamlSep, getZettelFileSyntax)
metaName := e.MetaName
eimc := extIsMetaAndContent(ext)
if eimc {
if metaName != "" {
ext = contentExtWithMeta(syntax, content)
}
|
| ︙ | ︙ |
Changes to box/notify/fsdir.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 notify import ( "os" "path/filepath" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package notify import ( "os" "path/filepath" |
| ︙ | ︙ | |||
51 52 53 54 55 56 57 |
log.Error().
Str("parentDir", absParentDir).Err(errParent).
Str("path", absPath).Err(err).
Msg("Unable to access Zettel directory and its parent directory")
watcher.Close()
return nil, err
}
| < | | | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
log.Error().
Str("parentDir", absParentDir).Err(errParent).
Str("path", absPath).Err(err).
Msg("Unable to access Zettel directory and its parent directory")
watcher.Close()
return nil, err
}
log.Info().Str("parentDir", absParentDir).Err(errParent).
Msg("Parent of Zettel directory cannot be supervised")
log.Info().Str("path", absPath).
Msg("Zettelstore might not detect a deletion or movement of the Zettel directory")
} else if err != nil {
// Not a problem, if container is not available. It might become available later.
log.Info().Err(err).Str("path", absPath).Msg("Zettel directory currently not available")
}
fsdn := &fsdirNotifier{
log: log,
events: make(chan Event),
refresh: make(chan struct{}),
done: make(chan struct{}),
|
| ︙ | ︙ |
Changes to box/notify/helper.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 notify import ( "archive/zip" "os" "zettelstore.de/z/logger" ) | > > > < < < < < | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2021-present Detlef Stern
//-----------------------------------------------------------------------------
package notify
import (
"archive/zip"
"os"
"zettelstore.de/z/logger"
)
// EntryFetcher return a list of (file) names of an directory.
type EntryFetcher interface {
Fetch() ([]string, error)
}
type dirPathFetcher struct {
dirPath string
|
| ︙ | ︙ |
Changes to box/notify/notify.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 notify provides some notification services to be used by box services. package notify import "fmt" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package notify provides some notification services to be used by box services. package notify import "fmt" |
| ︙ | ︙ |
Changes to box/notify/simpledir.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 notify import ( "path/filepath" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package notify import ( "path/filepath" |
| ︙ | ︙ |
Changes to cmd/cmd_file.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 cmd import ( "context" "flag" "fmt" "io" "os" "zettelstore.de/client.fossil/api" | > > > | | | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package cmd
import (
"context"
"flag"
"fmt"
"io"
"os"
"zettelstore.de/client.fossil/api"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/encoder"
"zettelstore.de/z/parser"
"zettelstore.de/z/zettel"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
// ---------- Subcommand: file -----------------------------------------------
func cmdFile(fs *flag.FlagSet) (int, error) {
enc := fs.Lookup("t").Value.String()
m, inp, err := getInput(fs.Args())
if m == nil {
return 2, err
}
z := parser.ParseZettel(
context.Background(),
zettel.Zettel{
Meta: m,
Content: zettel.NewContent(inp.Src[inp.Pos:]),
},
m.GetDefault(api.KeySyntax, meta.DefaultSyntax),
nil,
)
encdr := encoder.Create(api.Encoder(enc), &encoder.CreateParameter{Lang: m.GetDefault(api.KeyLang, api.ValueLangEN)})
if encdr == nil {
fmt.Fprintf(os.Stderr, "Unknown format %q\n", enc)
return 2, nil
}
|
| ︙ | ︙ |
Changes to cmd/cmd_password.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 cmd import ( "flag" "fmt" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package cmd import ( "flag" "fmt" |
| ︙ | ︙ |
Changes to cmd/cmd_run.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 cmd import ( "context" "flag" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package cmd import ( "context" "flag" |
| ︙ | ︙ |
Changes to cmd/command.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 cmd import ( "flag" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package cmd import ( "flag" |
| ︙ | ︙ |
Changes to cmd/main.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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 cmd import ( "crypto/sha256" "flag" "fmt" "net" "net/url" "os" "runtime/debug" "strconv" "strings" "time" "zettelstore.de/client.fossil/api" "zettelstore.de/z/auth" "zettelstore.de/z/auth/impl" "zettelstore.de/z/box" "zettelstore.de/z/box/compbox" "zettelstore.de/z/box/manager" "zettelstore.de/z/config" | > > > > < | 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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package cmd import ( "crypto/sha256" "flag" "fmt" "net" "net/url" "os" "runtime/debug" "strconv" "strings" "time" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/input" "zettelstore.de/z/auth" "zettelstore.de/z/auth/impl" "zettelstore.de/z/box" "zettelstore.de/z/box/compbox" "zettelstore.de/z/box/manager" "zettelstore.de/z/config" "zettelstore.de/z/kernel" "zettelstore.de/z/logger" "zettelstore.de/z/web/server" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) |
| ︙ | ︙ | |||
230 231 232 233 234 235 236 |
return err == nil
}
func setConfigValue(err error, subsys kernel.Service, key string, val any) error {
if err == nil {
err = kernel.Main.SetConfig(subsys, key, fmt.Sprint(val))
if err != nil {
| | | 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
return err == nil
}
func setConfigValue(err error, subsys kernel.Service, key string, val any) error {
if err == nil {
err = kernel.Main.SetConfig(subsys, key, fmt.Sprint(val))
if err != nil {
kernel.Main.GetKernelLogger().Error().Str("key", key).Str("value", fmt.Sprint(val)).Err(err).Msg("Unable to set configuration")
}
}
return err
}
func executeCommand(name string, args ...string) int {
command, ok := Get(name)
|
| ︙ | ︙ |
Changes to cmd/register.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 cmd provides command generic functions. package cmd // Mention all needed encoders, parsers and stores to have them registered. import ( | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package cmd provides command generic functions. package cmd // Mention all needed encoders, parsers and stores to have them registered. import ( |
| ︙ | ︙ |
Changes to cmd/zettelstore/main.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package main is the starting point for the zettelstore command. package main import ( "os" |
| ︙ | ︙ |
Changes to collect/collect.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 collect provides functions to collect items from a syntax tree. package collect import "zettelstore.de/z/ast" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package collect provides functions to collect items from a syntax tree. package collect import "zettelstore.de/z/ast" |
| ︙ | ︙ |
Changes to collect/collect_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 collect_test provides some unit test for collectors. package collect_test import ( "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package collect_test provides some unit test for collectors. package collect_test import ( "testing" |
| ︙ | ︙ |
Changes to collect/order.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 collect provides functions to collect items from a syntax tree. package collect import "zettelstore.de/z/ast" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package collect provides functions to collect items from a syntax tree. package collect import "zettelstore.de/z/ast" |
| ︙ | ︙ |
Deleted collect/split.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to config/config.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 config provides functions to retrieve runtime configuration data. package config import ( "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package config provides functions to retrieve runtime configuration data. package config import ( "context" |
| ︙ | ︙ |
Changes to docs/development/00010000000000.zettel.
1 2 3 4 5 | id: 00010000000000 title: Developments Notes role: zettel syntax: zmk created: 00010101000000 | | > | 1 2 3 4 5 6 7 8 9 10 11 | id: 00010000000000 title: Developments Notes role: zettel syntax: zmk created: 00010101000000 modified: 20231218182020 * [[Required Software|20210916193200]] * [[Fuzzing tests|20221026184300]] * [[Checklist for Release|20210916194900]] * [[Development tools|20231218181900]] |
Changes to docs/development/20210916193200.zettel.
1 2 3 4 5 | id: 20210916193200 title: Required Software role: zettel syntax: zmk created: 20210916193200 | | | | 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: 20210916193200 title: Required Software role: zettel syntax: zmk created: 20210916193200 modified: 20231213194509 The following software must be installed: * A current, supported [[release of Go|https://go.dev/doc/devel/release]], * [[Fossil|https://fossil-scm.org/]], * [[Git|https://git-scm.org/]] (most dependencies are accessible via Git only). Make sure that the software is in your path, e.g. via: ```sh export PATH=$PATH:/usr/local/go/bin export PATH=$PATH:$(go env GOPATH)/bin ``` The internal build tool need the following software. It can be installed / updated via the build tool itself: ``go run tools/devtools/devtools.go``. Otherwise you can install the software by hand: * [[shadow|https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/shadow]] via ``go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest``, * [[staticcheck|https://staticcheck.io/]] via ``go install honnef.co/go/tools/cmd/staticcheck@latest``, * [[unparam|https://mvdan.cc/unparam]][^[[GitHub|https://github.com/mvdan/unparam]]] via ``go install mvdan.cc/unparam@latest``, * [[govulncheck|https://golang.org/x/vuln/cmd/govulncheck]] via ``go install golang.org/x/vuln/cmd/govulncheck@latest``, |
Changes to docs/development/20210916194900.zettel.
1 2 3 4 5 | id: 20210916194900 title: Checklist for Release role: zettel syntax: zmk created: 20210916194900 | | | | | | 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: 20210916194900 title: Checklist for Release role: zettel syntax: zmk created: 20210916194900 modified: 20231213194631 # Sync with the official repository #* ``fossil sync -u`` # Make sure that there is no workspace defined. #* ``ls ..`` must not have a file ''go.work'', in no parent folder. # Make sure that all dependencies are up-to-date. #* ``cat go.mod`` # Clean up your Go workspace: #* ``go run tools/clean/clean.go`` (alternatively: ``make clean``). # All internal tests must succeed: #* ``go run tools/check/check.go -r`` (alternatively: ``make relcheck``). # The API tests must succeed on every development platform: #* ``go run tools/testapi/testapi.go`` (alternatively: ``make api``). # Run [[linkchecker|https://linkchecker.github.io/linkchecker/]] with the manual: #* ``go run -race cmd/zettelstore/main.go run -d docs/manual`` #* ``linkchecker http://127.0.0.1:23123 2>&1 | tee lc.txt`` #* Check all ""Error: 404 Not Found"" #* Check all ""Error: 403 Forbidden"": allowed for endpoint ''/p'' with encoding ''html'' for those zettel that are accessible only in ''expert-mode''. #* Try to resolve other error messages and warnings #* Warnings about empty content can be ignored |
| ︙ | ︙ | |||
40 41 42 43 44 45 46 | # Disable Fossil autosync mode: #* ``fossil setting autosync off`` # Commit the new release version: #* ``fossil commit --tag release --tag vVERSION -m "Version VERSION"`` #* **Important:** the tag must follow the given pattern, e.g. ''v0.0.15''. Otherwise client will not be able to import ''zettelkasten.de/z''. # Clean up your Go workspace: | | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | # Disable Fossil autosync mode: #* ``fossil setting autosync off`` # Commit the new release version: #* ``fossil commit --tag release --tag vVERSION -m "Version VERSION"`` #* **Important:** the tag must follow the given pattern, e.g. ''v0.0.15''. Otherwise client will not be able to import ''zettelkasten.de/z''. # Clean up your Go workspace: #* ``go run tools/clean/clean.go`` (alternatively: ``make clean``). # Create the release: #* ``go run tools/build/build.go release`` (alternatively: ``make release``). # Remove previous executables: #* ``fossil uv remove --glob '*-PREVVERSION*'`` # Add executables for release: #* ``cd releases`` #* ``fossil uv add *.zip`` #* ``cd ..`` #* Synchronize with main repository: #* ``fossil sync -u`` # Enable autosync: #* ``fossil setting autosync on`` |
Added docs/development/20231218181900.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 |
id: 20231218181900
title: Development tools
role: zettel
syntax: zmk
created: 20231218181956
modified: 20231218184500
The source code contains some tools to assist the development of Zettelstore.
These are located in the ''tools'' directory.
Most tool support the generic option ``-v``, which log internal activities.
Some of the tools can be called easier by using ``make``, that reads in a provided ''Makefile''.
=== Check
The ""check"" tool automates some testing activities.
It is called via the command line:
```
# go run tools/check/check.go
```
There is an additional option ``-r`` to check in advance of a release.
The following checks are executed:
* Execution of unit tests, like ``go test ./...``
* Analyze the source code for general problems, as in ``go vet ./...``
* Tries to find shadowed variable, via ``shadow ./...``
* Performs some additional checks on the source code, via ``staticcheck ./...``
* Checks the usage of function parameters and usage of return values, via ``unparam ./...``.
In case the option ''-r'' is set, the check includes exported functions and internal tests.
* In case option ''-r'' is set, the source code is checked against the vulnerability database, via ``govulncheck ./...``
Please note, that most of the tools above are not automatically installed in a standard Go distribution.
Use the command ""devtools"" to install them.
=== Devtools
The following command installs all needed tools:
```
# go run tooles/devtools/devtools.go
```
It will also automatically update these tools.
=== TestAPI
The following command will perform some high-level tests:
```sh
# go run tools/testapi/testapi.go
```
Basically, a Zettelstore will be started and then API calls will be made to simulate some typical activities with the Zettelstore.
If a Zettelstore is already running on port 23123, this Zettelstore will be used instead.
Even if the API test should clean up later, some zettel might stay created if a test fails.
This feature is used, if you want to have more control on the running Zettelstore.
You should start it with the following command:
```sh
# go run -race cmd/zettelstore/main.go run -c testdata/testbox/19700101000000.zettel
```
This allows you to debug failing API tests.
=== HTMLlint
The following command will check the generated HTML code for validity:
```sh
# go run tools/htmllint/htmllint.go
```
In addition, you might specify the URL od a running Zettelstore.
Otherwise ''http://localhost:23123'' is used.
This command fetches first the list of all zettel.
This list is used to check the generated HTML code (''ZID'' is the paceholder for the zettel identification):
* Check all zettel HTML encodings, via the path ''/z/ZID?enc=html&part=zettel''
* Check all zettel web views, via the path ''/h/ZID''
* The info page of all zettel is checked, via path ''/i/ZID''
* A subset of max. 100 zettel will be checked for the validity of their edit page, via ''/e/ZID''
* 10 random zettel are checked for a valid create form, via ''/c/ZID''
* The zettel rename form will be checked for 100 zettel, via ''/b/ZID''
* A maximum of 200 random zettel are checked for a valid delete dialog, via ''/d/ZID''
Depending on the selected Zettelstore, the command might take a long time.
You can shorten the time, if you disable any zettel query in the footer.
=== Build
The ""build"" tool allows to build the software, either for tests or for a release.
The following command will create a Zettelstore executable for the architecture of the current computer:
```sh
# go tools/build/build.go build
```
You will find the executable in the ''bin'' directory.
A full release will be build in the directory ''releases'', containing ZIP files for the computer architectures ""Linux/amd64"", ""Linux/arm"", ""MacOS/arm64"", ""MacOS/amd64"", and ""Windows/amd64"".
In addition, the manual is also build as a ZIP file:
```sh
# go run tools/build/build.go release
```
If you just want the ZIP file with the manual, please use:
```sh
# go run tools/build/build.go manual
```
In case you want to check the version of the Zettelstore to be build, use:
```sh
# go run tools/build/build.go version
```
=== Clean
To remove the directories ''bin'' and ''releases'', as well as all cached Go libraries used by Zettelstore, execute:
```sh
# go run tools/clean/clean.go
```
Internally, the following commands are executed
```sh
# rm -rf bin releases
# go clean ./...
# go clean -cache -modcache -testcache
```
|
Changes to docs/manual/00001004010000.zettel.
1 2 3 4 5 6 | id: 00001004010000 title: Zettelstore startup configuration role: manual tags: #configuration #manual #zettelstore syntax: zmk created: 20210126175322 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001004010000 title: Zettelstore startup configuration role: manual tags: #configuration #manual #zettelstore syntax: zmk created: 20210126175322 modified: 20240220190138 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. |
| ︙ | ︙ | |||
88 89 90 91 92 93 94 |
Can be changed at runtime, even for specific internal services, with the ''log-level'' command of the [[administrator console|00001004101000#log-level]].
Several specifications are separated by the semicolon character (""'';''"", U+003B).
Each specification consists of an optional service name, together with the colon character (""'':''"", U+003A), followed by the logging level.
Default: ""info"".
| | | | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
Can be changed at runtime, even for specific internal services, with the ''log-level'' command of the [[administrator console|00001004101000#log-level]].
Several specifications are separated by the semicolon character (""'';''"", U+003B).
Each specification consists of an optional service name, together with the colon character (""'':''"", U+003A), followed by the logging level.
Default: ""info"".
Examples: ""error"" will produce just error messages (e.g. no ""info"" messages); ""error;web:debug"" will emit debugging messages for the web component of Zettelstore while still producing error messages for all other components.
When you are familiar to operate the Zettelstore, you might set the level to ""error"" to receive less noisy messages from the Zettelstore.
; [!max-request-size|''max-request-size'']
: Limits the maximum byte size of a web request body to prevent clients from accidentally or maliciously sending a large request and wasting server resources.
The minimum value is 1024.
Default: 16777216 (16 MiB).
; [!owner|''owner'']
: [[Identifier|00001006050000]] of a zettel that contains data about the owner of the Zettelstore.
|
| ︙ | ︙ |
Changes to docs/manual/00001004059700.zettel.
1 2 3 4 5 | id: 00001004059700 title: List of supported logging levels role: manual tags: #configuration #manual #zettelstore syntax: zmk | > | | < | < | < < | | | | 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: 00001004059700 title: List of supported logging levels role: manual tags: #configuration #manual #zettelstore syntax: zmk created: 20211204182643 modified: 20240221134619 Zettelstore supports various levels of logging output. This allows you to see the inner workings of Zettelstore, or to avoid it. Each level has an associated name and number. A lower number signals more logging output. |= Name | Number :| Description | Trace | 1 | Show most of the inner workings | Debug | 2 | Show many internal values that might be interesting for a [[Zettelstore developer|00000000000005]]. | Info | 3 | Display information about an event. In most cases, there is no required action expected from you. | Error | 4 | Notify about an error, which was handled automatically. Something is broken. User intervention may be required, some important functionality may be disabled. Monitor the application. | Mandatory | 5 | Important message will be shown, e.g. the Zettelstore version at startup time. | Disabled | 6 | No messages will be shown If you set the logging level to a certain value, only messages with the same or higher numerical value will be shown. E.g. if you set the logging level to ""error"", no ""trace"", ""debug"", and ""info"" messages are shown, but ""error"" and ""mandatory"" messages. |
Changes to docs/manual/00001006010000.zettel.
1 2 3 4 5 | id: 00001006010000 title: Syntax of Metadata role: manual tags: #manual #syntax #zettelstore syntax: zmk | > | > | | 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: 00001006010000
title: Syntax of Metadata
role: manual
tags: #manual #syntax #zettelstore
syntax: zmk
created: 20210126175322
modified: 20240219193158
The metadata of a zettel is a collection of key-value pairs.
The syntax roughly resembles the internal header of an email ([[RFC5322|https://tools.ietf.org/html/rfc5322]]).
The key is a sequence of alphanumeric characters, a hyphen-minus character (""''-''"", U+002D) is also allowed.
It begins at the first position of a new line.
Uppercase letters of a key are translated to their lowercase equivalence.
A key is separated from its value either by
* a colon character (""'':''""),
* a non-empty sequence of space characters,
* a sequence of space characters, followed by a colon, followed by a sequence of space characters.
A value is a sequence of printable characters.
If the value should be continued in the following line, that following line (""continuation line"") must begin with a non-empty sequence of space characters.
The rest of the following line will be interpreted as the next part of the value.
There can be more than one continuation line for a value.
A non-continuation line that contains a possibly empty sequence of characters, followed by the percent sign character (""''%''"") is treated as a comment line.
It will be ignored.
|
| ︙ | ︙ |
Changes to docs/manual/00001006030000.zettel.
1 2 3 4 5 6 | id: 00001006030000 title: Supported Key Types role: manual tags: #manual #meta #reference #zettel #zettelstore syntax: zmk created: 20210126175322 | | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | id: 00001006030000 title: Supported Key Types role: manual tags: #manual #meta #reference #zettel #zettelstore syntax: zmk created: 20210126175322 modified: 20240219161909 All [[supported metadata keys|00001006020000]] conform to a type. User-defined metadata keys conform also to a type, based on the suffix of the key. |=Suffix|Type | ''-date'' | [[Timestamp|00001006034500]] | ''-number'' | [[Number|00001006033000]] | ''-role'' | [[Word|00001006035500]] | ''-time'' | [[Timestamp|00001006034500]] | ''-title'' | [[Zettelmarkup|00001006036500]] | ''-url'' | [[URL|00001006035000]] | ''-zettel'' | [[Identifier|00001006032000]] | ''-zid'' | [[Identifier|00001006032000]] | ''-zids'' | [[IdentifierSet|00001006032500]] | any other suffix | [[EString|00001006031500]] |
| ︙ | ︙ | |||
35 36 37 38 39 40 41 | * [[IdentifierSet|00001006032500]] * [[Number|00001006033000]] * [[String|00001006033500]] * [[TagSet|00001006034000]] * [[Timestamp|00001006034500]] * [[URL|00001006035000]] * [[Word|00001006035500]] | < | 34 35 36 37 38 39 40 41 | * [[IdentifierSet|00001006032500]] * [[Number|00001006033000]] * [[String|00001006033500]] * [[TagSet|00001006034000]] * [[Timestamp|00001006034500]] * [[URL|00001006035000]] * [[Word|00001006035500]] * [[Zettelmarkup|00001006036500]] |
Deleted docs/manual/00001006036000.zettel.
|
| < < < < < < < < < < < < < < < < < < < < |
Changes to docs/manual/00001007031140.zettel.
1 2 3 4 5 6 | id: 00001007031140 title: Zettelmarkup: Query Transclusion role: manual tags: #manual #search #zettelmarkup #zettelstore syntax: zmk created: 20220809132350 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
id: 00001007031140
title: Zettelmarkup: Query Transclusion
role: manual
tags: #manual #search #zettelmarkup #zettelstore
syntax: zmk
created: 20220809132350
modified: 20240219161800
A query transclusion is specified by the following sequence, starting at the first position in a line: ''{{{query:query-expression}}}''.
The line must literally start with the sequence ''{{{query:''.
Everything after this prefix is interpreted as a [[query expression|00001007700000]].
When evaluated, the query expression is evaluated, often resulting in a list of [[links|00001007040310]] to zettel, matching the query expression.
The result replaces the query transclusion element.
|
| ︙ | ︙ | |||
45 46 47 48 49 50 51 | : Transform the zettel list into an [[Atom 1.0|https://www.rfc-editor.org/rfc/rfc4287]]-conformant document / feed. The document is embedded into the referencing zettel. ; ''RSS'' (aggregate) : Transform the zettel list into a [[RSS 2.0|https://www.rssboard.org/rss-specification]]-conformant document / feed. The document is embedded into the referencing zettel. ; ''KEYS'' (aggregate) : Emit a list of all metadata keys, together with the number of zettel having the key. | | | | > > > | 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 |
: Transform the zettel list into an [[Atom 1.0|https://www.rfc-editor.org/rfc/rfc4287]]-conformant document / feed.
The document is embedded into the referencing zettel.
; ''RSS'' (aggregate)
: Transform the zettel list into a [[RSS 2.0|https://www.rssboard.org/rss-specification]]-conformant document / feed.
The document is embedded into the referencing zettel.
; ''KEYS'' (aggregate)
: Emit a list of all metadata keys, together with the number of zettel having the key.
; ''REDIRECT'', ''REINDEX'' (aggregate)
: Will be ignored.
These actions may have been copied from an existing [[API query call|00001012051400]] (or from a WebUI query), but are here superfluous (and possibly harmful).
; Any [[metadata key|00001006020000]] of type [[Word|00001006035500]] or of type [[TagSet|00001006034000]] (aggregates)
: Emit an aggregate of the given metadata key.
The key can be given in any letter case[^Except if the key name collides with one of the above names. In this case use at least one lower case letter.].
To allow some kind of backward compatibility, an action written in uppercase letters that leads to an empty result list, will be ignored.
In this case the list of selected zettel is returned.
Example:
```zmk
{{{query:tags:#search | tags}}}
```
This is a tag cloud of all tags that are used together with the tag #search:
:::example
{{{query:tags:#search | tags}}}
:::
|
Changes to docs/manual/00001007040324.zettel.
1 2 3 4 5 6 | id: 00001007040324 title: Zettelmarkup: Inline-mode Transclusion role: manual tags: #manual #zettelmarkup #zettelstore syntax: zmk created: 20210811154251 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001007040324 title: Zettelmarkup: Inline-mode Transclusion role: manual tags: #manual #zettelmarkup #zettelstore syntax: zmk created: 20210811154251 modified: 20231222164501 Inline-mode transclusion applies to all zettel that are parsed in a non-trivial way, e.g. as structured textual content. For example, textual content is assumed if the [[syntax|00001006020000#syntax]] of a zettel is ""zmk"" ([[Zettelmarkup|00001007000000]]), or ""markdown"" / ""md"" ([[Markdown|00001008010000]]). Since this type of 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. |
| ︙ | ︙ | |||
34 35 36 37 38 39 40 |
Example: ``{{00001007040322#spin}}`` is rendered as ::{{00001007040322#spin}}::{=example}.
** Just specifying the fragment identifier will reference something in the current page.
This is not allowed, to prevent a possible endless recursion.
* If the reference is a [[hosted or based|00001007040310#link-specifications]] link / URL to an image, that image will be rendered.
| | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
Example: ``{{00001007040322#spin}}`` is rendered as ::{{00001007040322#spin}}::{=example}.
** Just specifying the fragment identifier will reference something in the current page.
This is not allowed, to prevent a possible endless recursion.
* If the reference is a [[hosted or based|00001007040310#link-specifications]] link / URL to an image, that image will be rendered.
Example: ``{{//z/00000000040001}}{alt=Emoji}`` is rendered as ::{{//z/00000000040001}}{alt=Emoji}::{=example}
If no inline-structured elements are found, the transclude specification is replaced by an error message.
To avoid an exploding ""transclusion bomb"", a form of a [[billion laughs attack|https://en.wikipedia.org/wiki/Billion_laughs_attack]] (also known as ""XML bomb""), the total number of transclusions / expansions is limited.
The limit can be controlled by setting the value [[''max-transclusions''|00001004020000#max-transclusions]] of the runtime configuration zettel.
=== See also
[[Full transclusion|00001007031100]] does not work inside some text, but is used for [[block-structured elements|00001007030000]].
|
Changes to docs/manual/00001007720300.zettel.
1 2 3 4 5 6 | id: 00001007720300 title: Query: Context Directive role: manual tags: #manual #search #zettelstore syntax: zmk created: 20230707204706 | | > | | > | | | | > > | | 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: 00001007720300 title: Query: Context Directive role: manual tags: #manual #search #zettelstore syntax: zmk created: 20230707204706 modified: 20240209191045 A context directive calculates the __context__ of a list of zettel identifier. It starts with the keyword ''CONTEXT''. Optionally you may specify some context details, after the keyword ''CONTEXT'', separated by space characters. These are: * ''FULL'': additionally search for zettel with the same tags, * ''BACKWARD'': search for context only though backward links, * ''FORWARD'': search for context only through forward links, * ''COST'': one or more space characters, and a positive integer: set the maximum __cost__ (default: 17), * ''MAX'': one or more space characters, and a positive integer: set the maximum number of context zettel (default: 200). If no ''BACKWARD'' and ''FORWARD'' is specified, a search for context zettel will be done though backward and forward links. The cost of a context zettel is calculated iteratively: * Each of the specified zettel hast a cost of one. * A zettel found as a single folge zettel or single precursor zettel has the cost of the originating zettel, plus one. * A zettel found as a single subordinate zettel or single superior zettel has the cost of the originating zettel, plus 1.2. * A zettel found as a single successor zettel or single predecessor zettel has the cost of the originating zettel, plus seven. * A zettel found via another link without being part of a [[set of zettel identifier|00001006032500]], has the cost of the originating zettel, plus two. * A zettel which is part of a set of zettel identifier, has the cost of the originating zettel, plus one of the four choices above and multiplied with roughly a linear-logarithmic value based on the size of the set. * A zettel with the same tag, has the cost of the originating zettel, plus a linear-logarithmic number based on the number of zettel with this tag. If a zettel belongs to more than one tag compared with the current zettel, there is a discount of 90% per additional tag. This only applies if the ''FULL'' directive was specified. The maximum cost is only checked for all zettel that are not directly reachable from the initial, specified list of zettel. This ensures that initial zettel that have only a highly used tag, will also produce some context zettel. Despite its possibly complicated structure, this algorithm ensures in practice that the zettel context is a list of zettel, where the first elements are ""near"" to the specified zettel and the last elements are more ""distant"" to the specified zettel. It also penalties zettel that acts as a ""hub"" to other zettel, to make it more likely that only relevant zettel appear on the context list. This directive may be specified only once as a query directive. A second occurence of ''CONTEXT'' is interpreted as a [[search expression|00001007701000]]. In most cases it is easier to adjust the maximum cost than to perform another context search, which is relatively expensive in terms of retrieving effort. |
Changes to docs/manual/00001007770000.zettel.
1 2 3 4 5 6 | id: 00001007770000 title: Query: Action List role: manual tags: #manual #search #zettelstore syntax: zmk created: 20230707205246 | | | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | id: 00001007770000 title: Query: Action List role: manual tags: #manual #search #zettelstore syntax: zmk created: 20230707205246 modified: 20240219161813 With a [[list of zettel identifier|00001007710000]], a [[query directives|00001007720000]], or a [[search expression|00001007701000]], a list of zettel is selected. __Actions__ allow to modify this list to a certain degree. Which actions are allowed depends on the context. However, actions are further separated into __parameter action__ and __aggregate actions__. A parameter action just sets a parameter for an aggregate action. An aggregate action transforms the list of selected zettel into a different, aggregate form. Only the first aggregate form is executed, following aggregate actions are ignored. In most contexts, valid actions include the name of metadata keys, at least of type [[Word|00001006035500]] or [[TagSet|00001006034000]]. To allow some kind of backward compatibility, an action written in uppercase letters that leads to an empty result list, will be ignored. In this case the list of selected zettel is returned. |
Changes to docs/manual/00001007780000.zettel.
1 2 3 4 5 6 | id: 00001007780000 title: Formal syntax of query expressions role: manual tags: #manual #reference #search #zettelstore syntax: zmk created: 20220810144539 | | | > | 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: 00001007780000
title: Formal syntax of query expressions
role: manual
tags: #manual #reference #search #zettelstore
syntax: zmk
created: 20220810144539
modified: 20240219155949
```
QueryExpression := ZettelList? QueryDirective* SearchExpression ActionExpression?
ZettelList := (ZID (SPACE+ ZID)*).
ZID := '0'+ ('1' .. '9'') DIGIT*
| ('1' .. '9') DIGIT*.
QueryDirective := ContextDirective
| IdentDirective
| ItemsDirective
| UnlinkedDirective.
ContextDirective := "CONTEXT" (SPACE+ ContextDetail)*.
ContextDetail := "FULL"
| "BACKWARD"
| "FORWARD"
| "COST" SPACE+ PosInt
| "MAX" SPACE+ PosInt.
IdentDirective := IDENT.
ItemsDirective := ITEMS.
UnlinkedDirective := UNLINKED (SPACE+ PHRASE SPACE+ Word)*.
SearchExpression := SearchTerm (SPACE+ SearchTerm)*.
|
| ︙ | ︙ | |||
38 39 40 41 42 43 44 45 46 |
SearchOperator := '!'
| ('!')? ('~' | ':' | '[' | '}').
ExistOperator := '?'
| '!' '?'.
PosInt := '0'
| ('1' .. '9') DIGIT*.
ActionExpression := '|' (Word (SPACE+ Word)*)?
Word := NO-SPACE NO-SPACE*
```
| > > > > > > > > > > | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
SearchOperator := '!'
| ('!')? ('~' | ':' | '[' | '}').
ExistOperator := '?'
| '!' '?'.
PosInt := '0'
| ('1' .. '9') DIGIT*.
ActionExpression := '|' (Word (SPACE+ Word)*)?
Action := Word
| 'ATOM'
| 'KEYS'
| 'N' NO-SPACE*
| 'MAX' PosInt
| 'MIN' PosInt
| 'REDIRECT'
| 'REINDEX'
| 'RSS'
| 'TITLE' (SPACE Word)* .
Word := NO-SPACE NO-SPACE*
```
|
Changes to docs/manual/00001007790000.zettel.
1 2 3 4 5 6 | id: 00001007790000 title: Useful query expressions role: manual tags: #example #manual #search #zettelstore syntax: zmk created: 20220810144539 | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | id: 00001007790000 title: Useful query expressions role: manual tags: #example #manual #search #zettelstore syntax: zmk created: 20220810144539 modified: 20240216003702 |= Query Expression |= Meaning | [[query:role:configuration]] | Zettel that contains some configuration data for the Zettelstore | [[query:ORDER REVERSE created LIMIT 40]] | 40 recently created zettel | [[query:ORDER REVERSE published LIMIT 40]] | 40 recently updated zettel | [[query:PICK 40]] | 40 random zettel, ordered by zettel identifier | [[query:dead?]] | Zettel with invalid / dead links | [[query:backward!? precursor!?]] | Zettel that are not referenced by other zettel | [[query:tags!?]] | Zettel without tags | [[query:expire? ORDER expire]] | Zettel with an expire date, ordered from the nearest to the latest | [[query:00001007700000 CONTEXT]] | Zettel within the context of the [[given zettel|00001007700000]] | [[query:PICK 1 | REDIRECT]] | Redirect to a random zettel |
Changes to docs/manual/00001007903000.zettel.
1 2 3 4 5 6 | id: 00001007903000 title: Zettelmarkup: First Steps role: manual tags: #manual #tutorial #zettelmarkup #zettelstore syntax: zmk created: 20220810182917 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001007903000 title: Zettelmarkup: First Steps role: manual tags: #manual #tutorial #zettelmarkup #zettelstore syntax: zmk created: 20220810182917 modified: 20231201135849 [[Zettelmarkup|00001007000000]] allows you to leave your text as it is, at least in many situations. Some characters have a special meaning, but you have to enter them is a defined way to see a visible change. Zettelmarkup is designed to be used for zettel, which are relatively short. It allows to produce longer texts, but you should probably use a different tool, if you want to produce an scientific paper, to name an example. === Paragraphs |
| ︙ | ︙ | |||
27 28 29 30 31 32 33 | | ''An __emphasized__ word'' | An __emphasized__ word | Put two underscore characters before and after the text you want to emphasize | ''Someone uses **bold** text'' | Someone uses **bold** text | Put two asterisks before and after the text you want to see bold | ''He says: ""I love you!""'' | Her says: ""I love you!"" | Put two quotation mark characters before and after the text you want to quote. You probably see a principle. One nice thing about the quotation mark characters: they are rendered according to the current language. | | | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
| ''An __emphasized__ word'' | An __emphasized__ word | Put two underscore characters before and after the text you want to emphasize
| ''Someone uses **bold** text'' | Someone uses **bold** text | Put two asterisks before and after the text you want to see bold
| ''He says: ""I love you!""'' | Her says: ""I love you!"" | Put two quotation mark characters before and after the text you want to quote.
You probably see a principle.
One nice thing about the quotation mark characters: they are rendered according to the current language.
Examples: ""english""{lang=en}, ""french""{lang=fr}, ""german""{lang=de}.
You will see later, how to change the current language.
=== Lists
Quite often, text consists of lists.
Zettelmarkup supports different types of lists.
The most important lists are:
* Unnumbered lists,
|
| ︙ | ︙ |
Changes to docs/manual/00001007990000.zettel.
1 2 3 4 5 6 | id: 00001007990000 title: Zettelmarkup: Cheat Sheet role: manual tags: #manual #reference #zettelmarkup syntax: zmk created: 20221209191905 | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
id: 00001007990000
title: Zettelmarkup: Cheat Sheet
role: manual
tags: #manual #reference #zettelmarkup
syntax: zmk
created: 20221209191905
modified: 20231201140000
=== Overview
This Zettelmarkup cheat sheet provides a quick overview of many Zettelmarkup elements.
It can not cover any special case.
If you need more information about any of these elements, please refer to the detailed description.
=== Basic Syntax
|[[Text formatting|00001007040100]]|''__italic text__'' → __italic text__, ''**bold text**'' → **bold text**, ''""quoted text""'' → ""quoted text"", ''##marked text##'' → ##marked text##
|[[Text editing|00001007040100]]|''>>inserted text>>'' → >>inserted text>>, ''~~deleted text~~'' → ~~deleted text~~
|[[Text literal formatting|00001007040200]]|''\'\'entered text\'\''' → ''entered text'', ''``source code``'' → ``source code``, ''==text output=='' → ==text output==
|[[Superscript, subscript|00001007040100]]|''m^^2^^'' → m^^2^^, ''H,,2,,O'' → H,,2,,O
|[[Links to other zettel|00001007040310]]|''[[Link text|00001007990000]]'' → [[Link text|00001007990000]]
|[[Links to external resources|00001007040310]]|''[[Zettelstore|https://zettelstore.de]]'' → [[Zettelstore|https://zettelstore.de]]
|[[Embed an image|00001007040322]]|''{{Image text|00000000040001}}'' → {{Image text|00000000040001}}
|[[Embed content of first paragraph|00001007040324]]|''{{00001007990000}}'' → {{00001007990000}}
|
| ︙ | ︙ |
Changes to docs/manual/00001012051400.zettel.
1 2 3 4 5 6 | id: 00001012051400 title: API: Query the list of all zettel role: manual tags: #api #manual #zettelstore syntax: zmk created: 20220912111111 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001012051400 title: API: Query the list of all zettel role: manual tags: #api #manual #zettelstore syntax: zmk created: 20220912111111 modified: 20240219161831 precursor: 00001012051200 The [[endpoint|00001012920000]] ''/z'' also allows you to filter the list of all zettel[^If [[authentication is enabled|00001010040100]], you must include the a valid [[access token|00001012050200]] in the ''Authorization'' header] and optionally to provide some actions. A [[query|00001007700000]] is an optional [[search expression|00001007700000#search-expression]], together with an optional [[list of actions|00001007700000#action-list]] (described below). An empty search expression will select all zettel. An empty list of action, or no valid action, returns the list of all selected zettel metadata. |
| ︙ | ︙ | |||
105 106 107 108 109 110 111 112 113 114 115 | The following actions are supported: ; ''MINn'' (parameter) : Emit only those values with at least __n__ aggregated values. __n__ must be a positive integer, ''MIN'' must be given in upper-case letters. ; ''MAXn'' (parameter) : Emit only those values with at most __n__ aggregated values. __n__ must be a positive integer, ''MAX'' must be given in upper-case letters. ; ''REINDEX'' (aggregate) : Updates the internal search index for the selected zettel, roughly similar to the [[refresh|00001012080500]] API call. It is not really an aggregate, since it is used only for its side effect. It is allowed to specify another aggregate. | > > > | > | > > > > | 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 | The following actions are supported: ; ''MINn'' (parameter) : Emit only those values with at least __n__ aggregated values. __n__ must be a positive integer, ''MIN'' must be given in upper-case letters. ; ''MAXn'' (parameter) : Emit only those values with at most __n__ aggregated values. __n__ must be a positive integer, ''MAX'' must be given in upper-case letters. ; ''REDIRECT'' (aggregate) : Performs a HTTP redirect to the first selected zettel, using HTTP status code 302. The zettel identifier is in the body. ; ''REINDEX'' (aggregate) : Updates the internal search index for the selected zettel, roughly similar to the [[refresh|00001012080500]] API call. It is not really an aggregate, since it is used only for its side effect. It is allowed to specify another aggregate. ; Any [[metadata key|00001006020000]] of type [[Word|00001006035500]] or [[TagSet|00001006034000]] (aggregates) : Emit an aggregate of the given metadata key. The key can be given in any letter case. First, ''REINDEX'' actions are executed, then ''REDIRECT''. If no ''REDIRECT'' was found the first other aggregate action will be executed. To allow some kind of backward compatibility, an action written in uppercase letters that leads to an empty result list, will be ignored. In this case the list of selected zettel is returned. === HTTP Status codes ; ''200'' : Query was successful. ; ''204'' : Query was successful, but results in no content. Most likely, you specified no appropriate aggregator. ; ''302'' : Query was successful, redirect to first zettel in list. ; ''400'' : Request was not valid. There are several reasons for this. Maybe the access bearer token was not valid, or you forgot to specify a valid query. |
Changes to docs/manual/00001012931000.zettel.
1 2 3 4 5 6 | id: 00001012931000 title: Encoding of Sz role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403153903 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001012931000 title: Encoding of Sz role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403153903 modified: 20240123120319 Zettel in a [[Sz encoding|00001012920516]] are represented as a [[symbolic expression|00001012930000]]. To process these symbolic expressions, you need to know, how a specific part of a zettel is represented by a symbolic expression. Basically, each part of a zettel is represented as a list, often a nested list. The first element of that list is always an unique symbol, which denotes that part. The meaning / semantic of all other elements depend on that symbol. |
| ︙ | ︙ | |||
25 26 27 28 29 30 31 | Metadata is represented by a list, where the first element is the symbol ''META''. Following elements represent each metadatum[^""Metadatum"" is used as the singular form of metadata.] of a zettel in standard order. Standard order is: [[Title|00001006020000#title]], [[Role|00001006020000#role]], [[Tags|00001006020000#tags]], [[Syntax|00001006020000#syntax]], all other [[keys|00001006020000]] in alphabetic order. :::syntax | | | | < | | 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 |
Metadata is represented by a list, where the first element is the symbol ''META''.
Following elements represent each metadatum[^""Metadatum"" is used as the singular form of metadata.] of a zettel in standard order.
Standard order is: [[Title|00001006020000#title]], [[Role|00001006020000#role]], [[Tags|00001006020000#tags]], [[Syntax|00001006020000#syntax]], all other [[keys|00001006020000]] in alphabetic order.
:::syntax
__Metadata__ **=** ''(META'' [[__Metadatum__|00001012931200]] … '')''.
:::
=== Content
Zettel content is represented by a block.
:::syntax
__Content__ **=** [[__Block__|#block]].
:::
==== Block
A block is represented by a list with the symbol ''BLOCK'' as the first element.
All following elements represent a nested [[block-structured element|00001007030000]].
:::syntax
[!block|__Block__] **=** ''(BLOCK'' [[__BlockElement__|00001012931400]] … '')''.
:::
==== Inline
Both block-structured elements and some metadata values may contain [[inline-structured elements|00001007040000]].
Similar, inline-structured elements are represented as follows:
:::syntax
__Inline__ **=** ''(INLINE'' [[__InlineElement__|00001012931600]] … '')''.
:::
==== Attribute
[[Attributes|00001007050000]] may be specified for both block- and inline- structured elements.
Attributes are represented by the following schema.
:::syntax
__Attribute__ **=** ''('' **[** [[__AttributeKeyValue__|00001012931800]] … **]** ')'.
:::
Either, there are no attributes.
These are specified by the empty list ''()''.
Or there are attributes.
In this case, the first element of the list must be the symbol ''quote'': ''(quote'' ''('' A,,1,, A,,2,, … A,,n,, '')'''')''.
|
| ︙ | ︙ |
Changes to docs/manual/00001012931200.zettel.
1 2 3 4 5 6 | id: 00001012931200 title: Encoding of Sz Metadata role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161618 | | < < < | | < < < | | | | 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: 00001012931200
title: Encoding of Sz Metadata
role: manual
tags: #api #manual #reference #zettelstore
syntax: zmk
created: 20230403161618
modified: 20240219161848
A single metadata (""metadatum"") is represented by a triple: a symbol representing the type, a symbol representing the key, and either a string or a list that represent the value.
The symbol depends on the [[metadata key type|00001006030000]].
The value also depends somehow on the key type: a set of values is represented as a list, all other values are represented by a string, even if it is a number.
The following table maps key types to symbols and to the type of the value representation.
|=Key Type<| Symbol<| Value<
| [[Credential|00001006031000]] | ''CREDENTIAL'' | string
| [[EString|00001006031500]] | ''EMPTY-STRING'' | string
| [[Identifier|00001006032000]] | ''ZID'' | string
| [[IdentifierSet|00001006032500]] | ''ZID-SET'' | ListValue
| [[Number|00001006033000]] | ''NUMBER'' | string
| [[String|00001006033500]] | ''STRING'' | string
| [[TagSet|00001006034000]] | ''TAG-SET'' | ListValue
| [[Timestamp|00001006034500]] | ''TIMESTAMP'' | string
| [[URL|00001006035000]] | ''URL'' | string
| [[Word|00001006035500]] | ''WORD'' | string
| [[Zettelmarkup|00001006036500]] | ''ZETTELMARKUP'' | string
:::syntax
__ListValue__ **=** ''('' String,,1,, String,,2,, … String,,n,, '')''.
:::
Examples:
* The title of this zettel is represented as: ''(EMPTY-STRING title "Encoding of Sz Metadata")''
* The tags of this zettel are represented as: ''(TAG-SET tags ("#api" "#manual" "#reference" "#zettelstore"))''
|
Changes to docs/manual/00001012931400.zettel.
1 2 3 4 5 6 | id: 00001012931400 title: Encoding of Sz Block Elements role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161803 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001012931400 title: Encoding of Sz Block Elements role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161803 modified: 20240123120132 === ''PARA'' :::syntax __Paragraph__ **=** ''(PARA'' [[__InlineElement__|00001012931600]] … '')''. ::: A paragraph is just a list of inline elements. |
| ︙ | ︙ | |||
46 47 48 49 50 51 52 | === ''DESCRIPTION'' :::syntax __Description__ **=** ''(DESCRIPTION'' __DescriptionTerm__ __DescriptionValues__ __DescriptionTerm__ __DescriptionValues__ … '')''. ::: A description is a sequence of one ore more terms and values. :::syntax | | | | | | | 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 |
=== ''DESCRIPTION''
:::syntax
__Description__ **=** ''(DESCRIPTION'' __DescriptionTerm__ __DescriptionValues__ __DescriptionTerm__ __DescriptionValues__ … '')''.
:::
A description is a sequence of one ore more terms and values.
:::syntax
__DescriptionTerm__ **=** ''('' [[__InlineElement__|00001012931600]] … '')''.
:::
A description term is just an inline-structured value.
:::syntax
__DescriptionValues__ **=** ''(BLOCK'' [[__Block__|00001012931000#block]] … '')''.
:::
Description values are sequences of blocks.
=== ''TABLE''
:::syntax
__Table__ **=** ''(TABLE'' __TableHeader__ __TableRow__ … '')''.
:::
A table is a table header and a sequence of table rows.
:::syntax
__TableHeader__ **=** ''()'' **|** ''('' __TableCell__ … '')''.
:::
A table header is either the empty list or a list of table cells.
:::syntax
__TableRow__ **=** ''('' __TableCell__ … '')''.
:::
A table row is a list of table cells.
=== ''CELL'', ''CELL-*''
There are four kinds of table cells, one for each possible cell alignment.
The structure is the same for all kind.
:::syntax
__TableCell__ **=** __DefaultCell__ **|** __CenterCell__ **|** __LeftCell__ **|** __RightCell__.
|
| ︙ | ︙ | |||
105 106 107 108 109 110 111 | === ''REGION-*'' The following lists specifies different kinds of regions. A region treat a sequence of block elements to be belonging together in certain ways. The have a similar structure. :::syntax | | | | | 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 |
=== ''REGION-*''
The following lists specifies different kinds of regions.
A region treat a sequence of block elements to be belonging together in certain ways.
The have a similar structure.
:::syntax
__BlockRegion__ **=** ''(REGION-BLOCK'' [[__Attributes__|00001012931000#attribute]] ''('' [[__BlockElement__|00001012931400]] … '')'' [[__InlineElement__|00001012931600]] … '')''.
:::
A block region just treats the block to belong in an unspecified way.
Typically, the reason is given in the attributes.
The inline describes the block.
:::syntax
__QuoteRegion__ **=** ''(REGION-QUOTE'' [[__Attributes__|00001012931000#attribute]] ''('' [[__BlockElement__|00001012931400]] … '')'' [[__InlineElement__|00001012931600]] … '')''.
:::
A block region just treats the block to contain a longer quotation.
Attributes may further specify the quotation.
The inline typically describes author / source of the quotation.
:::syntax
__VerseRegion__ **=** ''(REGION-VERSE'' [[__Attributes__|00001012931000#attribute]] ''('' [[__BlockElement__|00001012931400]] … '')'' [[__InlineElement__|00001012931600]] … '')''.
:::
A block region just treats the block to contain a verse.
Soft line break are transformed into hard line breaks to save the structure of the verse / poem.
Attributes may further specify something.
The inline typically describes author / source of the verse.
=== ''VERBATIM-*''
|
| ︙ | ︙ | |||
163 164 165 166 167 168 169 | :::syntax __ZettelVerbatim__ **=** ''(VERBATIM-ZETTEL'' [[__Attributes__|00001012931000#attribute]] String '')''. ::: The string contains text that should be treated as (nested) zettel content. === ''BLOB'' :::syntax | | | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
:::syntax
__ZettelVerbatim__ **=** ''(VERBATIM-ZETTEL'' [[__Attributes__|00001012931000#attribute]] String '')''.
:::
The string contains text that should be treated as (nested) zettel content.
=== ''BLOB''
:::syntax
__BLOB__ **=** ''(BLOB'' ''('' [[__InlineElement__|00001012931600]] … '')'' String,,1,, String,,2,, '')''.
:::
A BLOB contains an image in block mode.
The inline elements states some description.
The first string contains the syntax of the image.
The second string contains the actual image.
If the syntax is ""SVG"", then the second string contains the SVG code.
Otherwise the (binary) image data is encoded with base64.
=== ''TRANSCLUDE''
:::syntax
__Transclude__ **=** ''(TRANSCLUDE'' [[__Attributes__|00001012931000#attribute]] [[__Reference__|00001012931900]] '')''.
:::
A transclude list only occurs for a parsed zettel, but not for a evaluated zettel.
Evaluating a zettel also means that all transclusions are resolved.
__Reference__ denotes the zettel to be transcluded.
|
Changes to docs/manual/00001012931600.zettel.
1 2 3 4 5 6 | id: 00001012931600 title: Encoding of Sz Inline Elements role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161845 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001012931600 title: Encoding of Sz Inline Elements role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161845 modified: 20240122122448 === ''TEXT'' :::syntax __Text__ **=** ''(TEXT'' String '')''. ::: Specifies the string as some text content, typically a word. |
| ︙ | ︙ | |||
120 121 122 123 124 125 126 | The first string is the mark string used in the content. The second string is the mark string transformed to a slug, i.e. transformed to lower case, remove non-ASCII characters. The third string is the slug string, but made unique for the whole zettel. Then follows the marked text as a sequence of __InlineElement__s. === ''ENDNOTE'' :::syntax | | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | The first string is the mark string used in the content. The second string is the mark string transformed to a slug, i.e. transformed to lower case, remove non-ASCII characters. The third string is the slug string, but made unique for the whole zettel. Then follows the marked text as a sequence of __InlineElement__s. === ''ENDNOTE'' :::syntax __Endnote__ **=** ''(ENDNOTE'' [[__Attributes__|00001012931000#attribute]] [[__InlineElement__|00001012931600]] … '')''. ::: Specifies endnote / footnote text. === ''FORMAT-*'' The following lists specifies some inline text formatting. The structure is always the same, the initial symbol denotes the actual formatting. |
| ︙ | ︙ |
Changes to docs/manual/00001012931800.zettel.
1 2 3 4 5 6 | id: 00001012931800 title: Encoding of Sz Attribute Values role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230403161923 | | | | < | | 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: 00001012931800
title: Encoding of Sz Attribute Values
role: manual
tags: #api #manual #reference #zettelstore
syntax: zmk
created: 20230403161923
modified: 20240122115245
An attribute is represented by a single cell.
The first element of the cell references the attribute key, the second value the corresponding value.
:::syntax
__AttributeKeyValue__ **=** ''('' __AttributeKey__ ''.'' __AttributeValue__ '')''.
:::
__AttributeKey__ and __AttributeValue__ are [[string values|00001012930500]].
An empty key denotes the generic attribute.
A key with the value ''"-"'' specifies the default attribute.
In this case, the attribute value is not interpreted.
Some examples:
* ''()'' represents the absence of attributes,
* ''(("-" . ""))'' represent the default attribute,
* ''(("-" . "") ("" . "syntax"))'' adds the generic attribute with the value ""syntax"",
* ''(("lang" . "en"))'' denotes the attribute key ""lang"" with a value ""en"".
|
Changes to docs/manual/00001012931900.zettel.
1 2 3 4 5 6 | id: 00001012931900 title: Encoding of Sz Reference Values role: manual tags: #api #manual #reference #zettelstore syntax: zmk created: 20230405123046 | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
id: 00001012931900
title: Encoding of Sz Reference Values
role: manual
tags: #api #manual #reference #zettelstore
syntax: zmk
created: 20230405123046
modified: 20240122094720
A reference is encoded as the actual reference value, and a symbol describing the state of that actual reference value.
:::syntax
__Reference__ **=** ''('' __ReferenceState__ String '')''.
:::
The string contains the actual reference value.
:::syntax
__ReferenceState__ **=** ''INVALID'' **|** ''ZETTEL'' **|** ''SELF'' **|** ''FOUND'' **|** ''BROKEN'' **|** ''HOSTED'' **|** ''BASED'' **|** ''QUERY'' **|** ''EXTERNAL''.
:::
The meaning of the state symbols corresponds to that of the symbols used for the description of [[link references|00001012931600#link]].
|
| ︙ | ︙ | |||
29 30 31 32 33 34 35 36 37 38 | ; ''FOUND'' : The reference value is a valid reference to an existing zettel. This value is only possible after evaluating the zettel. ; ''BROKEN'' : The reference value is a valid reference to an missing zettel. This value is only possible after evaluating the zettel. ; ''HOSTED'' ; ''BASED'' ; ''QUERY'' ; ''EXTERNAL'' | > > > > | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | ; ''FOUND'' : The reference value is a valid reference to an existing zettel. This value is only possible after evaluating the zettel. ; ''BROKEN'' : The reference value is a valid reference to an missing zettel. This value is only possible after evaluating the zettel. ; ''HOSTED'' : The reference value starts with one slash character, denoting an absolute local reference. ; ''BASED'' : The reference value starts with two slash characters, denoting a local reference interpreted relative to the Zettelstore base URL. ; ''QUERY'' : The reference value contains a query expression. ; ''EXTERNAL'' : The reference value contains a full URL, referencing a resource outside of the Zettelstore server. |
Changes to docs/manual/00001018000000.zettel.
1 2 3 4 5 | id: 00001018000000 title: Troubleshooting role: manual tags: #manual #zettelstore syntax: zmk | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001018000000 title: Troubleshooting role: manual tags: #manual #zettelstore syntax: zmk created: 20211027105921 modified: 20240221134749 This page lists some problems and their solutions that may occur when using your Zettelstore. === Installation * **Problem:** When you double-click on the Zettelstore executable icon, macOS complains that Zettelstore is an application from an unknown developer. Therefore, it will not start Zettelstore. ** **Solution:** Press the ''Ctrl'' key while opening the context menu of the Zettelstore executable with a right-click. |
| ︙ | ︙ | |||
24 25 26 27 28 29 30 | The difference between these two is the missing encryption of user name / password and for the answer of the Zettelstore if you use the ''http://'' schema. To be secure by default, the Zettelstore will not work in an insecure environment. ** **Solution 1:** If you are sure that your communication medium is safe, even if you use the ''http:/\/'' schema (for example, you are running the Zettelstore on the same computer you are working on, or if the Zettelstore is running on a computer in your protected local network), then you could add the entry ''insecure-cookie: true'' in you [[startup configuration|00001004010000#insecure-cookie]] file. ** **Solution 2:** If you are not sure about the security of your communication medium (for example, if unknown persons might use your local network), then you should run an [[external server|00001010090100]] in front of your Zettelstore to enable the use of the ''https://'' schema. === Working with Zettel Files * **Problem:** When you delete a zettel file by removing it from the ""disk"", e.g. by dropping it into the trash folder, by dragging into another folder, or by removing it from the command line, Zettelstore sometimes did not detect that change. | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | The difference between these two is the missing encryption of user name / password and for the answer of the Zettelstore if you use the ''http://'' schema. To be secure by default, the Zettelstore will not work in an insecure environment. ** **Solution 1:** If you are sure that your communication medium is safe, even if you use the ''http:/\/'' schema (for example, you are running the Zettelstore on the same computer you are working on, or if the Zettelstore is running on a computer in your protected local network), then you could add the entry ''insecure-cookie: true'' in you [[startup configuration|00001004010000#insecure-cookie]] file. ** **Solution 2:** If you are not sure about the security of your communication medium (for example, if unknown persons might use your local network), then you should run an [[external server|00001010090100]] in front of your Zettelstore to enable the use of the ''https://'' schema. === Working with Zettel Files * **Problem:** When you delete a zettel file by removing it from the ""disk"", e.g. by dropping it into the trash folder, by dragging into another folder, or by removing it from the command line, Zettelstore sometimes did not detect that change. If you access the zettel via Zettelstore, an error is reported. ** **Explanation:** Sometimes, the operating system does not tell Zettelstore about the removed zettel. This occurs mostly under MacOS. ** **Solution 1:** If you are running Zettelstore in [[""simple-mode""|00001004051100]] or if you have enabled [[''expert-mode''|00001004020000#expert-mode]], you are allowed to refresh the internal data by selecting ""Refresh"" in the Web User Interface (you find it in the menu ""Lists""). ** **Solution 2:** There is an [[API|00001012080500]] call to make Zettelstore aware of this change. ** **Solution 3:** If you have an enabled [[Administrator Console|00001004100000]] you can use the command [[''refresh''|00001004101000#refresh]] to make your changes visible. ** **Solution 4:** You configure the zettel box as [[""simple""|00001004011400]]. |
| ︙ | ︙ |
Changes to encoder/encoder.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 encoder provides a generic interface to encode the abstract syntax // tree into some text form. package encoder import ( | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package encoder provides a generic interface to encode the abstract syntax // tree into some text form. package encoder import ( |
| ︙ | ︙ |
Changes to encoder/encoder_blob_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 encoder_test import ( "testing" "zettelstore.de/client.fossil/api" | > > > | | | 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 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package encoder_test import ( "testing" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/input" "zettelstore.de/z/config" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" _ "zettelstore.de/z/parser/blob" // Allow to use BLOB parser. ) |
| ︙ | ︙ | |||
37 38 39 40 41 42 43 |
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x7e, 0x9b,
0x55, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0x62, 0x00, 0x00, 0x00,
0x06, 0x00, 0x03, 0x36, 0x37, 0x7c, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
0x42, 0x60, 0x82,
},
expect: expectMap{
encoderHTML: `<p><img alt="PNG" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg=="></p>`,
| | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x7e, 0x9b,
0x55, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0x62, 0x00, 0x00, 0x00,
0x06, 0x00, 0x03, 0x36, 0x37, 0x7c, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
0x42, 0x60, 0x82,
},
expect: expectMap{
encoderHTML: `<p><img alt="PNG" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg=="></p>`,
encoderSz: `(BLOCK (BLOB ((TEXT "PNG")) "png" "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg=="))`,
encoderSHTML: `((p (img (@ (alt . "PNG") (src . "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGNiAAAABgADNjd8qAAAAABJRU5ErkJggg==")))))`,
encoderText: "",
encoderZmk: `%% Unable to display BLOB with description 'PNG' and syntax 'png'.`,
},
},
}
|
| ︙ | ︙ |
Changes to encoder/encoder_block_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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 encoder_test
var tcsBlock = []zmkTestCase{
{
descr: "Empty Zettelmarkup should produce near nothing",
| > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2021-present Detlef Stern
//-----------------------------------------------------------------------------
package encoder_test
var tcsBlock = []zmkTestCase{
{
descr: "Empty Zettelmarkup should produce near nothing",
|
| ︙ | ︙ | |||
49 50 51 52 53 54 55 |
},
{
descr: "Rendered block comment",
zmk: "%%%{-}\nRender\n%%%",
expect: expectMap{
encoderHTML: "<!--\nRender\n-->\n",
encoderMD: "",
| | | | | | | | | 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 |
},
{
descr: "Rendered block comment",
zmk: "%%%{-}\nRender\n%%%",
expect: expectMap{
encoderHTML: "<!--\nRender\n-->\n",
encoderMD: "",
encoderSz: `(BLOCK (VERBATIM-COMMENT (("-" . "")) "Render"))`,
encoderSHTML: "((@@@ \"Render\"))",
encoderText: ``,
encoderZmk: useZmk,
},
},
{
descr: "Simple Heading",
zmk: `=== Top Job`,
expect: expectMap{
encoderHTML: "<h2 id=\"top-job\">Top Job</h2>",
encoderMD: "# Top Job",
encoderSz: `(BLOCK (HEADING 1 () "top-job" "top-job" (TEXT "Top") (SPACE) (TEXT "Job")))`,
encoderSHTML: `((h2 (@ (id . "top-job")) "Top" " " "Job"))`,
encoderText: `Top Job`,
encoderZmk: useZmk,
},
},
{
descr: "Simple List",
zmk: "* A\n* B\n* C",
expect: expectMap{
|
| ︙ | ︙ | |||
121 122 123 124 125 126 127 |
},
{
descr: "Thematic break with attribute",
zmk: `---{lang="zmk"}`,
expect: expectMap{
encoderHTML: `<hr lang="zmk">`,
encoderMD: "---",
| | | 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
},
{
descr: "Thematic break with attribute",
zmk: `---{lang="zmk"}`,
expect: expectMap{
encoderHTML: `<hr lang="zmk">`,
encoderMD: "---",
encoderSz: `(BLOCK (THEMATIC (("lang" . "zmk"))))`,
encoderSHTML: `((hr (@ (lang . "zmk"))))`,
encoderText: ``,
encoderZmk: useZmk,
},
},
{
descr: "No list after paragraph",
|
| ︙ | ︙ | |||
155 156 157 158 159 160 161 |
encoderZmk: useZmk,
},
},
{
descr: "Simple List Quote",
zmk: "> ToBeOrNotToBe",
expect: expectMap{
| | | | | | | | | | | | 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 |
encoderZmk: useZmk,
},
},
{
descr: "Simple List Quote",
zmk: "> ToBeOrNotToBe",
expect: expectMap{
encoderHTML: "<blockquote>ToBeOrNotToBe</blockquote>",
encoderMD: "> ToBeOrNotToBe",
encoderSz: `(BLOCK (QUOTATION (INLINE (TEXT "ToBeOrNotToBe"))))`,
encoderSHTML: `((blockquote (@L "ToBeOrNotToBe")))`,
encoderText: "ToBeOrNotToBe",
encoderZmk: useZmk,
},
},
{
descr: "Simple Quote Block",
zmk: "<<<\nToBeOrNotToBe\n<<< Romeo Julia",
expect: expectMap{
encoderHTML: "<blockquote><p>ToBeOrNotToBe</p><cite>Romeo Julia</cite></blockquote>",
encoderMD: "> ToBeOrNotToBe",
encoderSz: `(BLOCK (REGION-QUOTE () ((PARA (TEXT "ToBeOrNotToBe"))) (TEXT "Romeo") (SPACE) (TEXT "Julia")))`,
encoderSHTML: `((blockquote (p "ToBeOrNotToBe") (cite "Romeo" " " "Julia")))`,
encoderText: "ToBeOrNotToBe\nRomeo Julia",
encoderZmk: useZmk,
},
},
{
descr: "Quote Block with multiple paragraphs",
zmk: "<<<\nToBeOr\n\nNotToBe\n<<< Romeo",
expect: expectMap{
encoderHTML: "<blockquote><p>ToBeOr</p><p>NotToBe</p><cite>Romeo</cite></blockquote>",
encoderMD: "> ToBeOr\n\n> NotToBe",
encoderSz: `(BLOCK (REGION-QUOTE () ((PARA (TEXT "ToBeOr")) (PARA (TEXT "NotToBe"))) (TEXT "Romeo")))`,
encoderSHTML: `((blockquote (p "ToBeOr") (p "NotToBe") (cite "Romeo")))`,
encoderText: "ToBeOr\nNotToBe\nRomeo",
encoderZmk: useZmk,
},
},
{
descr: "Verse block",
zmk: `"""
A line
another line
Back
Paragraph
Spacy Para
""" Author`,
expect: expectMap{
encoderHTML: "<div><p>A\u00a0line<br>\u00a0\u00a0another\u00a0line<br>Back</p><p>Paragraph</p><p>\u00a0\u00a0\u00a0\u00a0Spacy\u00a0\u00a0Para</p><cite>Author</cite></div>",
encoderMD: "",
encoderSz: "(BLOCK (REGION-VERSE () ((PARA (TEXT \"A\") (SPACE \"\u00a0\") (TEXT \"line\") (HARD) (SPACE \"\u00a0\u00a0\") (TEXT \"another\") (SPACE \"\u00a0\") (TEXT \"line\") (HARD) (TEXT \"Back\")) (PARA (TEXT \"Paragraph\")) (PARA (SPACE \"\u00a0\u00a0\u00a0\u00a0\") (TEXT \"Spacy\") (SPACE \"\u00a0\u00a0\") (TEXT \"Para\"))) (TEXT \"Author\")))",
encoderSHTML: "((div (p \"A\" \"\u00a0\" \"line\" (br) \"\u00a0\u00a0\" \"another\" \"\u00a0\" \"line\" (br) \"Back\") (p \"Paragraph\") (p \"\u00a0\u00a0\u00a0\u00a0\" \"Spacy\" \"\u00a0\u00a0\" \"Para\") (cite \"Author\")))",
encoderText: "A line\n another line\nBack\nParagraph\n Spacy Para\nAuthor",
encoderZmk: "\"\"\"\nA\u00a0line\\\n\u00a0\u00a0another\u00a0line\\\nBack\nParagraph\n\u00a0\u00a0\u00a0\u00a0Spacy\u00a0\u00a0Para\n\"\"\" Author",
},
},
{
descr: "Span Block",
zmk: `:::
A simple
span
and much more
:::`,
expect: expectMap{
encoderHTML: "<div><p>A simple span and much more</p></div>",
encoderMD: "",
encoderSz: `(BLOCK (REGION-BLOCK () ((PARA (TEXT "A") (SPACE) (TEXT "simple") (SOFT) (SPACE) (TEXT "span") (SOFT) (TEXT "and") (SPACE) (TEXT "much") (SPACE) (TEXT "more")))))`,
encoderSHTML: `((div (p "A" " " "simple" " " " " "span" " " "and" " " "much" " " "more")))`,
encoderText: `A simple span and much more`,
encoderZmk: useZmk,
},
},
{
descr: "Simple Verbatim Code",
|
| ︙ | ︙ | |||
241 242 243 244 245 246 247 |
},
{
descr: "Simple Verbatim Code with visible spaces",
zmk: "```{-}\nHello World\n```",
expect: expectMap{
encoderHTML: "<pre><code>Hello\u2423World</code></pre>",
encoderMD: " Hello World",
| | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
},
{
descr: "Simple Verbatim Code with visible spaces",
zmk: "```{-}\nHello World\n```",
expect: expectMap{
encoderHTML: "<pre><code>Hello\u2423World</code></pre>",
encoderMD: " Hello World",
encoderSz: `(BLOCK (VERBATIM-CODE (("-" . "")) "Hello World"))`,
encoderSHTML: "((pre (code \"Hello\u2423World\")))",
encoderText: "Hello World",
encoderZmk: useZmk,
},
},
{
descr: "Simple Verbatim Eval",
|
| ︙ | ︙ | |||
277 278 279 280 281 282 283 |
},
{
descr: "Simple Description List",
zmk: "; Zettel\n: Paper\n: Note\n; Zettelkasten\n: Slip box",
expect: expectMap{
encoderHTML: "<dl><dt>Zettel</dt><dd><p>Paper</p></dd><dd><p>Note</p></dd><dt>Zettelkasten</dt><dd><p>Slip box</p></dd></dl>",
encoderMD: "",
| | | | | | | | | | 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 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 |
},
{
descr: "Simple Description List",
zmk: "; Zettel\n: Paper\n: Note\n; Zettelkasten\n: Slip box",
expect: expectMap{
encoderHTML: "<dl><dt>Zettel</dt><dd><p>Paper</p></dd><dd><p>Note</p></dd><dt>Zettelkasten</dt><dd><p>Slip box</p></dd></dl>",
encoderMD: "",
encoderSz: `(BLOCK (DESCRIPTION ((TEXT "Zettel")) (BLOCK (BLOCK (PARA (TEXT "Paper"))) (BLOCK (PARA (TEXT "Note")))) ((TEXT "Zettelkasten")) (BLOCK (BLOCK (PARA (TEXT "Slip") (SPACE) (TEXT "box"))))))`,
encoderSHTML: `((dl (dt "Zettel") (dd (p "Paper")) (dd (p "Note")) (dt "Zettelkasten") (dd (p "Slip" " " "box"))))`,
encoderText: "Zettel\nPaper\nNote\nZettelkasten\nSlip box",
encoderZmk: useZmk,
},
},
{
descr: "Description List with paragraphs as item",
zmk: "; Zettel\n: Paper\n\n Note\n; Zettelkasten\n: Slip box",
expect: expectMap{
encoderHTML: "<dl><dt>Zettel</dt><dd><p>Paper</p><p>Note</p></dd><dt>Zettelkasten</dt><dd><p>Slip box</p></dd></dl>",
encoderMD: "",
encoderSz: `(BLOCK (DESCRIPTION ((TEXT "Zettel")) (BLOCK (BLOCK (PARA (TEXT "Paper")) (PARA (TEXT "Note")))) ((TEXT "Zettelkasten")) (BLOCK (BLOCK (PARA (TEXT "Slip") (SPACE) (TEXT "box"))))))`,
encoderSHTML: `((dl (dt "Zettel") (dd (p "Paper") (p "Note")) (dt "Zettelkasten") (dd (p "Slip" " " "box"))))`,
encoderText: "Zettel\nPaper\nNote\nZettelkasten\nSlip box",
encoderZmk: useZmk,
},
},
{
descr: "Description List with keys, but no descriptions",
zmk: "; K1\n: D11\n: D12\n; K2\n; K3\n: D31",
expect: expectMap{
encoderHTML: "<dl><dt>K1</dt><dd><p>D11</p></dd><dd><p>D12</p></dd><dt>K2</dt><dt>K3</dt><dd><p>D31</p></dd></dl>",
encoderMD: "",
encoderSz: `(BLOCK (DESCRIPTION ((TEXT "K1")) (BLOCK (BLOCK (PARA (TEXT "D11"))) (BLOCK (PARA (TEXT "D12")))) ((TEXT "K2")) (BLOCK) ((TEXT "K3")) (BLOCK (BLOCK (PARA (TEXT "D31"))))))`,
encoderSHTML: `((dl (dt "K1") (dd (p "D11")) (dd (p "D12")) (dt "K2") (dt "K3") (dd (p "D31"))))`,
encoderText: "K1\nD11\nD12\nK2\nK3\nD31",
encoderZmk: useZmk,
},
},
{
descr: "Simple Table",
zmk: "|c1|c2|c3\n|d1||d3",
expect: expectMap{
encoderHTML: `<table><tbody><tr><td>c1</td><td>c2</td><td>c3</td></tr><tr><td>d1</td><td></td><td>d3</td></tr></tbody></table>`,
encoderMD: "",
encoderSz: `(BLOCK (TABLE () ((CELL (TEXT "c1")) (CELL (TEXT "c2")) (CELL (TEXT "c3"))) ((CELL (TEXT "d1")) (CELL) (CELL (TEXT "d3")))))`,
encoderSHTML: `((table (tbody (tr (td "c1") (td "c2") (td "c3")) (tr (td "d1") (td) (td "d3")))))`,
encoderText: "c1 c2 c3\nd1 d3",
encoderZmk: useZmk,
},
},
{
descr: "Table with alignment and comment",
zmk: `|h1>|=h2|h3:|
|%--+---+---+
|<c1|c2|:c3|
|f1|f2|=f3`,
expect: expectMap{
encoderHTML: `<table><thead><tr><td class="right">h1</td><td>h2</td><td class="center">h3</td></tr></thead><tbody><tr><td class="left">c1</td><td>c2</td><td class="center">c3</td></tr><tr><td class="right">f1</td><td>f2</td><td class="center">=f3</td></tr></tbody></table>`,
encoderMD: "",
encoderSz: `(BLOCK (TABLE ((CELL-RIGHT (TEXT "h1")) (CELL (TEXT "h2")) (CELL-CENTER (TEXT "h3"))) ((CELL-LEFT (TEXT "c1")) (CELL (TEXT "c2")) (CELL-CENTER (TEXT "c3"))) ((CELL-RIGHT (TEXT "f1")) (CELL (TEXT "f2")) (CELL-CENTER (TEXT "=f3")))))`,
encoderSHTML: `((table (thead (tr (td (@ (class . "right")) "h1") (td "h2") (td (@ (class . "center")) "h3"))) (tbody (tr (td (@ (class . "left")) "c1") (td "c2") (td (@ (class . "center")) "c3")) (tr (td (@ (class . "right")) "f1") (td "f2") (td (@ (class . "center")) "=f3")))))`,
encoderText: "h1 h2 h3\nc1 c2 c3\nf1 f2 =f3",
encoderZmk: `|=h1>|=h2|=h3:
|<c1|c2|c3
|f1|f2|=f3`,
},
},
{
descr: "Simple Endnote",
zmk: `Text[^Endnote]`,
expect: expectMap{
encoderHTML: "<p>Text<sup id=\"fnref:1\"><a class=\"zs-noteref\" href=\"#fn:1\" role=\"doc-noteref\">1</a></sup></p><ol class=\"zs-endnotes\"><li class=\"zs-endnote\" id=\"fn:1\" role=\"doc-endnote\" value=\"1\">Endnote <a class=\"zs-endnote-backref\" href=\"#fnref:1\" role=\"doc-backlink\">\u21a9\ufe0e</a></li></ol>",
encoderMD: "Text",
encoderSz: `(BLOCK (PARA (TEXT "Text") (ENDNOTE () (TEXT "Endnote"))))`,
encoderSHTML: "((p \"Text\" (sup (@ (id . \"fnref:1\")) (a (@ (class . \"zs-noteref\") (href . \"#fn:1\") (role . \"doc-noteref\")) \"1\"))))",
encoderText: "Text Endnote",
encoderZmk: useZmk,
},
},
{
descr: "Nested Endnotes",
zmk: `Text[^Endnote[^Nested]]`,
expect: expectMap{
encoderHTML: "<p>Text<sup id=\"fnref:1\"><a class=\"zs-noteref\" href=\"#fn:1\" role=\"doc-noteref\">1</a></sup></p><ol class=\"zs-endnotes\"><li class=\"zs-endnote\" id=\"fn:1\" role=\"doc-endnote\" value=\"1\">Endnote<sup id=\"fnref:2\"><a class=\"zs-noteref\" href=\"#fn:2\" role=\"doc-noteref\">2</a></sup> <a class=\"zs-endnote-backref\" href=\"#fnref:1\" role=\"doc-backlink\">\u21a9\ufe0e</a></li><li class=\"zs-endnote\" id=\"fn:2\" role=\"doc-endnote\" value=\"2\">Nested <a class=\"zs-endnote-backref\" href=\"#fnref:2\" role=\"doc-backlink\">\u21a9\ufe0e</a></li></ol>",
encoderMD: "Text",
encoderSz: `(BLOCK (PARA (TEXT "Text") (ENDNOTE () (TEXT "Endnote") (ENDNOTE () (TEXT "Nested")))))`,
encoderSHTML: "((p \"Text\" (sup (@ (id . \"fnref:1\")) (a (@ (class . \"zs-noteref\") (href . \"#fn:1\") (role . \"doc-noteref\")) \"1\"))))",
encoderText: "Text Endnote Nested",
encoderZmk: useZmk,
},
},
{
descr: "Transclusion",
zmk: `{{{http://example.com/image}}}{width="100px"}`,
expect: expectMap{
encoderHTML: `<p><img class="external" src="http://example.com/image" width="100px"></p>`,
encoderMD: "",
encoderSz: `(BLOCK (TRANSCLUDE (("width" . "100px")) (EXTERNAL "http://example.com/image")))`,
encoderSHTML: `((p (img (@ (class . "external") (src . "http://example.com/image") (width . "100px")))))`,
encoderText: "",
encoderZmk: useZmk,
},
},
{
descr: "A paragraph with a inline comment only should be empty in HTML",
|
| ︙ | ︙ |
Changes to encoder/encoder_inline_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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 encoder_test
var tcsInline = []zmkTestCase{
{
descr: "Empty Zettelmarkup should produce near nothing (inline)",
| > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2021-present Detlef Stern
//-----------------------------------------------------------------------------
package encoder_test
var tcsInline = []zmkTestCase{
{
descr: "Empty Zettelmarkup should produce near nothing (inline)",
|
| ︙ | ︙ | |||
157 158 159 160 161 162 163 |
},
{
descr: "Quotes formatting (german)",
zmk: `""quotes""{lang=de}`,
expect: expectMap{
encoderHTML: `<span lang="de">„quotes“</span>`,
encoderMD: "<q>quotes</q>",
| | | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
},
{
descr: "Quotes formatting (german)",
zmk: `""quotes""{lang=de}`,
expect: expectMap{
encoderHTML: `<span lang="de">„quotes“</span>`,
encoderMD: "<q>quotes</q>",
encoderSz: `(INLINE (FORMAT-QUOTE (("lang" . "de")) (TEXT "quotes")))`,
encoderSHTML: `((span (@ (lang . "de")) (@H "„") "quotes" (@H "“")))`,
encoderText: `quotes`,
encoderZmk: `""quotes""{lang="de"}`,
},
},
{
descr: "Empty quotes (default)",
|
| ︙ | ︙ | |||
181 182 183 184 185 186 187 |
},
{
descr: "Empty quotes (unknown)",
zmk: `""""{lang=unknown}`,
expect: expectMap{
encoderHTML: `<span lang="unknown">""</span>`,
encoderMD: "<q></q>",
| | | 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
},
{
descr: "Empty quotes (unknown)",
zmk: `""""{lang=unknown}`,
expect: expectMap{
encoderHTML: `<span lang="unknown">""</span>`,
encoderMD: "<q></q>",
encoderSz: `(INLINE (FORMAT-QUOTE (("lang" . "unknown"))))`,
encoderSHTML: `((span (@ (lang . "unknown")) (@H """ """)))`,
encoderText: ``,
encoderZmk: `""""{lang="unknown"}`,
},
},
{
descr: "Nested quotes (default)",
|
| ︙ | ︙ | |||
253 254 255 256 257 258 259 |
},
{
descr: "Code formatting with visible space",
zmk: "``x y``{-}",
expect: expectMap{
encoderHTML: "<code>x\u2423y</code>",
encoderMD: "`x y`",
| | | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
},
{
descr: "Code formatting with visible space",
zmk: "``x y``{-}",
expect: expectMap{
encoderHTML: "<code>x\u2423y</code>",
encoderMD: "`x y`",
encoderSz: `(INLINE (LITERAL-CODE (("-" . "")) "x y"))`,
encoderSHTML: "((code \"x\u2423y\"))",
encoderText: `x y`,
encoderZmk: useZmk,
},
},
{
descr: "HTML in Code formatting",
|
| ︙ | ︙ | |||
313 314 315 316 317 318 319 |
},
{
descr: "Nested Span Quote formatting",
zmk: `::""abc""::{lang=fr}`,
expect: expectMap{
encoderHTML: `<span lang="fr">« abc »</span>`,
encoderMD: "<q>abc</q>",
| | | 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
},
{
descr: "Nested Span Quote formatting",
zmk: `::""abc""::{lang=fr}`,
expect: expectMap{
encoderHTML: `<span lang="fr">« abc »</span>`,
encoderMD: "<q>abc</q>",
encoderSz: `(INLINE (FORMAT-SPAN (("lang" . "fr")) (FORMAT-QUOTE () (TEXT "abc"))))`,
encoderSHTML: `((span (@ (lang . "fr")) (@L (@H "«" " ") "abc" (@H " " "»"))))`,
encoderText: `abc`,
encoderZmk: `::""abc""::{lang="fr"}`,
},
},
{
descr: "Simple Citation",
|
| ︙ | ︙ | |||
372 373 374 375 376 377 378 |
},
{
descr: "Line comment",
zmk: `%%{-} line comment`,
expect: expectMap{
encoderHTML: `<!-- line comment -->`,
encoderMD: "",
| | | | | | 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 |
},
{
descr: "Line comment",
zmk: `%%{-} line comment`,
expect: expectMap{
encoderHTML: `<!-- line comment -->`,
encoderMD: "",
encoderSz: `(INLINE (LITERAL-COMMENT (("-" . "")) "line comment"))`,
encoderSHTML: `((@@ "line comment"))`,
encoderText: ``,
encoderZmk: useZmk,
},
},
{
descr: "Comment after text",
zmk: `Text %%{-} comment`,
expect: expectMap{
encoderHTML: `Text<!-- comment -->`,
encoderMD: "Text",
encoderSz: `(INLINE (TEXT "Text") (LITERAL-COMMENT (("-" . "")) "comment"))`,
encoderSHTML: `("Text" (@@ "comment"))`,
encoderText: `Text`,
encoderZmk: useZmk,
},
},
{
descr: "Comment after text and with -->",
zmk: `Text %%{-} comment --> end`,
expect: expectMap{
encoderHTML: `Text<!-- comment --> end -->`,
encoderMD: "Text",
encoderSz: `(INLINE (TEXT "Text") (LITERAL-COMMENT (("-" . "")) "comment --> end"))`,
encoderSHTML: `("Text" (@@ "comment --> end"))`,
encoderText: `Text`,
encoderZmk: useZmk,
},
},
{
descr: "Simple inline endnote",
zmk: `[^endnote]`,
expect: expectMap{
encoderHTML: `<sup id="fnref:1"><a class="zs-noteref" href="#fn:1" role="doc-noteref">1</a></sup>`,
encoderMD: "",
encoderSz: `(INLINE (ENDNOTE () (TEXT "endnote")))`,
encoderSHTML: `((sup (@ (id . "fnref:1")) (a (@ (class . "zs-noteref") (href . "#fn:1") (role . "doc-noteref")) "1")))`,
encoderText: `endnote`,
encoderZmk: useZmk,
},
},
{
descr: "Simple mark",
|
| ︙ | ︙ | |||
624 625 626 627 628 629 630 |
},
{
descr: "Dummy Embed",
zmk: `{{abc}}`,
expect: expectMap{
encoderHTML: `<img src="abc">`,
encoderMD: "",
| | | 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 |
},
{
descr: "Dummy Embed",
zmk: `{{abc}}`,
expect: expectMap{
encoderHTML: `<img src="abc">`,
encoderMD: "",
encoderSz: `(INLINE (EMBED () (EXTERNAL "abc") ""))`,
encoderSHTML: `((img (@ (src . "abc"))))`,
encoderText: ``,
encoderZmk: useZmk,
},
},
{
descr: "Inline HTML Zettel",
|
| ︙ | ︙ | |||
648 649 650 651 652 653 654 |
},
{
descr: "Inline Text Zettel",
zmk: `@@<hr>@@{="text"}`,
expect: expectMap{
encoderHTML: ``,
encoderMD: "<hr>",
| | | 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 |
},
{
descr: "Inline Text Zettel",
zmk: `@@<hr>@@{="text"}`,
expect: expectMap{
encoderHTML: ``,
encoderMD: "<hr>",
encoderSz: `(INLINE (LITERAL-ZETTEL (("" . "text")) "<hr>"))`,
encoderSHTML: `(())`,
encoderText: `<hr>`,
encoderZmk: useZmk,
},
},
{
descr: "",
|
| ︙ | ︙ |
Changes to encoder/encoder_test.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) 2021-present 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 encoder_test import ( "fmt" "strings" "testing" "zettelstore.de/client.fossil/api" "zettelstore.de/sx.fossil/sxreader" "zettelstore.de/z/ast" "zettelstore.de/z/config" "zettelstore.de/z/encoder" | > > > > < | 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 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package encoder_test import ( "fmt" "strings" "testing" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/input" "zettelstore.de/sx.fossil/sxreader" "zettelstore.de/z/ast" "zettelstore.de/z/config" "zettelstore.de/z/encoder" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" _ "zettelstore.de/z/encoder/htmlenc" // Allow to use HTML encoder. _ "zettelstore.de/z/encoder/mdenc" // Allow to use markdown encoder. _ "zettelstore.de/z/encoder/shtmlenc" // Allow to use SHTML encoder. _ "zettelstore.de/z/encoder/szenc" // Allow to use sz encoder. |
| ︙ | ︙ | |||
112 113 114 115 116 117 118 |
return
}
val, err := sxreader.MakeReader(strings.NewReader(exp)).Read()
if err != nil {
t.Error(err)
return
}
| | | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
return
}
val, err := sxreader.MakeReader(strings.NewReader(exp)).Read()
if err != nil {
t.Error(err)
return
}
got := val.String()
if exp != got {
prefix := fmt.Sprintf("Test #%d", testNum)
if d := descr; d != "" {
prefix += "\nReason: " + d
}
prefix += "\nMode: " + pe.mode()
t.Errorf("%s\n\nExpected: %q\nGot: %q", prefix, exp, got)
|
| ︙ | ︙ |
Changes to encoder/htmlenc/htmlenc.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 htmlenc encodes the abstract syntax tree into HTML5 via zettelstore-client. package htmlenc import ( "io" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- // Package htmlenc encodes the abstract syntax tree into HTML5 via zettelstore-client. package htmlenc import ( "io" |
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
// Create an encoder.
func Create(params *encoder.CreateParameter) *Encoder {
// We need a new transformer every time, because tx.inVerse must be unique.
// If we can refactor it out, the transformer can be created only once.
return &Encoder{
tx: szenc.NewTransformer(),
| | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
// Create an encoder.
func Create(params *encoder.CreateParameter) *Encoder {
// We need a new transformer every time, because tx.inVerse must be unique.
// If we can refactor it out, the transformer can be created only once.
return &Encoder{
tx: szenc.NewTransformer(),
th: shtml.NewEvaluator(1),
lang: params.Lang,
textEnc: textenc.Create(),
}
}
type Encoder struct {
tx *szenc.Transformer
|
| ︙ | ︙ | |||
73 74 75 76 77 78 79 |
xast := he.tx.GetSz(&zn.Ast)
hast, err := he.th.Evaluate(xast, &env)
if err != nil {
return 0, err
}
hen := he.th.Endnotes(&env)
| < < | < | | < | < | | | | < | < | | | | | | | | | 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 |
xast := he.tx.GetSz(&zn.Ast)
hast, err := he.th.Evaluate(xast, &env)
if err != nil {
return 0, err
}
hen := he.th.Endnotes(&env)
var head sx.ListBuilder
head.Add(shtml.SymHead)
head.Add(sx.Nil().Cons(sx.Nil().Cons(sx.Cons(sx.MakeSymbol("charset"), sx.String("utf-8"))).Cons(sxhtml.SymAttr)).Cons(shtml.SymMeta))
head.ExtendBang(hm)
var sb strings.Builder
if hasTitle {
he.textEnc.WriteInlines(&sb, &isTitle)
} else {
sb.Write(zn.Meta.Zid.Bytes())
}
head.Add(sx.MakeList(shtml.SymAttrTitle, sx.String(sb.String())))
var body sx.ListBuilder
body.Add(shtml.SymBody)
if hasTitle {
body.Add(htitle.Cons(shtml.SymH1))
}
body.ExtendBang(hast)
if hen != nil {
body.Add(sx.Cons(shtml.SymHR, nil))
body.Add(hen)
}
doc := sx.MakeList(
sxhtml.SymDoctype,
sx.MakeList(shtml.SymHtml, head.List(), body.List()),
)
gen := sxhtml.NewGenerator(sxhtml.WithNewline)
return gen.WriteHTML(w, doc)
}
// WriteMeta encodes meta data as HTML5.
func (he *Encoder) WriteMeta(w io.Writer, m *meta.Meta, evalMeta encoder.EvalMetaFunc) (int, error) {
env := shtml.MakeEnvironment(he.lang)
hm, err := he.th.Evaluate(he.tx.GetMeta(m, evalMeta), &env)
if err != nil {
return 0, err
}
gen := sxhtml.NewGenerator(sxhtml.WithNewline)
return gen.WriteListHTML(w, hm)
}
func (he *Encoder) WriteContent(w io.Writer, zn *ast.ZettelNode) (int, error) {
return he.WriteBlocks(w, &zn.Ast)
}
// WriteBlocks encodes a block slice.
func (he *Encoder) WriteBlocks(w io.Writer, bs *ast.BlockSlice) (int, error) {
env := shtml.MakeEnvironment(he.lang)
hobj, err := he.th.Evaluate(he.tx.GetSz(bs), &env)
if err == nil {
gen := sxhtml.NewGenerator()
length, err2 := gen.WriteListHTML(w, hobj)
if err2 != nil {
return length, err2
}
l, err2 := gen.WriteHTML(w, he.th.Endnotes(&env))
length += l
return length, err2
}
return 0, err
}
// WriteInlines writes an inline slice to the writer
func (he *Encoder) WriteInlines(w io.Writer, is *ast.InlineSlice) (int, error) {
env := shtml.MakeEnvironment(he.lang)
hobj, err := he.th.Evaluate(he.tx.GetSz(is), &env)
if err == nil {
gen := sxhtml.NewGenerator()
length, err2 := gen.WriteListHTML(w, hobj)
if err2 != nil {
return length, err2
}
return length, nil
}
return 0, err
}
|
Changes to encoder/mdenc/mdenc.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 mdenc encodes the abstract syntax tree back into Markdown. package mdenc import ( "io" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- // Package mdenc encodes the abstract syntax tree back into Markdown. package mdenc import ( "io" |
| ︙ | ︙ | |||
349 350 351 352 353 354 355 |
case ast.LiteralComment, ast.LiteralHTML: // ignore everything
default:
v.b.Write(ln.Content)
}
}
func (v *visitor) writeSpaces(n int) {
| | | 352 353 354 355 356 357 358 359 360 361 362 |
case ast.LiteralComment, ast.LiteralHTML: // ignore everything
default:
v.b.Write(ln.Content)
}
}
func (v *visitor) writeSpaces(n int) {
for range n {
v.b.WriteByte(' ')
}
}
|
Changes to encoder/shtmlenc/shtmlenc.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 shtmlenc encodes the abstract syntax tree into a s-expr which represents HTML. package shtmlenc import ( "io" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2023-present Detlef Stern //----------------------------------------------------------------------------- // Package shtmlenc encodes the abstract syntax tree into a s-expr which represents HTML. package shtmlenc import ( "io" |
| ︙ | ︙ | |||
29 30 31 32 33 34 35 |
// Create a SHTML encoder
func Create(params *encoder.CreateParameter) *Encoder {
// We need a new transformer every time, because tx.inVerse must be unique.
// If we can refactor it out, the transformer can be created only once.
return &Encoder{
tx: szenc.NewTransformer(),
| | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
// Create a SHTML encoder
func Create(params *encoder.CreateParameter) *Encoder {
// We need a new transformer every time, because tx.inVerse must be unique.
// If we can refactor it out, the transformer can be created only once.
return &Encoder{
tx: szenc.NewTransformer(),
th: shtml.NewEvaluator(1),
lang: params.Lang,
}
}
type Encoder struct {
tx *szenc.Transformer
th *shtml.Evaluator
|
| ︙ | ︙ |
Changes to encoder/szenc/szenc.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 szenc encodes the abstract syntax tree into a s-expr for zettel. package szenc import ( "io" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- // Package szenc encodes the abstract syntax tree into a s-expr for zettel. package szenc import ( "io" |
| ︙ | ︙ |
Changes to encoder/szenc/transform.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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2022-present 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 szenc
import (
"encoding/base64"
"fmt"
"strings"
"zettelstore.de/client.fossil/attrs"
"zettelstore.de/client.fossil/sz"
"zettelstore.de/sx.fossil"
"zettelstore.de/z/ast"
"zettelstore.de/z/encoder"
"zettelstore.de/z/zettel/meta"
)
// NewTransformer returns a new transformer to create s-expressions from AST nodes.
func NewTransformer() *Transformer {
| > > > < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | | | | | < < | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < | > | > > > > | | | | | | | | | | | | | | | | | > > > > > > > | | | > > > > > > > > > > > > | | | | | | | | | | | | | | > > > > > > > > > > > > | | < < < < < > | > > > > > > > > > > > > | | | | | < | | | | | 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 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 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2022-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2022-present Detlef Stern
//-----------------------------------------------------------------------------
package szenc
import (
"encoding/base64"
"fmt"
"strings"
"zettelstore.de/client.fossil/attrs"
"zettelstore.de/client.fossil/sz"
"zettelstore.de/sx.fossil"
"zettelstore.de/z/ast"
"zettelstore.de/z/encoder"
"zettelstore.de/z/zettel/meta"
)
// NewTransformer returns a new transformer to create s-expressions from AST nodes.
func NewTransformer() *Transformer {
t := Transformer{}
return &t
}
type Transformer struct {
inVerse bool
}
func (t *Transformer) GetSz(node ast.Node) *sx.Pair {
switch n := node.(type) {
case *ast.BlockSlice:
return t.getBlockList(n).Cons(sz.SymBlock)
case *ast.InlineSlice:
return t.getInlineList(*n).Cons(sz.SymInline)
case *ast.ParaNode:
return t.getInlineList(n.Inlines).Cons(sz.SymPara)
case *ast.VerbatimNode:
return sx.MakeList(
mapGetS(mapVerbatimKindS, n.Kind),
getAttributes(n.Attrs),
sx.String(string(n.Content)),
)
case *ast.RegionNode:
return t.getRegion(n)
case *ast.HeadingNode:
return t.getInlineList(n.Inlines).
Cons(sx.String(n.Fragment)).
Cons(sx.String(n.Slug)).
Cons(getAttributes(n.Attrs)).
Cons(sx.Int64(int64(n.Level))).
Cons(sz.SymHeading)
case *ast.HRuleNode:
return sx.MakeList(sz.SymThematic, getAttributes(n.Attrs))
case *ast.NestedListNode:
return t.getNestedList(n)
case *ast.DescriptionListNode:
return t.getDescriptionList(n)
case *ast.TableNode:
return t.getTable(n)
case *ast.TranscludeNode:
return sx.MakeList(sz.SymTransclude, getAttributes(n.Attrs), getReference(n.Ref))
case *ast.BLOBNode:
return t.getBLOB(n)
case *ast.TextNode:
return sx.MakeList(sz.SymText, sx.String(n.Text))
case *ast.SpaceNode:
if t.inVerse {
return sx.MakeList(sz.SymSpace, sx.String(n.Lexeme))
}
return sx.MakeList(sz.SymSpace)
case *ast.BreakNode:
if n.Hard {
return sx.MakeList(sz.SymHard)
}
return sx.MakeList(sz.SymSoft)
case *ast.LinkNode:
return t.getLink(n)
case *ast.EmbedRefNode:
return t.getInlineList(n.Inlines).
Cons(sx.String(n.Syntax)).
Cons(getReference(n.Ref)).
Cons(getAttributes(n.Attrs)).
Cons(sz.SymEmbed)
case *ast.EmbedBLOBNode:
return t.getEmbedBLOB(n)
case *ast.CiteNode:
return t.getInlineList(n.Inlines).
Cons(sx.String(n.Key)).
Cons(getAttributes(n.Attrs)).
Cons(sz.SymCite)
case *ast.FootnoteNode:
// (ENDNODE attrs InlineElement ...)
return t.getInlineList(n.Inlines).Cons(getAttributes(n.Attrs)).Cons(sz.SymEndnote)
case *ast.MarkNode:
return t.getInlineList(n.Inlines).
Cons(sx.String(n.Fragment)).
Cons(sx.String(n.Slug)).
Cons(sx.String(n.Mark)).
Cons(sz.SymMark)
case *ast.FormatNode:
return t.getInlineList(n.Inlines).
Cons(getAttributes(n.Attrs)).
Cons(mapGetS(mapFormatKindS, n.Kind))
case *ast.LiteralNode:
return sx.MakeList(
mapGetS(mapLiteralKindS, n.Kind),
getAttributes(n.Attrs),
sx.String(string(n.Content)),
)
}
return sx.MakeList(sz.SymUnknown, sx.String(fmt.Sprintf("%T %v", node, node)))
}
var mapVerbatimKindS = map[ast.VerbatimKind]*sx.Symbol{
ast.VerbatimZettel: sz.SymVerbatimZettel,
ast.VerbatimProg: sz.SymVerbatimProg,
ast.VerbatimEval: sz.SymVerbatimEval,
ast.VerbatimMath: sz.SymVerbatimMath,
ast.VerbatimComment: sz.SymVerbatimComment,
ast.VerbatimHTML: sz.SymVerbatimHTML,
}
var mapFormatKindS = map[ast.FormatKind]*sx.Symbol{
ast.FormatEmph: sz.SymFormatEmph,
ast.FormatStrong: sz.SymFormatStrong,
ast.FormatDelete: sz.SymFormatDelete,
ast.FormatInsert: sz.SymFormatInsert,
ast.FormatSuper: sz.SymFormatSuper,
ast.FormatSub: sz.SymFormatSub,
ast.FormatQuote: sz.SymFormatQuote,
ast.FormatMark: sz.SymFormatMark,
ast.FormatSpan: sz.SymFormatSpan,
}
var mapLiteralKindS = map[ast.LiteralKind]*sx.Symbol{
ast.LiteralZettel: sz.SymLiteralZettel,
ast.LiteralProg: sz.SymLiteralProg,
ast.LiteralInput: sz.SymLiteralInput,
ast.LiteralOutput: sz.SymLiteralOutput,
ast.LiteralComment: sz.SymLiteralComment,
ast.LiteralHTML: sz.SymLiteralHTML,
ast.LiteralMath: sz.SymLiteralMath,
}
var mapRegionKindS = map[ast.RegionKind]*sx.Symbol{
ast.RegionSpan: sz.SymRegionBlock,
ast.RegionQuote: sz.SymRegionQuote,
ast.RegionVerse: sz.SymRegionVerse,
}
func (t *Transformer) getRegion(rn *ast.RegionNode) *sx.Pair {
saveInVerse := t.inVerse
if rn.Kind == ast.RegionVerse {
t.inVerse = true
}
symBlocks := t.getBlockList(&rn.Blocks)
t.inVerse = saveInVerse
return t.getInlineList(rn.Inlines).
Cons(symBlocks).
Cons(getAttributes(rn.Attrs)).
Cons(mapGetS(mapRegionKindS, rn.Kind))
}
var mapNestedListKindS = map[ast.NestedListKind]*sx.Symbol{
ast.NestedListOrdered: sz.SymListOrdered,
ast.NestedListUnordered: sz.SymListUnordered,
ast.NestedListQuote: sz.SymListQuote,
}
func (t *Transformer) getNestedList(ln *ast.NestedListNode) *sx.Pair {
nlistObjs := make(sx.Vector, len(ln.Items)+1)
nlistObjs[0] = mapGetS(mapNestedListKindS, ln.Kind)
isCompact := isCompactList(ln.Items)
for i, item := range ln.Items {
if isCompact && len(item) > 0 {
paragraph := t.GetSz(item[0])
nlistObjs[i+1] = paragraph.Tail().Cons(sz.SymInline)
continue
}
itemObjs := make(sx.Vector, len(item))
for j, in := range item {
itemObjs[j] = t.GetSz(in)
}
if isCompact {
nlistObjs[i+1] = sx.MakeList(itemObjs...).Cons(sz.SymInline)
} else {
nlistObjs[i+1] = sx.MakeList(itemObjs...).Cons(sz.SymBlock)
}
}
return sx.MakeList(nlistObjs...)
}
func isCompactList(itemSlice []ast.ItemSlice) bool {
for _, items := range itemSlice {
if len(items) > 1 {
return false
}
if len(items) == 1 {
if _, ok := items[0].(*ast.ParaNode); !ok {
return false
}
}
}
return true
}
func (t *Transformer) getDescriptionList(dn *ast.DescriptionListNode) *sx.Pair {
dlObjs := make(sx.Vector, 2*len(dn.Descriptions)+1)
dlObjs[0] = sz.SymDescription
for i, def := range dn.Descriptions {
dlObjs[2*i+1] = t.getInlineList(def.Term)
descObjs := make(sx.Vector, len(def.Descriptions))
for j, b := range def.Descriptions {
dVal := make(sx.Vector, len(b))
for k, dn := range b {
dVal[k] = t.GetSz(dn)
}
descObjs[j] = sx.MakeList(dVal...).Cons(sz.SymBlock)
}
dlObjs[2*i+2] = sx.MakeList(descObjs...).Cons(sz.SymBlock)
}
return sx.MakeList(dlObjs...)
}
func (t *Transformer) getTable(tn *ast.TableNode) *sx.Pair {
tObjs := make(sx.Vector, len(tn.Rows)+2)
tObjs[0] = sz.SymTable
tObjs[1] = t.getHeader(tn.Header)
for i, row := range tn.Rows {
tObjs[i+2] = t.getRow(row)
}
return sx.MakeList(tObjs...)
}
func (t *Transformer) getHeader(header ast.TableRow) *sx.Pair {
if len(header) == 0 {
return nil
}
return t.getRow(header)
}
func (t *Transformer) getRow(row ast.TableRow) *sx.Pair {
rObjs := make(sx.Vector, len(row))
for i, cell := range row {
rObjs[i] = t.getCell(cell)
}
return sx.MakeList(rObjs...)
}
var alignmentSymbolS = map[ast.Alignment]*sx.Symbol{
ast.AlignDefault: sz.SymCell,
ast.AlignLeft: sz.SymCellLeft,
ast.AlignCenter: sz.SymCellCenter,
ast.AlignRight: sz.SymCellRight,
}
func (t *Transformer) getCell(cell *ast.TableCell) *sx.Pair {
return t.getInlineList(cell.Inlines).Cons(mapGetS(alignmentSymbolS, cell.Align))
}
func (t *Transformer) getBLOB(bn *ast.BLOBNode) *sx.Pair {
var lastObj sx.Object
if bn.Syntax == meta.SyntaxSVG {
lastObj = sx.String(string(bn.Blob))
} else {
lastObj = getBase64String(bn.Blob)
}
return sx.MakeList(
sz.SymBLOB,
t.getInlineList(bn.Description),
sx.String(bn.Syntax),
lastObj,
)
}
var mapRefStateLink = map[ast.RefState]*sx.Symbol{
ast.RefStateInvalid: sz.SymLinkInvalid,
ast.RefStateZettel: sz.SymLinkZettel,
ast.RefStateSelf: sz.SymLinkSelf,
ast.RefStateFound: sz.SymLinkFound,
ast.RefStateBroken: sz.SymLinkBroken,
ast.RefStateHosted: sz.SymLinkHosted,
ast.RefStateBased: sz.SymLinkBased,
ast.RefStateQuery: sz.SymLinkQuery,
ast.RefStateExternal: sz.SymLinkExternal,
}
func (t *Transformer) getLink(ln *ast.LinkNode) *sx.Pair {
return t.getInlineList(ln.Inlines).
Cons(sx.String(ln.Ref.Value)).
Cons(getAttributes(ln.Attrs)).
Cons(mapGetS(mapRefStateLink, ln.Ref.State))
}
func (t *Transformer) getEmbedBLOB(en *ast.EmbedBLOBNode) *sx.Pair {
tail := t.getInlineList(en.Inlines)
if en.Syntax == meta.SyntaxSVG {
tail = tail.Cons(sx.String(string(en.Blob)))
} else {
tail = tail.Cons(getBase64String(en.Blob))
}
return tail.Cons(sx.String(en.Syntax)).Cons(getAttributes(en.Attrs)).Cons(sz.SymEmbedBLOB)
}
func (t *Transformer) getBlockList(bs *ast.BlockSlice) *sx.Pair {
objs := make(sx.Vector, len(*bs))
for i, n := range *bs {
objs[i] = t.GetSz(n)
}
return sx.MakeList(objs...)
}
func (t *Transformer) getInlineList(is ast.InlineSlice) *sx.Pair {
objs := make(sx.Vector, len(is))
for i, n := range is {
objs[i] = t.GetSz(n)
}
return sx.MakeList(objs...)
}
func getAttributes(a attrs.Attributes) sx.Object {
if a.IsEmpty() {
return sx.Nil()
}
keys := a.Keys()
objs := make(sx.Vector, 0, len(keys))
for _, k := range keys {
objs = append(objs, sx.Cons(sx.String(k), sx.String(a[k])))
}
return sx.MakeList(objs...)
}
var mapRefStateS = map[ast.RefState]*sx.Symbol{
ast.RefStateInvalid: sz.SymRefStateInvalid,
ast.RefStateZettel: sz.SymRefStateZettel,
ast.RefStateSelf: sz.SymRefStateSelf,
ast.RefStateFound: sz.SymRefStateFound,
ast.RefStateBroken: sz.SymRefStateBroken,
ast.RefStateHosted: sz.SymRefStateHosted,
ast.RefStateBased: sz.SymRefStateBased,
ast.RefStateQuery: sz.SymRefStateQuery,
ast.RefStateExternal: sz.SymRefStateExternal,
}
func getReference(ref *ast.Reference) *sx.Pair {
return sx.MakeList(mapGetS(mapRefStateS, ref.State), sx.String(ref.Value))
}
var mapMetaTypeS = map[*meta.DescriptionType]*sx.Symbol{
meta.TypeCredential: sz.SymTypeCredential,
meta.TypeEmpty: sz.SymTypeEmpty,
meta.TypeID: sz.SymTypeID,
meta.TypeIDSet: sz.SymTypeIDSet,
meta.TypeNumber: sz.SymTypeNumber,
meta.TypeString: sz.SymTypeString,
meta.TypeTagSet: sz.SymTypeTagSet,
meta.TypeTimestamp: sz.SymTypeTimestamp,
meta.TypeURL: sz.SymTypeURL,
meta.TypeWord: sz.SymTypeWord,
meta.TypeZettelmarkup: sz.SymTypeZettelmarkup,
}
func (t *Transformer) GetMeta(m *meta.Meta, evalMeta encoder.EvalMetaFunc) *sx.Pair {
pairs := m.ComputedPairs()
objs := make(sx.Vector, 0, len(pairs))
for _, p := range pairs {
key := p.Key
ty := m.Type(key)
symType := mapGetS(mapMetaTypeS, ty)
var obj sx.Object
if ty.IsSet {
setList := meta.ListFromValue(p.Value)
setObjs := make(sx.Vector, len(setList))
for i, val := range setList {
setObjs[i] = sx.String(val)
}
obj = sx.MakeList(setObjs...)
} else if ty == meta.TypeZettelmarkup {
is := evalMeta(p.Value)
obj = t.getInlineList(is)
} else {
obj = sx.String(p.Value)
}
objs = append(objs, sx.Nil().Cons(obj).Cons(sx.MakeSymbol(key)).Cons(symType))
}
return sx.MakeList(objs...).Cons(sz.SymMeta)
}
func mapGetS[T comparable](m map[T]*sx.Symbol, k T) *sx.Symbol {
if result, found := m[k]; found {
return result
}
return sx.MakeSymbol(fmt.Sprintf("**%v:NOT-FOUND**", k))
}
func getBase64String(data []byte) sx.String {
var sb strings.Builder
encoder := base64.NewEncoder(base64.StdEncoding, &sb)
_, err := encoder.Write(data)
if err == nil {
|
| ︙ | ︙ |
Changes to encoder/textenc/textenc.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 textenc encodes the abstract syntax tree into its text. package textenc import ( "io" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package textenc encodes the abstract syntax tree into its text. package textenc import ( "io" |
| ︙ | ︙ |
Changes to encoder/write.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 encoder import ( "encoding/base64" "io" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package encoder import ( "encoding/base64" "io" |
| ︙ | ︙ |
Changes to encoder/zmkenc/zmkenc.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 zmkenc encodes the abstract syntax tree back into Zettelmarkup. package zmkenc import ( "fmt" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package zmkenc encodes the abstract syntax tree back into Zettelmarkup. package zmkenc import ( "fmt" |
| ︙ | ︙ | |||
368 369 370 371 372 373 374 |
last = i + 1
continue
}
if i < len(tn.Text)-1 {
s := tn.Text[i : i+2]
if escapeSeqs.Has(s) {
v.b.WriteString(tn.Text[last:i])
| | | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
last = i + 1
continue
}
if i < len(tn.Text)-1 {
s := tn.Text[i : i+2]
if escapeSeqs.Has(s) {
v.b.WriteString(tn.Text[last:i])
for j := range len(s) {
v.b.WriteBytes('\\', s[j])
}
i++
last = i + 1
continue
}
}
|
| ︙ | ︙ | |||
523 524 525 526 527 528 529 |
}
}
v.b.WriteByte('}')
}
func (v *visitor) writeEscaped(s string, toEscape byte) {
last := 0
| | | 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 |
}
}
v.b.WriteByte('}')
}
func (v *visitor) writeEscaped(s string, toEscape byte) {
last := 0
for i := range len(s) {
if b := s[i]; b == toEscape || b == '\\' {
v.b.WriteString(s[last:i])
v.b.WriteBytes('\\', b)
last = i + 1
}
}
v.b.WriteString(s[last:])
}
func syntaxToHTML(a attrs.Attributes) attrs.Attributes {
return a.Clone().Set("", meta.SyntaxHTML).Remove(api.KeySyntax)
}
|
Changes to encoding/atom/atom.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 atom provides an Atom encoding. package atom import ( "bytes" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- // Package atom provides an Atom encoding. package atom import ( "bytes" |
| ︙ | ︙ | |||
77 78 79 80 81 82 83 |
entryUpdated := ""
if val, found := m.Get(api.KeyPublished); found {
if published, err := time.ParseInLocation(id.TimestampLayout, val, time.Local); err == nil {
entryUpdated = published.UTC().Format(time.RFC3339)
}
}
| | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
entryUpdated := ""
if val, found := m.Get(api.KeyPublished); found {
if published, err := time.ParseInLocation(id.TimestampLayout, val, time.Local); err == nil {
entryUpdated = published.UTC().Format(time.RFC3339)
}
}
link := c.NewURLBuilderAbs().SetZid(m.Zid.ZettelID()).String()
buf.WriteString(" <entry>\n")
xml.WriteTag(buf, " ", "title", encoding.TitleAsText(m))
xml.WriteTag(buf, " ", "id", link)
buf.WriteString(` <link rel="self" href="`)
strfun.XMLEscape(buf, link)
buf.WriteString(`"/>` + "\n")
|
| ︙ | ︙ |
Changes to encoding/encoding.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 encoding provides helper functions for encodings. package encoding import ( "time" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- // Package encoding provides helper functions for encodings. package encoding import ( "time" |
| ︙ | ︙ |
Changes to encoding/rss/rss.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 rss provides a RSS encoding. package rss import ( "bytes" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- // Package rss provides a RSS encoding. package rss import ( "bytes" |
| ︙ | ︙ | |||
87 88 89 90 91 92 93 |
itemPublished := ""
if val, found := m.Get(api.KeyPublished); found {
if published, err := time.ParseInLocation(id.TimestampLayout, val, time.Local); err == nil {
itemPublished = published.UTC().Format(time.RFC1123Z)
}
}
| | | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
itemPublished := ""
if val, found := m.Get(api.KeyPublished); found {
if published, err := time.ParseInLocation(id.TimestampLayout, val, time.Local); err == nil {
itemPublished = published.UTC().Format(time.RFC1123Z)
}
}
link := c.NewURLBuilderAbs().SetZid(m.Zid.ZettelID()).String()
buf.WriteString(" <item>\n")
xml.WriteTag(buf, " ", "title", encoding.TitleAsText(m))
xml.WriteTag(buf, " ", "link", link)
xml.WriteTag(buf, " ", "guid", link)
if itemPublished != "" {
xml.WriteTag(buf, " ", "pubDate", itemPublished)
|
| ︙ | ︙ |
Changes to encoding/xml/xml.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 xml provides helper for a XML-based encoding. package xml import ( "bytes" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- // Package xml provides helper for a XML-based encoding. package xml import ( "bytes" |
| ︙ | ︙ |
Changes to evaluator/evaluator.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) 2021-present 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 interprets and evaluates the AST. package evaluator import ( "context" "errors" "fmt" "path" "strconv" "strings" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/attrs" "zettelstore.de/z/ast" "zettelstore.de/z/box" "zettelstore.de/z/config" | > > > > > > > < > | > > > > > > > > > > > > > > > > > > > > > > > > > | | < > > > > | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2021-present Detlef Stern
//-----------------------------------------------------------------------------
// Package evaluator interprets and evaluates the AST.
package evaluator
import (
"bytes"
"context"
"errors"
"fmt"
"path"
"strconv"
"strings"
"zettelstore.de/client.fossil/api"
"zettelstore.de/client.fossil/attrs"
"zettelstore.de/client.fossil/input"
"zettelstore.de/sx.fossil/sxbuiltins"
"zettelstore.de/sx.fossil/sxreader"
"zettelstore.de/z/ast"
"zettelstore.de/z/box"
"zettelstore.de/z/config"
"zettelstore.de/z/parser"
"zettelstore.de/z/parser/cleaner"
"zettelstore.de/z/parser/draw"
"zettelstore.de/z/query"
"zettelstore.de/z/zettel"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
// Port contains all methods to retrieve zettel (or part of it) to evaluate a zettel.
type Port interface {
GetZettel(context.Context, id.Zid) (zettel.Zettel, error)
QueryMeta(ctx context.Context, q *query.Query) ([]*meta.Meta, error)
}
// EvaluateZettel evaluates the given zettel in the given context, with the
// given ports, and the given environment.
func EvaluateZettel(ctx context.Context, port Port, rtConfig config.Config, zn *ast.ZettelNode) {
switch zn.Syntax {
case meta.SyntaxNone:
// AST is empty, evaluate to a description list of metadata.
zn.Ast = evaluateMetadata(zn.Meta)
case meta.SyntaxSxn:
zn.Ast = evaluateSxn(zn.Ast)
default:
EvaluateBlock(ctx, port, rtConfig, &zn.Ast)
}
}
func evaluateSxn(bs ast.BlockSlice) ast.BlockSlice {
// Check for structure made in parser/plain/plain.go:parseSxnBlocks
if len(bs) == 1 {
// If len(bs) > 1 --> an error was found during parsing
if vn, isVerbatim := bs[0].(*ast.VerbatimNode); isVerbatim && vn.Kind == ast.VerbatimProg {
if classAttr, hasClass := vn.Attrs.Get(""); hasClass && classAttr == meta.SyntaxSxn {
rd := sxreader.MakeReader(bytes.NewReader(vn.Content))
if objs, err := rd.ReadAll(); err == nil {
result := make(ast.BlockSlice, len(objs))
for i, obj := range objs {
var buf bytes.Buffer
sxbuiltins.Print(&buf, obj)
result[i] = &ast.VerbatimNode{
Kind: ast.VerbatimProg,
Attrs: attrs.Attributes{"": classAttr},
Content: buf.Bytes(),
}
}
return result
}
}
}
}
return bs
}
// EvaluateBlock evaluates the given block list in the given context, with
// the given ports, and the given environment.
func EvaluateBlock(ctx context.Context, port Port, rtConfig config.Config, bns *ast.BlockSlice) {
evaluateNode(ctx, port, rtConfig, bns)
cleaner.CleanBlockSlice(bns, true)
|
| ︙ | ︙ | |||
257 258 259 260 261 262 263 |
ml, err := e.port.QueryMeta(e.ctx, q)
if err != nil {
if errors.Is(err, &box.ErrNotAllowed{}) {
return nil
}
return makeBlockNode(createInlineErrorText(nil, "Unable", "to", "search", "zettel"))
}
| | | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
ml, err := e.port.QueryMeta(e.ctx, q)
if err != nil {
if errors.Is(err, &box.ErrNotAllowed{}) {
return nil
}
return makeBlockNode(createInlineErrorText(nil, "Unable", "to", "search", "zettel"))
}
result, _ := QueryAction(e.ctx, q, ml, e.rtConfig)
if result != nil {
ast.Walk(e, result)
}
return result
}
func (e *evaluator) checkMaxTransclusions(ref *ast.Reference) ast.InlineNode {
|
| ︙ | ︙ | |||
403 404 405 406 407 408 409 |
if errors.Is(err, &box.ErrNotAllowed{}) {
return nil
}
e.transcludeCount++
return createInlineErrorImage(en)
}
| | | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
if errors.Is(err, &box.ErrNotAllowed{}) {
return nil
}
e.transcludeCount++
return createInlineErrorImage(en)
}
if syntax := zettel.Meta.GetDefault(api.KeySyntax, meta.DefaultSyntax); parser.IsImageFormat(syntax) {
e.updateImageRefNode(en, zettel.Meta, syntax)
return en
} else if !parser.IsASTParser(syntax) {
// Not embeddable.
e.transcludeCount++
return createInlineErrorText(ref, "Not", "embeddable (syntax="+syntax+")")
}
|
| ︙ | ︙ | |||
532 533 534 535 536 537 538 |
func (e *evaluator) evaluateEmbeddedInline(content []byte, syntax string) ast.InlineSlice {
is := parser.ParseInlines(input.NewInput(content), syntax)
ast.Walk(e, &is)
return is
}
func (e *evaluator) evaluateEmbeddedZettel(zettel zettel.Zettel) *ast.ZettelNode {
| | | 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 |
func (e *evaluator) evaluateEmbeddedInline(content []byte, syntax string) ast.InlineSlice {
is := parser.ParseInlines(input.NewInput(content), syntax)
ast.Walk(e, &is)
return is
}
func (e *evaluator) evaluateEmbeddedZettel(zettel zettel.Zettel) *ast.ZettelNode {
zn := parser.ParseZettel(e.ctx, zettel, zettel.Meta.GetDefault(api.KeySyntax, meta.DefaultSyntax), e.rtConfig)
ast.Walk(e, &zn.Ast)
return zn
}
func findInlineSlice(bs *ast.BlockSlice, fragment string) ast.InlineSlice {
if fragment == "" {
return firstInlinesToEmbed(*bs)
|
| ︙ | ︙ |
Changes to evaluator/list.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 ( "bytes" "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package evaluator import ( "bytes" "context" |
| ︙ | ︙ | |||
26 27 28 29 30 31 32 | "zettelstore.de/z/encoding/rss" "zettelstore.de/z/parser" "zettelstore.de/z/query" "zettelstore.de/z/zettel/meta" ) // QueryAction transforms a list of metadata according to query actions into a AST nested list. | | | | | | | | | | | | | > > | > > | | | | | | 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 |
"zettelstore.de/z/encoding/rss"
"zettelstore.de/z/parser"
"zettelstore.de/z/query"
"zettelstore.de/z/zettel/meta"
)
// QueryAction transforms a list of metadata according to query actions into a AST nested list.
func QueryAction(ctx context.Context, q *query.Query, ml []*meta.Meta, rtConfig config.Config) (ast.BlockNode, int) {
ap := actionPara{
ctx: ctx,
q: q,
ml: ml,
kind: ast.NestedListUnordered,
min: -1,
max: -1,
title: rtConfig.GetSiteName(),
}
actions := q.Actions()
if len(actions) == 0 {
return ap.createBlockNodeMeta("")
}
acts := make([]string, 0, len(actions))
for i, act := range actions {
if strings.HasPrefix(act, api.NumberedAction[0:1]) {
ap.kind = ast.NestedListOrdered
continue
}
if strings.HasPrefix(act, api.MinAction) {
if num, err := strconv.Atoi(act[3:]); err == nil && num > 0 {
ap.min = num
continue
}
}
if strings.HasPrefix(act, api.MaxAction) {
if num, err := strconv.Atoi(act[3:]); err == nil && num > 0 {
ap.max = num
continue
}
}
if act == api.TitleAction && i+1 < len(actions) {
ap.title = strings.Join(actions[i+1:], " ")
break
}
if act == api.ReIndexAction {
continue
}
acts = append(acts, act)
}
var firstUnknowAct string
for _, act := range acts {
switch act {
case api.AtomAction:
return ap.createBlockNodeAtom(rtConfig)
case api.RSSAction:
return ap.createBlockNodeRSS(rtConfig)
case api.KeysAction:
return ap.createBlockNodeMetaKeys()
}
key := strings.ToLower(act)
switch meta.Type(key) {
case meta.TypeWord:
return ap.createBlockNodeWord(key)
case meta.TypeTagSet:
return ap.createBlockNodeTagSet(key)
}
if firstUnknowAct == "" {
firstUnknowAct = act
}
}
bn, numItems := ap.createBlockNodeMeta(strings.ToLower(firstUnknowAct))
if bn != nil && numItems == 0 && firstUnknowAct == strings.ToUpper(firstUnknowAct) {
bn, numItems = ap.createBlockNodeMeta("")
}
return bn, numItems
}
type actionPara struct {
ctx context.Context
q *query.Query
ml []*meta.Meta
kind ast.NestedListKind
min int
max int
title string
}
func (ap *actionPara) createBlockNodeWord(key string) (ast.BlockNode, int) {
var buf bytes.Buffer
ccs, bufLen := ap.prepareCatAction(key, &buf)
if len(ccs) == 0 {
return nil, 0
}
items := make([]ast.ItemSlice, 0, len(ccs))
ccs.SortByName()
for _, cat := range ccs {
buf.WriteString(cat.Name)
items = append(items, ast.ItemSlice{ast.CreateParaNode(&ast.LinkNode{
Attrs: nil,
Ref: ast.ParseReference(buf.String()),
Inlines: ast.InlineSlice{&ast.TextNode{Text: cat.Name}},
})})
buf.Truncate(bufLen)
}
return &ast.NestedListNode{
Kind: ap.kind,
Items: items,
Attrs: nil,
}, len(items)
}
func (ap *actionPara) createBlockNodeTagSet(key string) (ast.BlockNode, int) {
var buf bytes.Buffer
ccs, bufLen := ap.prepareCatAction(key, &buf)
if len(ccs) == 0 {
return nil, 0
}
ccs.SortByCount()
ccs = ap.limitTags(ccs)
countMap := ap.calcFontSizes(ccs)
para := make(ast.InlineSlice, 0, len(ccs))
ccs.SortByName()
|
| ︙ | ︙ | |||
159 160 161 162 163 164 165 |
Kind: ast.FormatSuper,
Attrs: nil,
Inlines: ast.InlineSlice{&ast.TextNode{Text: strconv.Itoa(cat.Count)}},
},
)
buf.Truncate(bufLen)
}
| | | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
Kind: ast.FormatSuper,
Attrs: nil,
Inlines: ast.InlineSlice{&ast.TextNode{Text: strconv.Itoa(cat.Count)}},
},
)
buf.Truncate(bufLen)
}
return &ast.ParaNode{Inlines: para}, len(ccs)
}
func (ap *actionPara) limitTags(ccs meta.CountedCategories) meta.CountedCategories {
if min, max := ap.min, ap.max; min > 0 || max > 0 {
if min < 0 {
min = ccs[len(ccs)-1].Count
}
|
| ︙ | ︙ | |||
183 184 185 186 187 188 189 | } return temp } } return ccs } | | | | 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
}
return temp
}
}
return ccs
}
func (ap *actionPara) createBlockNodeMetaKeys() (ast.BlockNode, int) {
arr := make(meta.Arrangement, 128)
for _, m := range ap.ml {
for k := range m.Map() {
arr[k] = append(arr[k], m)
}
}
if len(arr) == 0 {
return nil, 0
}
ccs := arr.Counted()
ccs.SortByName()
var buf bytes.Buffer
bufLen := ap.prepareSimpleQuery(&buf)
items := make([]ast.ItemSlice, 0, len(ccs))
|
| ︙ | ︙ | |||
229 230 231 232 233 234 235 |
&ast.TextNode{Text: ")"},
)})
}
return &ast.NestedListNode{
Kind: ap.kind,
Items: items,
Attrs: nil,
| | | | | | 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 |
&ast.TextNode{Text: ")"},
)})
}
return &ast.NestedListNode{
Kind: ap.kind,
Items: items,
Attrs: nil,
}, len(items)
}
func (ap *actionPara) createBlockNodeMeta(key string) (ast.BlockNode, int) {
if len(ap.ml) == 0 {
return nil, 0
}
items := make([]ast.ItemSlice, 0, len(ap.ml))
for _, m := range ap.ml {
if key != "" {
if _, found := m.Get(key); !found {
continue
}
}
items = append(items, ast.ItemSlice{ast.CreateParaNode(&ast.LinkNode{
Attrs: nil,
Ref: ast.ParseReference(m.Zid.String()),
Inlines: parser.ParseSpacedText(m.GetTitle()),
})})
}
return &ast.NestedListNode{
Kind: ap.kind,
Items: items,
Attrs: nil,
}, len(items)
}
func (ap *actionPara) prepareCatAction(key string, buf *bytes.Buffer) (meta.CountedCategories, int) {
if len(ap.ml) == 0 {
return nil, 0
}
ccs := meta.CreateArrangement(ap.ml, key).Counted()
|
| ︙ | ︙ | |||
290 291 292 293 294 295 296 |
const fontSizes = 6 // Must be the number of CSS classes zs-font-size-* in base.css
const fontSizes64 = float64(fontSizes)
func (*actionPara) calcFontSizes(ccs meta.CountedCategories) map[int]attrs.Attributes {
var fsAttrs [fontSizes]attrs.Attributes
var a attrs.Attributes
| | | 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
const fontSizes = 6 // Must be the number of CSS classes zs-font-size-* in base.css
const fontSizes64 = float64(fontSizes)
func (*actionPara) calcFontSizes(ccs meta.CountedCategories) map[int]attrs.Attributes {
var fsAttrs [fontSizes]attrs.Attributes
var a attrs.Attributes
for i := range fontSizes {
fsAttrs[i] = a.AddClass("zs-font-size-" + strconv.Itoa(i))
}
countMap := make(map[int]int, len(ccs))
for _, cat := range ccs {
countMap[cat.Count]++
}
|
| ︙ | ︙ | |||
340 341 342 343 344 345 346 |
}
}
return result
}
func calcBudget(total, curSize float64) float64 { return math.Round(total / (fontSizes64 - curSize)) }
| | | | | | 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 |
}
}
return result
}
func calcBudget(total, curSize float64) float64 { return math.Round(total / (fontSizes64 - curSize)) }
func (ap *actionPara) createBlockNodeRSS(cfg config.Config) (ast.BlockNode, int) {
var rssConfig rss.Configuration
rssConfig.Setup(ap.ctx, cfg)
rssConfig.Title = ap.title
data := rssConfig.Marshal(ap.q, ap.ml)
return &ast.VerbatimNode{
Kind: ast.VerbatimProg,
Attrs: attrs.Attributes{"lang": "xml"},
Content: data,
}, len(ap.ml)
}
func (ap *actionPara) createBlockNodeAtom(cfg config.Config) (ast.BlockNode, int) {
var atomConfig atom.Configuration
atomConfig.Setup(cfg)
atomConfig.Title = ap.title
data := atomConfig.Marshal(ap.q, ap.ml)
return &ast.VerbatimNode{
Kind: ast.VerbatimProg,
Attrs: attrs.Attributes{"lang": "xml"},
Content: data,
}, len(ap.ml)
}
|
Changes to evaluator/metadata.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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/zettel/meta" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package evaluator import ( "zettelstore.de/z/ast" "zettelstore.de/z/zettel/meta" |
| ︙ | ︙ |
Changes to go.mod.
1 2 | module zettelstore.de/z | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | module zettelstore.de/z go 1.22 require ( github.com/fsnotify/fsnotify v1.7.0 github.com/yuin/goldmark v1.7.0 golang.org/x/crypto v0.20.0 golang.org/x/term v0.17.0 golang.org/x/text v0.14.0 zettelstore.de/client.fossil v0.0.0-20240304164340-1f9d9b832cdd zettelstore.de/sx.fossil v0.0.0-20240304124557-67e0a1799d1d ) require golang.org/x/sys v0.17.0 // indirect |
Changes to go.sum.
1 2 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA= github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= zettelstore.de/client.fossil v0.0.0-20240304164340-1f9d9b832cdd h1:+LUJqi1mvXo/zM9Ii64hcGd1LD3oC8kh5yrmw2fFoco= zettelstore.de/client.fossil v0.0.0-20240304164340-1f9d9b832cdd/go.mod h1:y5zhvVuDHJKFcySEe70537w+5RL50jpeZjqyQuBjfa0= zettelstore.de/sx.fossil v0.0.0-20240304124557-67e0a1799d1d h1:Gl5ZmdNV5wJsNMIQYjAd/sWLq2ng4NP+eglWU7lQP+I= zettelstore.de/sx.fossil v0.0.0-20240304124557-67e0a1799d1d/go.mod h1:/iGHxFXoo6GSV04PUkwaLuFrrCa5LMorxD73iLMAruI= |
Deleted input/entity.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted input/entity_test.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted input/input.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted input/input_test.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted input/runes.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to kernel/impl/auth.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "errors" "sync" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package impl import ( "errors" "sync" |
| ︙ | ︙ | |||
68 69 70 71 72 73 74 |
func (as *authService) Start(*myKernel) error {
as.mxService.Lock()
defer as.mxService.Unlock()
readonlyMode := as.GetNextConfig(kernel.AuthReadonly).(bool)
owner := as.GetNextConfig(kernel.AuthOwner).(id.Zid)
authMgr, err := as.createManager(readonlyMode, owner)
if err != nil {
| | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
func (as *authService) Start(*myKernel) error {
as.mxService.Lock()
defer as.mxService.Unlock()
readonlyMode := as.GetNextConfig(kernel.AuthReadonly).(bool)
owner := as.GetNextConfig(kernel.AuthOwner).(id.Zid)
authMgr, err := as.createManager(readonlyMode, owner)
if err != nil {
as.logger.Error().Err(err).Msg("Unable to create manager")
return err
}
as.logger.Info().Msg("Start Manager")
as.manager = authMgr
return nil
}
|
| ︙ | ︙ |
Changes to kernel/impl/box.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "context" "errors" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package impl import ( "context" "errors" |
| ︙ | ︙ | |||
78 79 80 81 82 83 84 |
}
boxURIs = append(boxURIs, u.(*url.URL))
}
ps.mxService.Lock()
defer ps.mxService.Unlock()
mgr, err := ps.createManager(boxURIs, kern.auth.manager, &kern.cfg)
if err != nil {
| | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
}
boxURIs = append(boxURIs, u.(*url.URL))
}
ps.mxService.Lock()
defer ps.mxService.Unlock()
mgr, err := ps.createManager(boxURIs, kern.auth.manager, &kern.cfg)
if err != nil {
ps.logger.Error().Err(err).Msg("Unable to create manager")
return err
}
ps.logger.Info().Str("location", mgr.Location()).Msg("Start Manager")
if err = mgr.Start(context.Background()); err != nil {
ps.logger.Error().Err(err).Msg("Unable to start manager")
return err
}
kern.cfg.setBox(mgr)
ps.manager = mgr
return nil
}
|
| ︙ | ︙ |
Changes to kernel/impl/cfg.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "context" "errors" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package impl import ( "context" "errors" |
| ︙ | ︙ |
Changes to kernel/impl/cmd.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package impl import ( "fmt" "io" |
| ︙ | ︙ |
Changes to kernel/impl/config.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "errors" "fmt" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package impl import ( "errors" "fmt" |
| ︙ | ︙ |
Changes to kernel/impl/core.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" "net" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package impl import ( "fmt" "net" |
| ︙ | ︙ |
Changes to kernel/impl/impl.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "errors" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package impl provides the kernel implementation. package impl import ( "errors" |
| ︙ | ︙ | |||
67 68 69 70 71 72 73 | srv service srvnum kernel.Service } type serviceDependency map[kernel.Service][]kernel.Service const ( defaultNormalLogLevel = logger.InfoLevel | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
srv service
srvnum kernel.Service
}
type serviceDependency map[kernel.Service][]kernel.Service
const (
defaultNormalLogLevel = logger.InfoLevel
defaultSimpleLogLevel = logger.ErrorLevel
)
// create a new kernel.
func init() {
kernel.Main = createKernel()
}
|
| ︙ | ︙ | |||
95 96 97 98 99 100 101 |
kernel.AuthService: {&kern.auth, "auth", defaultNormalLogLevel},
kernel.BoxService: {&kern.box, "box", defaultNormalLogLevel},
kernel.WebService: {&kern.web, "web", defaultNormalLogLevel},
}
kern.srvNames = make(map[string]serviceData, len(kern.srvs))
for key, srvD := range kern.srvs {
if _, ok := kern.srvNames[srvD.name]; ok {
| | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
kernel.AuthService: {&kern.auth, "auth", defaultNormalLogLevel},
kernel.BoxService: {&kern.box, "box", defaultNormalLogLevel},
kernel.WebService: {&kern.web, "web", defaultNormalLogLevel},
}
kern.srvNames = make(map[string]serviceData, len(kern.srvs))
for key, srvD := range kern.srvs {
if _, ok := kern.srvNames[srvD.name]; ok {
kern.logger.Error().Str("service", srvD.name).Msg("Service data already set, ignore")
}
kern.srvNames[srvD.name] = serviceData{srvD.srv, key}
l := logger.New(lw, strings.ToUpper(srvD.name)).SetLevel(srvD.logLevel)
kern.logger.Debug().Str("service", srvD.name).Msg("Initialize")
srvD.srv.Initialize(l)
}
kern.depStart = serviceDependency{
|
| ︙ | ︙ | |||
162 163 164 165 166 167 168 |
logger.Mandatory().Msg("Licensed under the latest version of the EUPL (European Union Public License)")
if configFilename != "" {
logger.Mandatory().Str("filename", configFilename).Msg("Configuration file found")
} else {
logger.Mandatory().Msg("No configuration file found / used")
}
if kern.core.GetCurConfig(kernel.CoreDebug).(bool) {
| | | | | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
logger.Mandatory().Msg("Licensed under the latest version of the EUPL (European Union Public License)")
if configFilename != "" {
logger.Mandatory().Str("filename", configFilename).Msg("Configuration file found")
} else {
logger.Mandatory().Msg("No configuration file found / used")
}
if kern.core.GetCurConfig(kernel.CoreDebug).(bool) {
logger.Info().Msg("----------------------------------------")
logger.Info().Msg("DEBUG MODE, DO NO USE THIS IN PRODUCTION")
logger.Info().Msg("----------------------------------------")
}
if kern.auth.GetCurConfig(kernel.AuthReadonly).(bool) {
logger.Info().Msg("Read-only mode")
}
}
if lineServer {
port := kern.core.GetNextConfig(kernel.CorePort).(int)
|
| ︙ | ︙ | |||
274 275 276 277 278 279 280 |
// LogRecover outputs some information about the previous panic.
func (kern *myKernel) LogRecover(name string, recoverInfo interface{}) bool {
return kern.doLogRecover(name, recoverInfo)
}
func (kern *myKernel) doLogRecover(name string, recoverInfo interface{}) bool {
stack := debug.Stack()
| | | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
// LogRecover outputs some information about the previous panic.
func (kern *myKernel) LogRecover(name string, recoverInfo interface{}) bool {
return kern.doLogRecover(name, recoverInfo)
}
func (kern *myKernel) doLogRecover(name string, recoverInfo interface{}) bool {
stack := debug.Stack()
kern.logger.Error().Str("recovered_from", fmt.Sprint(recoverInfo)).Bytes("stack", stack).Msg(name)
kern.core.updateRecoverInfo(name, recoverInfo, stack)
return true
}
// --- Profiling ---------------------------------------------------------
var errProfileInWork = errors.New("already profiling")
|
| ︙ | ︙ |
Changes to kernel/impl/log.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "os" "sync" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package impl import ( "os" "sync" |
| ︙ | ︙ | |||
72 73 74 75 76 77 78 | } buf = append(buf, msg...) buf = append(buf, details...) buf = append(buf, '\n') _, err := os.Stdout.Write(buf) klw.mx.Unlock() | < < < | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
}
buf = append(buf, msg...)
buf = append(buf, details...)
buf = append(buf, '\n')
_, err := os.Stdout.Write(buf)
klw.mx.Unlock()
return err
}
func addTimestamp(buf *[]byte, ts time.Time) {
year, month, day := ts.Date()
itoa(buf, year, 4)
*buf = append(*buf, '-')
|
| ︙ | ︙ | |||
122 123 124 125 126 127 128 |
defer klw.mx.RUnlock()
if !klw.full {
if klw.writePos == 0 {
return nil
}
result := make([]kernel.LogEntry, klw.writePos)
| | | | 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 |
defer klw.mx.RUnlock()
if !klw.full {
if klw.writePos == 0 {
return nil
}
result := make([]kernel.LogEntry, klw.writePos)
for i := range klw.writePos {
copyE2E(&result[i], &klw.data[i])
}
return result
}
result := make([]kernel.LogEntry, cap(klw.data))
pos := 0
for j := klw.writePos; j < cap(klw.data); j++ {
copyE2E(&result[pos], &klw.data[j])
pos++
}
for j := range klw.writePos {
copyE2E(&result[pos], &klw.data[j])
pos++
}
return result
}
func (klw *kernelLogWriter) getLastLogTime() time.Time {
|
| ︙ | ︙ |
Changes to kernel/impl/server.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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 (
"bufio"
"net"
)
func startLineServer(kern *myKernel, listenAddr string) error {
ln, err := net.Listen("tcp", listenAddr)
if err != nil {
| > > > | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2021-present Detlef Stern
//-----------------------------------------------------------------------------
package impl
import (
"bufio"
"net"
)
func startLineServer(kern *myKernel, listenAddr string) error {
ln, err := net.Listen("tcp", listenAddr)
if err != nil {
kern.logger.Error().Err(err).Msg("Unable to start administration console")
return err
}
kern.logger.Mandatory().Str("listen", listenAddr).Msg("Start administration console")
go func() { lineServer(ln, kern) }()
return nil
}
|
| ︙ | ︙ |
Changes to kernel/impl/web.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "errors" "net" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package impl import ( "errors" "net" |
| ︙ | ︙ | |||
142 143 144 145 146 147 148 |
secureCookie := ws.GetNextConfig(kernel.WebSecureCookie).(bool)
maxRequestSize := ws.GetNextConfig(kernel.WebMaxRequestSize).(int64)
if maxRequestSize < 1024 {
maxRequestSize = 1024
}
if !strings.HasSuffix(baseURL, urlPrefix) {
| | | | | | 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 |
secureCookie := ws.GetNextConfig(kernel.WebSecureCookie).(bool)
maxRequestSize := ws.GetNextConfig(kernel.WebMaxRequestSize).(int64)
if maxRequestSize < 1024 {
maxRequestSize = 1024
}
if !strings.HasSuffix(baseURL, urlPrefix) {
ws.logger.Error().Str("base-url", baseURL).Str("url-prefix", urlPrefix).Msg(
"url-prefix is not a suffix of base-url")
return errWrongBasePrefix
}
if lap := netip.MustParseAddrPort(listenAddr); !kern.auth.manager.WithAuth() && !lap.Addr().IsLoopback() {
ws.logger.Info().Str("listen", listenAddr).Msg("service may be reached from outside, but authentication is not enabled")
}
srvw := impl.New(ws.logger, listenAddr, baseURL, urlPrefix, persistentCookie, secureCookie, maxRequestSize, kern.auth.manager)
err := kern.web.setupServer(srvw, kern.box.manager, kern.auth.manager, &kern.cfg)
if err != nil {
ws.logger.Error().Err(err).Msg("Unable to create")
return err
}
if kern.core.GetNextConfig(kernel.CoreDebug).(bool) {
srvw.SetDebug()
}
if err = srvw.Run(); err != nil {
ws.logger.Error().Err(err).Msg("Unable to start")
return err
}
ws.logger.Info().Str("listen", listenAddr).Str("base-url", baseURL).Msg("Start Service")
ws.mxService.Lock()
ws.srvw = srvw
ws.mxService.Unlock()
|
| ︙ | ︙ |
Changes to kernel/kernel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 kernel provides the main kernel service. package kernel import ( "io" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package kernel provides the main kernel service. package kernel import ( "io" |
| ︙ | ︙ |
Changes to logger/logger.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 logger implements a logging package for use in the Zettelstore. package logger import ( "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package logger implements a logging package for use in the Zettelstore. package logger import ( "context" |
| ︙ | ︙ | |||
25 26 27 28 29 30 31 | type Level uint8 // Constants for Level const ( NoLevel Level = iota // the absent log level TraceLevel // Log most internal activities DebugLevel // Log most data updates | < < < < < < < < < < < < | 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 |
type Level uint8
// Constants for Level
const (
NoLevel Level = iota // the absent log level
TraceLevel // Log most internal activities
DebugLevel // Log most data updates
InfoLevel // Log normal activities
ErrorLevel // Log (persistent) errors
MandatoryLevel // Log only mandatory events
NeverLevel // Logging is disabled
)
var logLevel = [...]string{
" ",
"TRACE",
"DEBUG",
"INFO ",
"ERROR",
">>>>>",
"NEVER",
}
var strLevel = [...]string{
"",
"trace",
"debug",
"info",
"error",
"mandatory",
"disabled",
}
// IsValid returns true, if the level is a valid level
func (l Level) IsValid() bool { return TraceLevel <= l && l <= NeverLevel }
|
| ︙ | ︙ | |||
167 168 169 170 171 172 173 |
// Trace creates a tracing message.
func (l *Logger) Trace() *Message { return newMessage(l, TraceLevel) }
// Debug creates a debug message.
func (l *Logger) Debug() *Message { return newMessage(l, DebugLevel) }
| < < < < < < < < < < < < | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
// Trace creates a tracing message.
func (l *Logger) Trace() *Message { return newMessage(l, TraceLevel) }
// Debug creates a debug message.
func (l *Logger) Debug() *Message { return newMessage(l, DebugLevel) }
// Info creates a message suitable for information data.
func (l *Logger) Info() *Message { return newMessage(l, InfoLevel) }
// Error creates a message suitable for errors.
func (l *Logger) Error() *Message { return newMessage(l, ErrorLevel) }
// Mandatory creates a message that will always logged, except when logging
// is disabled.
func (l *Logger) Mandatory() *Message { return newMessage(l, MandatoryLevel) }
// Clone creates a message to clone the logger.
func (l *Logger) Clone() *Message {
msg := newMessage(l, NeverLevel)
|
| ︙ | ︙ |
Changes to logger/logger_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 logger_test import ( "fmt" "os" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package logger_test import ( "fmt" "os" |
| ︙ | ︙ | |||
23 24 25 26 27 28 29 |
testcases := []struct {
text string
exp logger.Level
}{
{"tra", logger.TraceLevel},
{"deb", logger.DebugLevel},
{"info", logger.InfoLevel},
| < < < | | | | | 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 |
testcases := []struct {
text string
exp logger.Level
}{
{"tra", logger.TraceLevel},
{"deb", logger.DebugLevel},
{"info", logger.InfoLevel},
{"err", logger.ErrorLevel},
{"manda", logger.MandatoryLevel},
{"dis", logger.NeverLevel},
{"d", logger.Level(0)},
}
for i, tc := range testcases {
got := logger.ParseLevel(tc.text)
if got != tc.exp {
t.Errorf("%d: ParseLevel(%q) == %q, but got %q", i, tc.text, tc.exp, got)
}
}
}
func BenchmarkDisabled(b *testing.B) {
log := logger.New(&stderrLogWriter{}, "").SetLevel(logger.NeverLevel)
for range b.N {
log.Info().Str("key", "val").Msg("Benchmark")
}
}
type stderrLogWriter struct{}
func (*stderrLogWriter) WriteMessage(level logger.Level, ts time.Time, prefix, msg string, details []byte) error {
fmt.Fprintf(os.Stderr, "%v %v %v %v %v\n", level.Format(), ts, prefix, msg, string(details))
return nil
}
type testLogWriter struct{}
func (*testLogWriter) WriteMessage(logger.Level, time.Time, string, string, []byte) error {
return nil
}
func BenchmarkStrMessage(b *testing.B) {
log := logger.New(&testLogWriter{}, "")
for range b.N {
log.Info().Str("key", "val").Msg("Benchmark")
}
}
func BenchmarkMessage(b *testing.B) {
log := logger.New(&testLogWriter{}, "")
for range b.N {
log.Info().Msg("Benchmark")
}
}
func BenchmarkCloneStrMessage(b *testing.B) {
log := logger.New(&testLogWriter{}, "").Clone().Str("sss", "ttt").Child()
for range b.N {
log.Info().Msg("123456789")
}
}
|
Changes to logger/message.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 logger import ( "context" "net/http" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package logger import ( "context" "net/http" |
| ︙ | ︙ |
Changes to parser/blob/blob.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 blob provides a parser of binary data. package blob import ( | > > > | | | 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-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
// Package blob provides a parser of binary data.
package blob
import (
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/ast"
"zettelstore.de/z/parser"
"zettelstore.de/z/zettel/meta"
)
func init() {
parser.Register(&parser.Info{
Name: meta.SyntaxGif,
|
| ︙ | ︙ |
Changes to parser/cleaner/cleaner.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 cleaner provides functions to clean up the parsed AST. package cleaner import ( "strconv" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package cleaner provides functions to clean up the parsed AST. package cleaner import ( "strconv" |
| ︙ | ︙ |
Changes to parser/draw/canvas.go.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import ( "bytes" "fmt" | > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package draw import ( "bytes" "fmt" |
| ︙ | ︙ | |||
93 94 95 96 97 98 99 |
c.findTexts()
sort.Sort(c.objs)
}
// findPaths by starting with a point that wasn't yet visited, beginning at the top
// left of the grid.
func (c *canvas) findPaths() {
| | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
c.findTexts()
sort.Sort(c.objs)
}
// findPaths by starting with a point that wasn't yet visited, beginning at the top
// left of the grid.
func (c *canvas) findPaths() {
for y := range c.siz.Y {
p := point{y: y}
for x := range c.siz.X {
p.x = x
if c.isVisited(p) {
continue
}
ch := c.at(p)
if !ch.isPathStart() {
continue
|
| ︙ | ︙ | |||
123 124 125 126 127 128 129 |
c.objs = append(c.objs, objs...)
}
}
}
// findTexts with a second pass through the grid attempts to identify any text within the grid.
func (c *canvas) findTexts() {
| | | | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
c.objs = append(c.objs, objs...)
}
}
}
// findTexts with a second pass through the grid attempts to identify any text within the grid.
func (c *canvas) findTexts() {
for y := range c.siz.Y {
p := point{}
p.y = y
for x := range c.siz.X {
p.x = x
if c.isVisited(p) {
continue
}
ch := c.at(p)
if !ch.isTextStart() {
continue
|
| ︙ | ︙ |
Changes to parser/draw/canvas_test.go.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import ( "reflect" "strings" | > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package draw import ( "reflect" "strings" |
| ︙ | ︙ | |||
654 655 656 657 658 659 660 | " | | | | |", " +-----+-------+---------+---+", "", "", } chunk := []byte(strings.Join(data, "\n")) input := make([]byte, 0, len(chunk)*b.N) | | | 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 |
" | | | | |",
" +-----+-------+---------+---+",
"",
"",
}
chunk := []byte(strings.Join(data, "\n"))
input := make([]byte, 0, len(chunk)*b.N)
for range b.N {
input = append(input, chunk...)
}
expected := 30 * b.N
b.ResetTimer()
c, err := newCanvas(input)
if err != nil {
b.Fatalf("Error creating canvas: %s", err)
|
| ︙ | ︙ |
Changes to parser/draw/char.go.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import "unicode" type char rune | > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package draw import "unicode" type char rune |
| ︙ | ︙ |
Changes to parser/draw/draw.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 draw provides a parser to create SVG from ASCII drawing. // // It is not a parser registered by the general parser framework (directed by // metadata "syntax" of a zettel). It will be used when a zettel is evaluated. package draw import ( "strconv" "zettelstore.de/client.fossil/attrs" | > > > | | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2022-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2022-present Detlef Stern
//-----------------------------------------------------------------------------
// Package draw provides a parser to create SVG from ASCII drawing.
//
// It is not a parser registered by the general parser framework (directed by
// metadata "syntax" of a zettel). It will be used when a zettel is evaluated.
package draw
import (
"strconv"
"zettelstore.de/client.fossil/attrs"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/ast"
"zettelstore.de/z/parser"
"zettelstore.de/z/zettel/meta"
)
func init() {
parser.Register(&parser.Info{
Name: meta.SyntaxDraw,
|
| ︙ | ︙ |
Changes to parser/draw/draw_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 draw_test import ( "testing" | > > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
//-----------------------------------------------------------------------------
// Copyright (c) 2022-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2022-present Detlef Stern
//-----------------------------------------------------------------------------
package draw_test
import (
"testing"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/config"
"zettelstore.de/z/parser"
"zettelstore.de/z/zettel/meta"
)
func FuzzParseBlocks(f *testing.F) {
f.Fuzz(func(t *testing.T, src []byte) {
t.Parallel()
|
| ︙ | ︙ |
Changes to parser/draw/object.go.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import "fmt" // object represents one of an open path, a closed path, or text. | > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package draw import "fmt" // object represents one of an open path, a closed path, or text. |
| ︙ | ︙ |
Changes to parser/draw/point.go.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import "fmt" // A renderHint suggests ways the SVG renderer may appropriately represent this point. | > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package draw import "fmt" // A renderHint suggests ways the SVG renderer may appropriately represent this point. |
| ︙ | ︙ |
Changes to parser/draw/svg.go.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import ( "bytes" "fmt" | > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package draw import ( "bytes" "fmt" |
| ︙ | ︙ |
Changes to parser/draw/svg_test.go.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. //----------------------------------------------------------------------------- package draw import ( "strings" "testing" | > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // license, but later changed to fulfil the needs of Zettelstore. The following // statements affects the original code as found on // https://github.com/asciitosvg/asciitosvg (Commit: // ca82a5ce41e2190a05e07af6e8b3ea4e3256a283, 2020-11-20): // // Copyright 2012 - 2018 The ASCIIToSVG Contributors // All rights reserved. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package draw import ( "strings" "testing" |
| ︙ | ︙ |
Changes to parser/markdown/markdown.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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 markdown provides a parser for markdown. package markdown import ( "bytes" "fmt" "strconv" "strings" gm "github.com/yuin/goldmark" gmAst "github.com/yuin/goldmark/ast" gmText "github.com/yuin/goldmark/text" "zettelstore.de/client.fossil/attrs" | > > > | | | | 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-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
// Package markdown provides a parser for markdown.
package markdown
import (
"bytes"
"fmt"
"strconv"
"strings"
gm "github.com/yuin/goldmark"
gmAst "github.com/yuin/goldmark/ast"
gmText "github.com/yuin/goldmark/text"
"zettelstore.de/client.fossil/attrs"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/ast"
"zettelstore.de/z/encoder/textenc"
"zettelstore.de/z/parser"
"zettelstore.de/z/zettel/meta"
)
func init() {
parser.Register(&parser.Info{
Name: meta.SyntaxMarkdown,
|
| ︙ | ︙ | |||
145 146 147 148 149 150 151 |
Content: p.acceptRawText(node),
}
}
func (p *mdP) acceptRawText(node gmAst.Node) []byte {
lines := node.Lines()
result := make([]byte, 0, 512)
| | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
Content: p.acceptRawText(node),
}
}
func (p *mdP) acceptRawText(node gmAst.Node) []byte {
lines := node.Lines()
result := make([]byte, 0, 512)
for i := range lines.Len() {
s := lines.At(i)
line := s.Value(p.source)
if l := len(line); l > 0 {
if l > 1 && line[l-2] == '\r' && line[l-1] == '\n' {
line = line[0 : l-2]
} else if line[l-1] == '\n' || line[l-1] == '\r' {
line = line[0 : l-1]
|
| ︙ | ︙ | |||
461 462 463 464 465 466 467 |
Attrs: nil, // TODO
},
}
}
func (p *mdP) acceptRawHTML(node *gmAst.RawHTML) ast.InlineSlice {
segs := make([][]byte, 0, node.Segments.Len())
| | | 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 |
Attrs: nil, // TODO
},
}
}
func (p *mdP) acceptRawHTML(node *gmAst.RawHTML) ast.InlineSlice {
segs := make([][]byte, 0, node.Segments.Len())
for i := range node.Segments.Len() {
segment := node.Segments.At(i)
segs = append(segs, segment.Value(p.source))
}
return ast.InlineSlice{
&ast.LiteralNode{
Kind: ast.LiteralHTML,
Attrs: nil, // TODO: add HTML as language
Content: bytes.Join(segs, nil),
},
}
}
|
Changes to parser/markdown/markdown_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 markdown import ( "strings" "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package markdown import ( "strings" "testing" |
| ︙ | ︙ |
Changes to parser/none/none.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( | > > > | | | 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-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
// Package none provides a none-parser, e.g. for zettel with just metadata.
package none
import (
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/ast"
"zettelstore.de/z/parser"
"zettelstore.de/z/zettel/meta"
)
func init() {
parser.Register(&parser.Info{
Name: meta.SyntaxNone,
|
| ︙ | ︙ |
Changes to parser/parser.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 parser provides a generic interface to a range of different parsers. package parser import ( "context" "fmt" "strings" "zettelstore.de/client.fossil/api" | > > > | | | | 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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package parser provides a generic interface to a range of different parsers. package parser import ( "context" "fmt" "strings" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/input" "zettelstore.de/z/ast" "zettelstore.de/z/config" "zettelstore.de/z/parser/cleaner" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/meta" ) // Info describes a single parser. // |
| ︙ | ︙ | |||
81 82 83 84 85 86 87 |
pi, ok := registry[syntax]
if !ok {
return false
}
return pi.IsASTParser
}
| < < < < < < < < < < < < | 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 |
pi, ok := registry[syntax]
if !ok {
return false
}
return pi.IsASTParser
}
// IsImageFormat returns whether the given syntax is known to be an image format.
func IsImageFormat(syntax string) bool {
pi, ok := registry[syntax]
if !ok {
return false
}
return pi.IsImageFormat
}
// ParseBlocks parses some input and returns a slice of block nodes.
func ParseBlocks(inp *input.Input, m *meta.Meta, syntax string, hi config.HTMLInsecurity) ast.BlockSlice {
bs := Get(syntax).ParseBlocks(inp, m, syntax)
cleaner.CleanBlockSlice(&bs, hi.AllowHTML(syntax))
return bs
}
// ParseInlines parses some input and returns a slice of inline nodes.
func ParseInlines(inp *input.Input, syntax string) ast.InlineSlice {
|
| ︙ | ︙ | |||
155 156 157 158 159 160 161 |
func ParseZettel(ctx context.Context, zettel zettel.Zettel, syntax string, rtConfig config.Config) *ast.ZettelNode {
m := zettel.Meta
inhMeta := m
if rtConfig != nil {
inhMeta = rtConfig.AddDefaultValues(ctx, inhMeta)
}
if syntax == "" {
| | | | 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 |
func ParseZettel(ctx context.Context, zettel zettel.Zettel, syntax string, rtConfig config.Config) *ast.ZettelNode {
m := zettel.Meta
inhMeta := m
if rtConfig != nil {
inhMeta = rtConfig.AddDefaultValues(ctx, inhMeta)
}
if syntax == "" {
syntax = inhMeta.GetDefault(api.KeySyntax, meta.DefaultSyntax)
}
parseMeta := inhMeta
if syntax == meta.SyntaxNone {
parseMeta = m
}
hi := config.NoHTML
if rtConfig != nil {
hi = rtConfig.GetHTMLInsecurity()
}
return &ast.ZettelNode{
Meta: m,
Content: zettel.Content,
Zid: m.Zid,
InhMeta: inhMeta,
Ast: ParseBlocks(input.NewInput(zettel.Content.AsBytes()), parseMeta, syntax, hi),
Syntax: syntax,
}
}
|
Changes to parser/parser_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 parser_test import ( "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package parser_test import ( "testing" |
| ︙ | ︙ | |||
26 27 28 29 30 31 32 |
)
func TestParserType(t *testing.T) {
syntaxSet := strfun.NewSet(parser.GetSyntaxes()...)
testCases := []struct {
syntax string
ast bool
| < | | | | | | | | | | | | | | | | | < < < | 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 |
)
func TestParserType(t *testing.T) {
syntaxSet := strfun.NewSet(parser.GetSyntaxes()...)
testCases := []struct {
syntax string
ast bool
image bool
}{
{meta.SyntaxHTML, false, false},
{meta.SyntaxCSS, false, false},
{meta.SyntaxDraw, true, false},
{meta.SyntaxGif, false, true},
{meta.SyntaxJPEG, false, true},
{meta.SyntaxJPG, false, true},
{meta.SyntaxMarkdown, true, false},
{meta.SyntaxMD, true, false},
{meta.SyntaxNone, false, false},
{meta.SyntaxPlain, false, false},
{meta.SyntaxPNG, false, true},
{meta.SyntaxSVG, false, true},
{meta.SyntaxSxn, false, false},
{meta.SyntaxText, false, false},
{meta.SyntaxTxt, false, false},
{meta.SyntaxWebp, false, true},
{meta.SyntaxZmk, true, false},
}
for _, tc := range testCases {
delete(syntaxSet, tc.syntax)
if got := parser.IsASTParser(tc.syntax); got != tc.ast {
t.Errorf("Syntax %q is AST: %v, but got %v", tc.syntax, tc.ast, got)
}
if got := parser.IsImageFormat(tc.syntax); got != tc.image {
t.Errorf("Syntax %q is image: %v, but got %v", tc.syntax, tc.image, got)
}
}
for syntax := range syntaxSet {
t.Errorf("Forgot to test syntax %q", syntax)
}
}
|
Changes to parser/plain/plain.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 plain provides a parser for plain text data. package plain import ( "bytes" "strings" "zettelstore.de/client.fossil/attrs" | > > > | < | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
// Package plain provides a parser for plain text data.
package plain
import (
"bytes"
"strings"
"zettelstore.de/client.fossil/attrs"
"zettelstore.de/client.fossil/input"
"zettelstore.de/sx.fossil/sxreader"
"zettelstore.de/z/ast"
"zettelstore.de/z/parser"
"zettelstore.de/z/zettel/meta"
)
func init() {
parser.Register(&parser.Info{
Name: meta.SyntaxTxt,
|
| ︙ | ︙ | |||
132 133 134 135 136 137 138 |
}
// TODO: check proper end </svg>
return svgSrc
}
func parseSxnBlocks(inp *input.Input, _ *meta.Meta, syntax string) ast.BlockSlice {
rd := sxreader.MakeReader(bytes.NewReader(inp.Src))
| | < | | | | | | > > | | | < < < < < < < < < < < | 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 |
}
// TODO: check proper end </svg>
return svgSrc
}
func parseSxnBlocks(inp *input.Input, _ *meta.Meta, syntax string) ast.BlockSlice {
rd := sxreader.MakeReader(bytes.NewReader(inp.Src))
_, err := rd.ReadAll()
result := ast.BlockSlice{
&ast.VerbatimNode{
Kind: ast.VerbatimProg,
Attrs: attrs.Attributes{"": syntax},
Content: inp.ScanLineContent(),
},
}
if err != nil {
result = append(result, ast.CreateParaNode(&ast.TextNode{
Text: err.Error(),
}))
}
return result
}
func parseSxnInlines(inp *input.Input, syntax string) ast.InlineSlice {
inp.SkipToEOL()
return ast.InlineSlice{&ast.LiteralNode{
Kind: ast.LiteralProg,
Attrs: attrs.Attributes{"": syntax},
Content: append([]byte(nil), inp.Src[0:inp.Pos]...),
}}
}
|
Changes to parser/zettelmark/block.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 zettelmark import ( "fmt" | > > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package zettelmark
import (
"fmt"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/ast"
)
// parseBlockSlice parses a sequence of blocks.
func (cp *zmkP) parseBlockSlice() ast.BlockSlice {
inp := cp.inp
var lastPara *ast.ParaNode
bs := ast.BlockSlice{}
|
| ︙ | ︙ | |||
404 405 406 407 408 409 410 |
}
}
return ln, newLnCount
}
func (cp *zmkP) cleanupParsedNestedList(newLnCount int) (res ast.BlockNode, success bool) {
listDepth := len(cp.lists)
| | | 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
}
}
return ln, newLnCount
}
func (cp *zmkP) cleanupParsedNestedList(newLnCount int) (res ast.BlockNode, success bool) {
listDepth := len(cp.lists)
for i := range newLnCount {
childPos := listDepth - i - 1
parentPos := childPos - 1
if parentPos < 0 {
return cp.lists[0], true
}
if prevItems := cp.lists[parentPos].Items; len(prevItems) > 0 {
lastItem := len(prevItems) - 1
|
| ︙ | ︙ |
Changes to parser/zettelmark/inline.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 zettelmark import ( "bytes" "fmt" "strings" "zettelstore.de/client.fossil/attrs" | > > > | | | 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) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package zettelmark
import (
"bytes"
"fmt"
"strings"
"zettelstore.de/client.fossil/attrs"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/ast"
"zettelstore.de/z/zettel/meta"
)
// parseInlineSlice parses a sequence of Inlines until EOS.
func (cp *zmkP) parseInlineSlice() (ins ast.InlineSlice) {
inp := cp.inp
for inp.Ch != input.EOS {
|
| ︙ | ︙ |
Changes to parser/zettelmark/node.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 zettelmark import "zettelstore.de/z/ast" // Internal nodes for parsing zettelmark. These will be removed in | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package zettelmark import "zettelstore.de/z/ast" // Internal nodes for parsing zettelmark. These will be removed in |
| ︙ | ︙ |
Changes to parser/zettelmark/post-processor.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 zettelmark import ( "strings" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package zettelmark import ( "strings" |
| ︙ | ︙ | |||
127 128 129 130 131 132 133 |
}
}
}
func (pp *postProcessor) visitTable(tn *ast.TableNode) {
width := tableWidth(tn)
tn.Align = make([]ast.Alignment, width)
| | | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
}
}
}
func (pp *postProcessor) visitTable(tn *ast.TableNode) {
width := tableWidth(tn)
tn.Align = make([]ast.Alignment, width)
for i := range width {
tn.Align[i] = ast.AlignDefault
}
if len(tn.Rows) > 0 && isHeaderRow(tn.Rows[0]) {
tn.Header = tn.Rows[0]
tn.Rows = tn.Rows[1:]
pp.visitTableHeader(tn)
}
|
| ︙ | ︙ |
Changes to parser/zettelmark/zettelmark.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 zettelmark provides a parser for zettelmarkup. package zettelmark import ( "strings" "unicode" "zettelstore.de/client.fossil/attrs" | > > > | | | 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) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
// Package zettelmark provides a parser for zettelmarkup.
package zettelmark
import (
"strings"
"unicode"
"zettelstore.de/client.fossil/attrs"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/ast"
"zettelstore.de/z/parser"
"zettelstore.de/z/zettel/meta"
)
func init() {
parser.Register(&parser.Info{
Name: meta.SyntaxZmk,
|
| ︙ | ︙ |
Changes to parser/zettelmark/zettelmark_fuzz_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 zettelmark_test import ( "testing" | > > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
//-----------------------------------------------------------------------------
// Copyright (c) 2022-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2022-present Detlef Stern
//-----------------------------------------------------------------------------
package zettelmark_test
import (
"testing"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/config"
"zettelstore.de/z/parser"
"zettelstore.de/z/zettel/meta"
)
func FuzzParseBlocks(f *testing.F) {
f.Fuzz(func(t *testing.T, src []byte) {
t.Parallel()
|
| ︙ | ︙ |
Changes to parser/zettelmark/zettelmark_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 zettelmark_test provides some tests for the zettelmarkup parser. package zettelmark_test import ( "fmt" "strings" "testing" "zettelstore.de/client.fossil/attrs" | > > > | | | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
// Package zettelmark_test provides some tests for the zettelmarkup parser.
package zettelmark_test
import (
"fmt"
"strings"
"testing"
"zettelstore.de/client.fossil/attrs"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/ast"
"zettelstore.de/z/config"
"zettelstore.de/z/parser"
"zettelstore.de/z/zettel/meta"
)
type TestCase struct{ source, want string }
type TestCases []TestCase
|
| ︙ | ︙ |
Changes to query/compiled.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 query import ( | > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2023-present Detlef Stern //----------------------------------------------------------------------------- package query import ( "math/rand/v2" "sort" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // Compiled is a compiled query, to be used in a Box |
| ︙ | ︙ | |||
135 136 137 138 139 140 141 |
}
if limit := c.limit; limit > 0 && limit < count {
count = limit
c.limit = 0
}
order := make([]int, len(metaList))
| | | | | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
}
if limit := c.limit; limit > 0 && limit < count {
count = limit
c.limit = 0
}
order := make([]int, len(metaList))
for i := range len(metaList) {
order[i] = i
}
rnd := c.newRandom()
picked := make([]int, count)
for i := range count {
last := len(order) - i
n := rnd.IntN(last)
picked[i] = order[n]
order[n] = order[last-1]
}
order = nil
sort.Ints(picked)
result := make([]*meta.Meta, count)
for i, p := range picked {
|
| ︙ | ︙ | |||
167 168 169 170 171 172 173 |
)
return metaList
}
func (c *Compiled) newRandom() *rand.Rand {
seed := c.seed
if seed <= 0 {
| | | | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
)
return metaList
}
func (c *Compiled) newRandom() *rand.Rand {
seed := c.seed
if seed <= 0 {
seed = rand.IntN(10000) + 10001
}
return rand.New(rand.NewPCG(uint64(seed), uint64(seed)))
}
func limitElements(metaList []*meta.Meta, limit int) []*meta.Meta {
if limit > 0 && limit < len(metaList) {
return metaList[:limit]
}
return metaList
}
func sortMetaByZid(metaList []*meta.Meta) []*meta.Meta {
sort.Slice(metaList, func(i, j int) bool { return metaList[i].Zid > metaList[j].Zid })
return metaList
}
|
Changes to query/context.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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2023-present 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 query
import (
"container/heap"
"context"
"zettelstore.de/client.fossil/api"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
// ContextSpec contains all specification values for calculating a context.
type ContextSpec struct {
Direction ContextDirection
MaxCost int
MaxCount int
}
// ContextDirection specifies the direction a context should be calculated.
type ContextDirection uint8
const (
ContextDirBoth ContextDirection = iota
ContextDirForward
ContextDirBackward
)
// ContextPort is the collection of box methods needed by this directive.
type ContextPort interface {
GetMeta(ctx context.Context, zid id.Zid) (*meta.Meta, error)
SelectMeta(ctx context.Context, metaSeq []*meta.Meta, q *Query) ([]*meta.Meta, error)
}
func (spec *ContextSpec) Print(pe *PrintEnv) {
pe.printSpace()
pe.writeString(api.ContextDirective)
switch spec.Direction {
case ContextDirBackward:
pe.printSpace()
pe.writeString(api.BackwardDirective)
case ContextDirForward:
pe.printSpace()
pe.writeString(api.ForwardDirective)
}
pe.printPosInt(api.CostDirective, spec.MaxCost)
pe.printPosInt(api.MaxDirective, spec.MaxCount)
}
func (spec *ContextSpec) Execute(ctx context.Context, startSeq []*meta.Meta, port ContextPort) []*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 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2023-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------
package query
import (
"container/heap"
"context"
"math"
"zettelstore.de/client.fossil/api"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
// ContextSpec contains all specification values for calculating a context.
type ContextSpec struct {
Direction ContextDirection
MaxCost int
MaxCount int
Full bool
}
// ContextDirection specifies the direction a context should be calculated.
type ContextDirection uint8
const (
ContextDirBoth ContextDirection = iota
ContextDirForward
ContextDirBackward
)
// ContextPort is the collection of box methods needed by this directive.
type ContextPort interface {
GetMeta(ctx context.Context, zid id.Zid) (*meta.Meta, error)
SelectMeta(ctx context.Context, metaSeq []*meta.Meta, q *Query) ([]*meta.Meta, error)
}
func (spec *ContextSpec) Print(pe *PrintEnv) {
pe.printSpace()
pe.writeString(api.ContextDirective)
if spec.Full {
pe.printSpace()
pe.writeString(api.FullDirective)
}
switch spec.Direction {
case ContextDirBackward:
pe.printSpace()
pe.writeString(api.BackwardDirective)
case ContextDirForward:
pe.printSpace()
pe.writeString(api.ForwardDirective)
}
pe.printPosInt(api.CostDirective, spec.MaxCost)
pe.printPosInt(api.MaxDirective, spec.MaxCount)
}
func (spec *ContextSpec) Execute(ctx context.Context, startSeq []*meta.Meta, port ContextPort) []*meta.Meta {
maxCost := float64(spec.MaxCost)
if maxCost <= 0 {
maxCost = 17
}
maxCount := spec.MaxCount
if maxCount <= 0 {
maxCount = 200
}
tasks := newQueue(startSeq, maxCost, maxCount, port)
isBackward := spec.Direction == ContextDirBoth || spec.Direction == ContextDirBackward
isForward := spec.Direction == ContextDirBoth || spec.Direction == ContextDirForward
result := []*meta.Meta{}
for {
m, cost := tasks.next()
if m == nil {
break
}
result = append(result, m)
for _, p := range m.ComputedPairsRest() {
tasks.addPair(ctx, p.Key, p.Value, cost, isBackward, isForward)
}
if !spec.Full {
continue
}
if tags, found := m.GetList(api.KeyTags); found {
tasks.addTags(ctx, tags, cost)
}
}
return result
}
type ztlCtxItem struct {
cost float64
meta *meta.Meta
}
type ztlCtxQueue []ztlCtxItem
func (q ztlCtxQueue) Len() int { return len(q) }
func (q ztlCtxQueue) Less(i, j int) bool { return q[i].cost < q[j].cost }
func (q ztlCtxQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] }
func (q *ztlCtxQueue) Push(x any) { *q = append(*q, x.(ztlCtxItem)) }
func (q *ztlCtxQueue) Pop() any {
old := *q
n := len(old)
item := old[n-1]
old[n-1].meta = nil // avoid memory leak
*q = old[0 : n-1]
return item
}
type contextTask struct {
port ContextPort
seen id.Set
queue ztlCtxQueue
maxCost float64
limit int
tagMetas map[string][]*meta.Meta
tagZids map[string]id.Set // just the zids of tagMetas
metaZid map[id.Zid]*meta.Meta // maps zid to meta for all meta retrieved with tags
}
func newQueue(startSeq []*meta.Meta, maxCost float64, limit int, port ContextPort) *contextTask {
result := &contextTask{
port: port,
seen: id.NewSet(),
maxCost: maxCost,
limit: limit,
tagMetas: make(map[string][]*meta.Meta),
tagZids: make(map[string]id.Set),
metaZid: make(map[id.Zid]*meta.Meta),
}
queue := make(ztlCtxQueue, 0, len(startSeq))
for _, m := range startSeq {
queue = append(queue, ztlCtxItem{cost: 1, meta: m})
}
heap.Init(&queue)
result.queue = queue
return result
}
func (ct *contextTask) addPair(ctx context.Context, key, value string, curCost float64, isBackward, isForward bool) {
if key == api.KeyBack {
return
}
newCost := curCost + contextCost(key)
if key == api.KeyBackward {
if isBackward {
ct.addIDSet(ctx, newCost, value)
|
| ︙ | ︙ | |||
158 159 160 161 162 163 164 |
if t := meta.Type(key); t == meta.TypeID {
ct.addID(ctx, newCost, value)
} else if t == meta.TypeIDSet {
ct.addIDSet(ctx, newCost, value)
}
}
| | > > | < | | | < < < < < < | < < | | > > | | > > | | < | | > | > > > > > > > | | > > > | | | < > | | > | | > > > > > | < < < | | | | | | > | < | | > | | < < > > | | | | < | < | | 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 |
if t := meta.Type(key); t == meta.TypeID {
ct.addID(ctx, newCost, value)
} else if t == meta.TypeIDSet {
ct.addIDSet(ctx, newCost, value)
}
}
func contextCost(key string) float64 {
switch key {
case api.KeyFolge, api.KeyPrecursor:
return 1
case api.KeySubordinates, api.KeySuperior:
return 1.5
case api.KeySuccessors, api.KeyPredecessor:
return 7
}
return 2
}
func (ct *contextTask) addID(ctx context.Context, newCost float64, value string) {
if zid, errParse := id.Parse(value); errParse == nil {
if m, errGetMeta := ct.port.GetMeta(ctx, zid); errGetMeta == nil {
ct.addMeta(m, newCost)
}
}
}
func (ct *contextTask) addMeta(m *meta.Meta, newCost float64) {
// If len(zc.seen) <= 1, the initial zettel is processed. In this case allow all
// other zettel that are directly reachable, without taking the cost into account.
// Of course, the limit ist still relevant.
if !ct.hasLimit() && (len(ct.seen) <= 1 || ct.maxCost == 0 || newCost <= ct.maxCost) {
if _, found := ct.seen[m.Zid]; !found {
heap.Push(&ct.queue, ztlCtxItem{cost: newCost, meta: m})
}
}
}
func (ct *contextTask) addIDSet(ctx context.Context, newCost float64, value string) {
elems := meta.ListFromValue(value)
refCost := referenceCost(newCost, len(elems))
for _, val := range elems {
ct.addID(ctx, refCost, val)
}
}
func referenceCost(baseCost float64, numReferences int) float64 {
nRefs := float64(numReferences)
return nRefs*math.Log2(nRefs+1) + baseCost
}
func (ct *contextTask) addTags(ctx context.Context, tags []string, baseCost float64) {
var zidSet id.Set
for _, tag := range tags {
zs := ct.updateTagData(ctx, tag)
zidSet = zidSet.Copy(zs)
}
for _, zid := range zidSet.Sorted() { // .Sorted() to stay deterministic
minCost := math.MaxFloat64
costFactor := 1.1
for _, tag := range tags {
tagZids := ct.tagZids[tag]
if tagZids.Contains(zid) {
cost := tagCost(baseCost, len(tagZids))
if cost < minCost {
minCost = cost
}
costFactor /= 1.1
}
}
ct.addMeta(ct.metaZid[zid], minCost*costFactor)
}
}
func (ct *contextTask) updateTagData(ctx context.Context, tag string) id.Set {
if _, found := ct.tagMetas[tag]; found {
return ct.tagZids[tag]
}
q := Parse(api.KeyTags + api.SearchOperatorHas + tag + " ORDER REVERSE " + api.KeyID)
ml, err := ct.port.SelectMeta(ctx, nil, q)
if err != nil {
ml = nil
}
ct.tagMetas[tag] = ml
zids := id.NewSetCap(len(ml))
for _, m := range ml {
zid := m.Zid
zids = zids.Add(zid)
if _, found := ct.metaZid[zid]; !found {
ct.metaZid[zid] = m
}
}
ct.tagZids[tag] = zids
return zids
}
func tagCost(baseCost float64, numTags int) float64 {
nTags := float64(numTags)
return nTags*math.Log2(nTags+1) + baseCost
}
func (ct *contextTask) next() (*meta.Meta, float64) {
if ct.hasLimit() {
return nil, -1
}
for len(ct.queue) > 0 {
item := heap.Pop(&ct.queue).(ztlCtxItem)
m := item.meta
zid := m.Zid
|
| ︙ | ︙ |
Changes to query/parser.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 query import ( "strconv" "zettelstore.de/client.fossil/api" | > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
//-----------------------------------------------------------------------------
// Copyright (c) 2022-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2022-present Detlef Stern
//-----------------------------------------------------------------------------
package query
import (
"strconv"
"zettelstore.de/client.fossil/api"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
// Parse the query specification and return a Query object.
func Parse(spec string) (q *Query) { return q.Parse(spec) }
|
| ︙ | ︙ | |||
201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
spec := &ContextSpec{}
for {
ps.skipSpace()
if ps.mustStop() {
break
}
pos := inp.Pos
if ps.acceptSingleKw(api.BackwardDirective) {
spec.Direction = ContextDirBackward
continue
}
inp.SetPos(pos)
if ps.acceptSingleKw(api.ForwardDirective) {
spec.Direction = ContextDirForward
| > > > > > | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
spec := &ContextSpec{}
for {
ps.skipSpace()
if ps.mustStop() {
break
}
pos := inp.Pos
if ps.acceptSingleKw(api.FullDirective) {
spec.Full = true
continue
}
inp.SetPos(pos)
if ps.acceptSingleKw(api.BackwardDirective) {
spec.Direction = ContextDirBackward
continue
}
inp.SetPos(pos)
if ps.acceptSingleKw(api.ForwardDirective) {
spec.Direction = ContextDirForward
|
| ︙ | ︙ |
Changes to query/parser_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 query_test import ( "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package query_test import ( "testing" |
| ︙ | ︙ | |||
31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
{"1 ITEMS", "00000000000001 ITEMS"},
{"ITEMS", "ITEMS"},
{"CONTEXT", "CONTEXT"}, {"CONTEXT a", "CONTEXT a"},
{"0 CONTEXT", "0 CONTEXT"}, {"1 CONTEXT", "00000000000001 CONTEXT"},
{"00000000000001 CONTEXT", "00000000000001 CONTEXT"},
{"100000000000001 CONTEXT", "100000000000001 CONTEXT"},
{"1 CONTEXT BACKWARD", "00000000000001 CONTEXT BACKWARD"},
{"1 CONTEXT FORWARD", "00000000000001 CONTEXT FORWARD"},
{"1 CONTEXT COST ", "00000000000001 CONTEXT COST"},
{"1 CONTEXT COST 3", "00000000000001 CONTEXT COST 3"}, {"1 CONTEXT COST x", "00000000000001 CONTEXT COST x"},
{"1 CONTEXT MAX 5", "00000000000001 CONTEXT MAX 5"}, {"1 CONTEXT MAX y", "00000000000001 CONTEXT MAX y"},
{"1 CONTEXT MAX 5 COST 7", "00000000000001 CONTEXT COST 7 MAX 5"},
{"1 CONTEXT | N", "00000000000001 CONTEXT | N"},
| > | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
{"1 ITEMS", "00000000000001 ITEMS"},
{"ITEMS", "ITEMS"},
{"CONTEXT", "CONTEXT"}, {"CONTEXT a", "CONTEXT a"},
{"0 CONTEXT", "0 CONTEXT"}, {"1 CONTEXT", "00000000000001 CONTEXT"},
{"00000000000001 CONTEXT", "00000000000001 CONTEXT"},
{"100000000000001 CONTEXT", "100000000000001 CONTEXT"},
{"1 CONTEXT FULL", "00000000000001 CONTEXT FULL"},
{"1 CONTEXT BACKWARD", "00000000000001 CONTEXT BACKWARD"},
{"1 CONTEXT FORWARD", "00000000000001 CONTEXT FORWARD"},
{"1 CONTEXT COST ", "00000000000001 CONTEXT COST"},
{"1 CONTEXT COST 3", "00000000000001 CONTEXT COST 3"}, {"1 CONTEXT COST x", "00000000000001 CONTEXT COST x"},
{"1 CONTEXT MAX 5", "00000000000001 CONTEXT MAX 5"}, {"1 CONTEXT MAX y", "00000000000001 CONTEXT MAX y"},
{"1 CONTEXT MAX 5 COST 7", "00000000000001 CONTEXT COST 7 MAX 5"},
{"1 CONTEXT | N", "00000000000001 CONTEXT | N"},
|
| ︙ | ︙ |
Changes to query/print.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 query import ( "io" "strconv" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package query import ( "io" "strconv" |
| ︙ | ︙ |
Changes to query/query.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 query provides a query for zettel. package query import ( "context" | > > > | | 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-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package query provides a query for zettel. package query import ( "context" "math/rand/v2" "slices" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) // Searcher is used to select zettel identifier based on search criteria. |
| ︙ | ︙ | |||
243 244 245 246 247 248 249 |
func (q *Query) addKey(key string, op compareOp) *Query {
q = createIfNeeded(q)
q.terms[len(q.terms)-1].addKey(key, op)
return q
}
| > > > > > > > > > > | > | > | 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 |
func (q *Query) addKey(key string, op compareOp) *Query {
q = createIfNeeded(q)
q.terms[len(q.terms)-1].addKey(key, op)
return q
}
var missingMap = map[compareOp]bool{
cmpNotExist: true,
cmpNotEqual: true,
cmpHasNot: true,
cmpNoMatch: true,
}
// GetMetaValues returns the slice of all values specified for a given metadata key.
// If `withMissing` is true, all values are returned. Otherwise only those,
// where the comparison operator will positively search for a value.
func (q *Query) GetMetaValues(key string, withMissing bool) (vals []string) {
if q == nil {
return nil
}
for _, term := range q.terms {
if mvs, hasMv := term.mvals[key]; hasMv {
for _, ev := range mvs {
if withMissing || !missingMap[ev.op] {
vals = append(vals, ev.value)
}
}
}
}
slices.Sort(vals)
return slices.Compact(vals)
}
|
| ︙ | ︙ | |||
287 288 289 290 291 292 293 |
return q.seed, q.seed > 0
}
// SetDeterministic signals that the result should be the same if the seed is the same.
func (q *Query) SetDeterministic() *Query {
q = createIfNeeded(q)
if q.seed <= 0 {
| | | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
return q.seed, q.seed > 0
}
// SetDeterministic signals that the result should be the same if the seed is the same.
func (q *Query) SetDeterministic() *Query {
q = createIfNeeded(q)
if q.seed <= 0 {
q.seed = int(rand.IntN(10000) + 1)
}
return q
}
// Actions returns the slice of action specifications
func (q *Query) Actions() []string {
if q == nil {
|
| ︙ | ︙ |
Changes to query/retrieve.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 query // This file contains helper functions to search within the index. import ( | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package query // This file contains helper functions to search within the index. import ( |
| ︙ | ︙ |
Changes to query/select.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 query import ( "fmt" "strconv" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package query import ( "fmt" "strconv" |
| ︙ | ︙ | |||
117 118 119 120 121 122 123 | return createMatchTimestampFunc(values, addSearch) case meta.TypeNumber: return createMatchNumberFunc(values, addSearch) case meta.TypeTagSet: return createMatchTagSetFunc(values, addSearch) case meta.TypeWord: return createMatchWordFunc(values, addSearch) | < < | | 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 |
return createMatchTimestampFunc(values, addSearch)
case meta.TypeNumber:
return createMatchNumberFunc(values, addSearch)
case meta.TypeTagSet:
return createMatchTagSetFunc(values, addSearch)
case meta.TypeWord:
return createMatchWordFunc(values, addSearch)
case meta.TypeZettelmarkup:
return createMatchZmkFunc(values, addSearch)
}
return createMatchStringFunc(values, addSearch)
}
func createMatchIDFunc(values []expValue, addSearch addSearchFunc) matchValueFunc {
preds := valuesToIDPredicates(values, addSearch)
return func(value string) bool {
for _, pred := range preds {
if !pred(value) {
return false
}
}
return true
}
}
func createMatchIDSetFunc(values []expValue, addSearch addSearchFunc) matchValueFunc {
predList := valuesToSetPredicates(preprocessSet(values), addSearch)
return func(value string) bool {
ids := meta.ListFromValue(value)
for _, preds := range predList {
for _, pred := range preds {
if !pred(ids) {
return false
}
|
| ︙ | ︙ | |||
177 178 179 180 181 182 183 |
}
}
return true
}
}
func createMatchTagSetFunc(values []expValue, addSearch addSearchFunc) matchValueFunc {
| | | 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
}
}
return true
}
}
func createMatchTagSetFunc(values []expValue, addSearch addSearchFunc) matchValueFunc {
predList := valuesToSetPredicates(processTagSet(preprocessSet(sliceToLower(values))), addSearch)
return func(value string) bool {
tags := meta.TagsFromValue(value)
for _, preds := range predList {
for _, pred := range preds {
if !pred(tags) {
return false
}
|
| ︙ | ︙ | |||
230 231 232 233 234 235 236 |
return func(value string) bool {
value = strings.ToLower(value)
for _, pred := range preds {
if !pred(value) {
return false
}
}
| < < < < < < < < < < < < < < < | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
return func(value string) bool {
value = strings.ToLower(value)
for _, pred := range preds {
if !pred(value) {
return false
}
}
return true
}
}
func sliceToLower(sl []expValue) []expValue {
result := make([]expValue, 0, len(sl))
for _, s := range sl {
|
| ︙ | ︙ | |||
307 308 309 310 311 312 313 |
}
}
return true
}
func zmk2text(zmk string) string {
isASCII, hasUpper, needParse := true, false, false
| | | 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
}
}
return true
}
func zmk2text(zmk string) string {
isASCII, hasUpper, needParse := true, false, false
for i := range len(zmk) {
ch := zmk[i]
if ch >= utf8.RuneSelf {
isASCII = false
break
}
hasUpper = hasUpper || ('A' <= ch && ch <= 'Z')
needParse = needParse || !(('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || ('0' <= ch && ch <= '9') || ch == ' ')
|
| ︙ | ︙ | |||
382 383 384 385 386 387 388 |
result[i] = createWordCompareFunc(value, op)
}
}
return result
}
func isDigits(s string) bool {
| | | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
result[i] = createWordCompareFunc(value, op)
}
}
return result
}
func isDigits(s string) bool {
for i := range len(s) {
if ch := s[i]; ch < '0' || '9' < ch {
return false
}
}
return true
}
|
| ︙ | ︙ | |||
562 563 564 565 566 567 568 |
default:
panic(fmt.Sprintf("Unknown compare operation %d with value %q", cmpOp, cmpVal))
}
}
type stringSetPredicate func(value []string) bool
| | | 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 |
default:
panic(fmt.Sprintf("Unknown compare operation %d with value %q", cmpOp, cmpVal))
}
}
type stringSetPredicate func(value []string) bool
func valuesToSetPredicates(values [][]expValue, addSearch addSearchFunc) [][]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 := disambiguateWordOp(v.op); op {
case cmpEqual:
|
| ︙ | ︙ |
Changes to query/select_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 query_test import ( "context" "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package query_test import ( "context" "testing" |
| ︙ | ︙ |
Changes to query/sorter.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 query import ( "strconv" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package query import ( "strconv" |
| ︙ | ︙ |
Changes to query/specs.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 query import "zettelstore.de/client.fossil/api" // IdentSpec contains all specification values to calculate the ident directive. | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2023-present Detlef Stern //----------------------------------------------------------------------------- package query import "zettelstore.de/client.fossil/api" // IdentSpec contains all specification values to calculate the ident directive. |
| ︙ | ︙ |
Changes to query/unlinked.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 query import ( "zettelstore.de/client.fossil/api" "zettelstore.de/z/strfun" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2023-present Detlef Stern //----------------------------------------------------------------------------- package query import ( "zettelstore.de/client.fossil/api" "zettelstore.de/z/strfun" |
| ︙ | ︙ |
Changes to strfun/escape.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 strfun import "io" var ( | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package strfun import "io" var ( |
| ︙ | ︙ |
Changes to strfun/set.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//-----------------------------------------------------------------------------
// Copyright (c) 2022-present 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 strfun
// Set ist a set of strings.
type Set map[string]struct{}
| > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//-----------------------------------------------------------------------------
// Copyright (c) 2022-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2022-present Detlef Stern
//-----------------------------------------------------------------------------
package strfun
// Set ist a set of strings.
type Set map[string]struct{}
|
| ︙ | ︙ |
Changes to strfun/slugify.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 strfun import ( "strings" "unicode" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package strfun import ( "strings" "unicode" |
| ︙ | ︙ |
Changes to strfun/slugify_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 strfun_test import ( "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package strfun_test import ( "testing" |
| ︙ | ︙ |
Changes to strfun/strfun.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 strfun provides some string functions. package strfun import ( "strings" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package strfun provides some string functions. package strfun import ( "strings" |
| ︙ | ︙ | |||
36 37 38 39 40 41 42 |
runes[maxLen-1] = '\u2025'
}
var sb strings.Builder
for _, r := range runes {
sb.WriteRune(r)
}
| | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
runes[maxLen-1] = '\u2025'
}
var sb strings.Builder
for _, r := range runes {
sb.WriteRune(r)
}
for range maxLen - len(runes) {
sb.WriteRune(pad)
}
return sb.String()
}
// SplitLines splits the given string into a list of lines.
func SplitLines(s string) []string {
|
| ︙ | ︙ |
Changes to strfun/strfun_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 strfun_test import ( "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package strfun_test import ( "testing" |
| ︙ | ︙ |
Changes to tests/client/client_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 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present Detlef Stern // // This file is part of Zettelstore. // // Zettelstore client 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 client provides a client for accessing the Zettelstore via its API. package client_test import ( "context" "flag" "fmt" "net/http" "net/url" "strconv" "testing" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/client" "zettelstore.de/z/kernel" ) | > > > > > | 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 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present Detlef Stern // // This file is part of Zettelstore. // // Zettelstore client is licensed under the latest version of the EUPL // (European Union Public License). Please see file LICENSE.txt for your rights // and obligations under this license. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package client provides a client for accessing the Zettelstore via its API. package client_test import ( "context" "flag" "fmt" "io" "net/http" "net/url" "slices" "strconv" "testing" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/client" "zettelstore.de/z/kernel" ) |
| ︙ | ︙ | |||
414 415 416 417 418 419 420 421 422 423 424 425 426 427 |
exp := api.ZettelID("00000000060010")
if err != nil {
t.Error(err)
} else if zid != exp {
t.Errorf("role zettel for zettel should be %q, but got %q", exp, zid)
}
}
func TestVersion(t *testing.T) {
t.Parallel()
c := getClient()
ver, err := c.GetVersionInfo(context.Background())
if err != nil {
t.Error(err)
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
exp := api.ZettelID("00000000060010")
if err != nil {
t.Error(err)
} else if zid != exp {
t.Errorf("role zettel for zettel should be %q, but got %q", exp, zid)
}
}
func TestRedirect(t *testing.T) {
t.Parallel()
c := getClient()
search := api.OrderDirective + " " + api.ReverseDirective + " " + api.KeyID + api.ActionSeparator + api.RedirectAction
ub := c.NewURLBuilder('z').AppendQuery(search)
respRedirect, err := http.Get(ub.String())
if err != nil {
t.Error(err)
return
}
defer respRedirect.Body.Close()
bodyRedirect, err := io.ReadAll(respRedirect.Body)
if err != nil {
t.Error(err)
return
}
ub.ClearQuery().SetZid(api.ZidEmoji)
respEmoji, err := http.Get(ub.String())
if err != nil {
t.Error(err)
return
}
defer respEmoji.Body.Close()
bodyEmoji, err := io.ReadAll(respEmoji.Body)
if err != nil {
t.Error(err)
return
}
if !slices.Equal(bodyRedirect, bodyEmoji) {
t.Error("Wrong redirect")
t.Error("REDIRECT", respRedirect)
t.Error("EXPECTED", respEmoji)
}
}
func TestVersion(t *testing.T) {
t.Parallel()
c := getClient()
ver, err := c.GetVersionInfo(context.Background())
if err != nil {
t.Error(err)
|
| ︙ | ︙ |
Changes to tests/client/crud_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present Detlef Stern // // This file is part of Zettelstore. // // Zettelstore client 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 client_test import ( "context" "strings" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present Detlef Stern // // This file is part of Zettelstore. // // Zettelstore client is licensed under the latest version of the EUPL // (European Union Public License). Please see file LICENSE.txt for your rights // and obligations under this license. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package client_test import ( "context" "strings" |
| ︙ | ︙ |
Changes to tests/client/embed_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 client_test import ( "context" "strings" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package client_test import ( "context" "strings" |
| ︙ | ︙ |
Changes to tests/markdown_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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 tests import ( "bytes" "encoding/json" "fmt" "os" "strings" "testing" "zettelstore.de/client.fossil/api" "zettelstore.de/z/ast" "zettelstore.de/z/config" "zettelstore.de/z/encoder" _ "zettelstore.de/z/encoder/htmlenc" _ "zettelstore.de/z/encoder/mdenc" _ "zettelstore.de/z/encoder/shtmlenc" _ "zettelstore.de/z/encoder/szenc" _ "zettelstore.de/z/encoder/textenc" _ "zettelstore.de/z/encoder/zmkenc" | > > > > < | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package tests
import (
"bytes"
"encoding/json"
"fmt"
"os"
"strings"
"testing"
"zettelstore.de/client.fossil/api"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/ast"
"zettelstore.de/z/config"
"zettelstore.de/z/encoder"
_ "zettelstore.de/z/encoder/htmlenc"
_ "zettelstore.de/z/encoder/mdenc"
_ "zettelstore.de/z/encoder/shtmlenc"
_ "zettelstore.de/z/encoder/szenc"
_ "zettelstore.de/z/encoder/textenc"
_ "zettelstore.de/z/encoder/zmkenc"
"zettelstore.de/z/parser"
_ "zettelstore.de/z/parser/markdown"
_ "zettelstore.de/z/parser/zettelmark"
"zettelstore.de/z/zettel/meta"
)
type markdownTestCase struct {
|
| ︙ | ︙ |
Changes to tests/naughtystrings_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 tests import ( "bufio" "io" "os" "path/filepath" "testing" "zettelstore.de/client.fossil/api" | > > > | | | | 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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package tests import ( "bufio" "io" "os" "path/filepath" "testing" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/input" _ "zettelstore.de/z/cmd" "zettelstore.de/z/encoder" "zettelstore.de/z/parser" "zettelstore.de/z/zettel/meta" ) // Test all parser / encoder with a list of "naughty strings", i.e. unusual strings // that often crash software. |
| ︙ | ︙ |
Changes to tests/regression_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 tests provides some higher-level tests. package tests import ( "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package tests provides some higher-level tests. package tests import ( "context" |
| ︙ | ︙ |
Deleted tools/build.go.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added tools/build/build.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 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 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
//-----------------------------------------------------------------------------
// Copyright (c) 2021-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2021-present Detlef Stern
//-----------------------------------------------------------------------------
// Package main provides a command to build and run the software.
package main
import (
"archive/zip"
"bytes"
"flag"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
"time"
"zettelstore.de/client.fossil/api"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/strfun"
"zettelstore.de/z/tools"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
func readVersionFile() (string, error) {
content, err := os.ReadFile("VERSION")
if err != nil {
return "", err
}
return strings.TrimFunc(string(content), func(r rune) bool {
return r <= ' '
}), nil
}
func getVersion() string {
base, err := readVersionFile()
if err != nil {
base = "dev"
}
return base
}
var dirtyPrefixes = []string{
"DELETED ", "ADDED ", "UPDATED ", "CONFLICT ", "EDITED ", "RENAMED ", "EXTRA "}
const dirtySuffix = "-dirty"
func readFossilDirty() (string, error) {
s, err := tools.ExecuteCommand(nil, "fossil", "status", "--differ")
if err != nil {
return "", err
}
for _, line := range strfun.SplitLines(s) {
for _, prefix := range dirtyPrefixes {
if strings.HasPrefix(line, prefix) {
return dirtySuffix, nil
}
}
}
return "", nil
}
func getFossilDirty() string {
fossil, err := readFossilDirty()
if err != nil {
return ""
}
return fossil
}
func cmdBuild() error {
return doBuild(tools.EnvDirectProxy, getVersion(), "bin/zettelstore")
}
func doBuild(env []string, version, target string) error {
env = append(env, "CGO_ENABLED=0")
env = append(env, tools.EnvGoVCS...)
out, err := tools.ExecuteCommand(
env,
"go", "build",
"-tags", "osusergo,netgo",
"-trimpath",
"-ldflags", fmt.Sprintf("-X main.version=%v -w", version),
"-o", target,
"zettelstore.de/z/cmd/zettelstore",
)
if err != nil {
return err
}
if len(out) > 0 {
fmt.Println(out)
}
return nil
}
func cmdHelp() {
fmt.Println(`Usage: go run tools/build/build.go [-v] COMMAND
Options:
-v Verbose output.
Commands:
build Build the software for local computer.
help Output this text.
manual Create a ZIP file with all manual zettel
release Create the software for various platforms and put them in
appropriate named ZIP files.
version Print the current version of the software.
All commands can be abbreviated as long as they remain unique.`)
}
func main() {
flag.BoolVar(&tools.Verbose, "v", false, "Verbose output")
flag.Parse()
var err error
args := flag.Args()
if len(args) < 1 {
cmdHelp()
} else {
switch args[0] {
case "b", "bu", "bui", "buil", "build":
err = cmdBuild()
case "m", "ma", "man", "manu", "manua", "manual":
err = cmdManual()
case "r", "re", "rel", "rele", "relea", "releas", "release":
err = cmdRelease()
case "v", "ve", "ver", "vers", "versi", "versio", "version":
fmt.Print(getVersion())
case "h", "he", "hel", "help":
cmdHelp()
default:
fmt.Fprintf(os.Stderr, "Unknown command %q\n", args[0])
cmdHelp()
os.Exit(1)
}
}
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
// --- manual
func cmdManual() error {
base := getReleaseVersionData()
return createManualZip(".", base)
}
func createManualZip(path, base string) error {
manualPath := filepath.Join("docs", "manual")
entries, err := os.ReadDir(manualPath)
if err != nil {
return err
}
zipName := filepath.Join(path, "manual-"+base+".zip")
zipFile, err := os.OpenFile(zipName, os.O_RDWR|os.O_CREATE, 0600)
if err != nil {
return err
}
defer zipFile.Close()
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
for _, entry := range entries {
if err = createManualZipEntry(manualPath, entry, zipWriter); err != nil {
return err
}
}
return nil
}
const versionZid = "00001000000001"
func createManualZipEntry(path string, entry fs.DirEntry, zipWriter *zip.Writer) error {
info, err := entry.Info()
if err != nil {
return err
}
fh, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
name := entry.Name()
fh.Name = name
fh.Method = zip.Deflate
w, err := zipWriter.CreateHeader(fh)
if err != nil {
return err
}
manualFile, err := os.Open(filepath.Join(path, name))
if err != nil {
return err
}
defer manualFile.Close()
if name != versionZid+".zettel" {
_, err = io.Copy(w, manualFile)
return err
}
data, err := io.ReadAll(manualFile)
if err != nil {
return err
}
inp := input.NewInput(data)
m := meta.NewFromInput(id.MustParse(versionZid), inp)
m.SetNow(api.KeyModified)
var buf bytes.Buffer
if _, err = fmt.Fprintf(&buf, "id: %s\n", versionZid); err != nil {
return err
}
if _, err = m.WriteComputed(&buf); err != nil {
return err
}
version := getVersion()
if _, err = fmt.Fprintf(&buf, "\n%s", version); err != nil {
return err
}
_, err = io.Copy(w, &buf)
return err
}
//--- release
func cmdRelease() error {
if err := tools.Check(true); err != nil {
return err
}
base := getReleaseVersionData()
releases := []struct {
arch string
os string
env []string
name string
}{
{"amd64", "linux", nil, "zettelstore"},
{"arm", "linux", []string{"GOARM=6"}, "zettelstore"},
{"amd64", "darwin", nil, "zettelstore"},
{"arm64", "darwin", nil, "zettelstore"},
{"amd64", "windows", nil, "zettelstore.exe"},
}
for _, rel := range releases {
env := append([]string{}, rel.env...)
env = append(env, "GOARCH="+rel.arch, "GOOS="+rel.os)
env = append(env, tools.EnvDirectProxy...)
env = append(env, tools.EnvGoVCS...)
zsName := filepath.Join("releases", rel.name)
if err := doBuild(env, base, zsName); err != nil {
return err
}
zipName := fmt.Sprintf("zettelstore-%v-%v-%v.zip", base, rel.os, rel.arch)
if err := createReleaseZip(zsName, zipName, rel.name); err != nil {
return err
}
if err := os.Remove(zsName); err != nil {
return err
}
}
return createManualZip("releases", base)
}
func getReleaseVersionData() string {
if fossil := getFossilDirty(); fossil != "" {
fmt.Fprintln(os.Stderr, "Warning: releasing a dirty version")
}
base := getVersion()
if strings.HasSuffix(base, "dev") {
return base[:len(base)-3] + "preview-" + time.Now().Local().Format("20060102")
}
return base
}
func createReleaseZip(zsName, zipName, fileName string) error {
zipFile, err := os.OpenFile(filepath.Join("releases", zipName), os.O_RDWR|os.O_CREATE, 0600)
if err != nil {
return err
}
defer zipFile.Close()
zw := zip.NewWriter(zipFile)
defer zw.Close()
err = addFileToZip(zw, zsName, fileName)
if err != nil {
return err
}
err = addFileToZip(zw, "LICENSE.txt", "LICENSE.txt")
if err != nil {
return err
}
err = addFileToZip(zw, "docs/readmezip.txt", "README.txt")
return err
}
func addFileToZip(zipFile *zip.Writer, filepath, filename string) error {
zsFile, err := os.Open(filepath)
if err != nil {
return err
}
defer zsFile.Close()
stat, err := zsFile.Stat()
if err != nil {
return err
}
fh, err := zip.FileInfoHeader(stat)
if err != nil {
return err
}
fh.Name = filename
fh.Method = zip.Deflate
w, err := zipFile.CreateHeader(fh)
if err != nil {
return err
}
_, err = io.Copy(w, zsFile)
return err
}
|
Added tools/check/check.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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2023-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------
// Package main provides a command to execute unit tests.
package main
import (
"flag"
"fmt"
"os"
"zettelstore.de/z/tools"
)
var release bool
func main() {
flag.BoolVar(&release, "r", false, "Release check")
flag.BoolVar(&tools.Verbose, "v", false, "Verbose output")
flag.Parse()
if err := tools.Check(release); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
|
Added tools/clean/clean.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) 2023-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------
// Package main provides a command to clean / remove development artifacts.
package main
import (
"flag"
"fmt"
"os"
"zettelstore.de/z/tools"
)
func main() {
flag.BoolVar(&tools.Verbose, "v", false, "Verbose output")
flag.Parse()
if err := cmdClean(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
func cmdClean() error {
for _, dir := range []string{"bin", "releases"} {
err := os.RemoveAll(dir)
if err != nil {
return err
}
}
out, err := tools.ExecuteCommand(nil, "go", "clean", "./...")
if err != nil {
return err
}
if len(out) > 0 {
fmt.Println(out)
}
out, err = tools.ExecuteCommand(nil, "go", "clean", "-cache", "-modcache", "-testcache")
if err != nil {
return err
}
if len(out) > 0 {
fmt.Println(out)
}
return nil
}
|
Added tools/devtools/devtools.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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2023-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------
// Package main provides a command to install development tools.
package main
import (
"flag"
"fmt"
"os"
"zettelstore.de/z/tools"
)
func main() {
flag.BoolVar(&tools.Verbose, "v", false, "Verbose output")
flag.Parse()
if err := cmdTools(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
func cmdTools() error {
tools := []struct{ name, pack string }{
{"shadow", "golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest"},
{"unparam", "mvdan.cc/unparam@latest"},
{"staticcheck", "honnef.co/go/tools/cmd/staticcheck@latest"},
{"govulncheck", "golang.org/x/vuln/cmd/govulncheck@latest"},
{"deadcode", "golang.org/x/tools/cmd/deadcode@latest"},
{"errcheck", "github.com/kisielk/errcheck@latest"},
}
for _, tool := range tools {
err := doGoInstall(tool.pack)
if err != nil {
return err
}
}
return nil
}
func doGoInstall(pack string) error {
out, err := tools.ExecuteCommand(nil, "go", "install", pack)
if err != nil {
fmt.Fprintln(os.Stderr, "Unable to install package", pack)
if len(out) > 0 {
fmt.Fprintln(os.Stderr, out)
}
}
return err
}
|
Added tools/htmllint/htmllint.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 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2023-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------
package main
import (
"context"
"flag"
"fmt"
"log"
"math/rand/v2"
"net/url"
"os"
"regexp"
"sort"
"strings"
"zettelstore.de/client.fossil/api"
"zettelstore.de/client.fossil/client"
"zettelstore.de/z/tools"
)
func main() {
flag.BoolVar(&tools.Verbose, "v", false, "Verbose output")
flag.Parse()
if err := cmdValidateHTML(flag.Args()); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
func cmdValidateHTML(args []string) error {
rawURL := "http://localhost:23123"
if len(args) > 0 {
rawURL = args[0]
}
u, err := url.Parse(rawURL)
if err != nil {
return err
}
client := client.NewClient(u)
_, _, metaList, err := client.QueryZettelData(context.Background(), "")
if err != nil {
return err
}
zids, perm := calculateZids(metaList)
for _, kd := range keyDescr {
msgCount := 0
fmt.Fprintf(os.Stderr, "Now checking: %s\n", kd.text)
for _, zid := range zidsToUse(zids, perm, kd.sampleSize) {
var nmsgs int
nmsgs, err = validateHTML(client, kd.uc, api.ZettelID(zid))
if err != nil {
fmt.Fprintf(os.Stderr, "* error while validating zettel %v with: %v\n", zid, err)
msgCount += 1
} else {
msgCount += nmsgs
}
}
if msgCount == 1 {
fmt.Fprintln(os.Stderr, "==> found 1 possible issue")
} else if msgCount > 1 {
fmt.Fprintf(os.Stderr, "==> found %v possible issues\n", msgCount)
}
}
return nil
}
func calculateZids(metaList []api.ZidMetaRights) ([]string, []int) {
zids := make([]string, len(metaList))
for i, m := range metaList {
zids[i] = string(m.ID)
}
sort.Strings(zids)
return zids, rand.Perm(len(metaList))
}
func zidsToUse(zids []string, perm []int, sampleSize int) []string {
if sampleSize < 0 || len(perm) <= sampleSize {
return zids
}
if sampleSize == 0 {
return nil
}
result := make([]string, sampleSize)
for i := range sampleSize {
result[i] = zids[perm[i]]
}
sort.Strings(result)
return result
}
var keyDescr = []struct {
uc urlCreator
text string
sampleSize int
}{
{getHTMLZettel, "zettel HTML encoding", -1},
{createJustKey('h'), "zettel web view", -1},
{createJustKey('i'), "zettel info view", -1},
{createJustKey('e'), "zettel edit form", 100},
{createJustKey('c'), "zettel create form", 10},
{createJustKey('b'), "zettel rename form", 100},
{createJustKey('d'), "zettel delete dialog", 200},
}
type urlCreator func(*client.Client, api.ZettelID) *api.URLBuilder
func createJustKey(key byte) urlCreator {
return func(c *client.Client, zid api.ZettelID) *api.URLBuilder {
return c.NewURLBuilder(key).SetZid(zid)
}
}
func getHTMLZettel(client *client.Client, zid api.ZettelID) *api.URLBuilder {
return client.NewURLBuilder('z').SetZid(zid).
AppendKVQuery(api.QueryKeyEncoding, api.EncodingHTML).
AppendKVQuery(api.QueryKeyPart, api.PartZettel)
}
func validateHTML(client *client.Client, uc urlCreator, zid api.ZettelID) (int, error) {
ub := uc(client, zid)
if tools.Verbose {
fmt.Fprintf(os.Stderr, "GET %v\n", ub)
}
data, err := client.Get(context.Background(), ub)
if err != nil {
return 0, err
}
if len(data) == 0 {
return 0, nil
}
_, stderr, err := tools.ExecuteFilter(data, nil, "tidy", "-e", "-q", "-lang", "en")
if err != nil {
switch err.Error() {
case "exit status 1":
case "exit status 2":
default:
log.Println("SERR", stderr)
return 0, err
}
}
if stderr == "" {
return 0, nil
}
if msgs := filterTidyMessages(strings.Split(stderr, "\n")); len(msgs) > 0 {
fmt.Fprintln(os.Stderr, zid)
for _, msg := range msgs {
fmt.Fprintln(os.Stderr, "-", msg)
}
return len(msgs), nil
}
return 0, nil
}
var reLine = regexp.MustCompile(`line \d+ column \d+ - (.+): (.+)`)
func filterTidyMessages(lines []string) []string {
result := make([]string, 0, len(lines))
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
matches := reLine.FindStringSubmatch(line)
if len(matches) <= 1 {
if line == "This document has errors that must be fixed before" ||
line == "using HTML Tidy to generate a tidied up version." {
continue
}
result = append(result, "!!!"+line)
continue
}
if matches[1] == "Error" {
if len(matches) > 2 {
if matches[2] == "<search> is not recognized!" {
continue
}
}
}
if matches[1] != "Warning" {
result = append(result, "???"+line)
continue
}
if len(matches) > 2 {
switch matches[2] {
case "discarding unexpected <search>",
"discarding unexpected </search>",
`<input> proprietary attribute "inputmode"`:
continue
}
}
result = append(result, line)
}
return result
}
|
Added tools/testapi/testapi.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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2023-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------
// Package main provides a command to test the API
package main
import (
"errors"
"flag"
"fmt"
"io"
"net"
"os"
"os/exec"
"strings"
"time"
"zettelstore.de/z/tools"
)
func main() {
flag.BoolVar(&tools.Verbose, "v", false, "Verbose output")
flag.Parse()
if err := cmdTestAPI(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
type zsInfo struct {
cmd *exec.Cmd
out strings.Builder
adminAddress string
}
func cmdTestAPI() error {
var err error
var info zsInfo
needServer := !addressInUse(":23123")
if needServer {
err = startZettelstore(&info)
}
if err != nil {
return err
}
err = tools.CheckGoTest("zettelstore.de/z/tests/client", "-base-url", "http://127.0.0.1:23123")
if needServer {
err1 := stopZettelstore(&info)
if err == nil {
err = err1
}
}
return err
}
func startZettelstore(info *zsInfo) error {
info.adminAddress = ":2323"
name, arg := "go", []string{
"run", "cmd/zettelstore/main.go", "run",
"-c", "./testdata/testbox/19700101000000.zettel", "-a", info.adminAddress[1:]}
tools.LogCommand("FORK", nil, name, arg)
cmd := tools.PrepareCommand(tools.EnvGoVCS, name, arg, nil, &info.out, os.Stderr)
if !tools.Verbose {
cmd.Stderr = nil
}
err := cmd.Start()
time.Sleep(2 * time.Second)
for range 100 {
time.Sleep(time.Millisecond * 100)
if addressInUse(info.adminAddress) {
info.cmd = cmd
return err
}
}
time.Sleep(4 * time.Second) // Wait for all zettel to be indexed.
return errors.New("zettelstore did not start")
}
func stopZettelstore(i *zsInfo) error {
conn, err := net.Dial("tcp", i.adminAddress)
if err != nil {
fmt.Println("Unable to stop Zettelstore")
return err
}
io.WriteString(conn, "shutdown\n")
conn.Close()
err = i.cmd.Wait()
return err
}
func addressInUse(address string) bool {
conn, err := net.Dial("tcp", address)
if err != nil {
return false
}
conn.Close()
return true
}
|
Added tools/tools.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 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2023-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------
// Package tools provides a collection of functions to build needed tools.
package tools
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"os/exec"
"strings"
"zettelstore.de/z/strfun"
)
var EnvDirectProxy = []string{"GOPROXY=direct"}
var EnvGoVCS = []string{"GOVCS=zettelstore.de:fossil"}
var Verbose bool
func ExecuteCommand(env []string, name string, arg ...string) (string, error) {
LogCommand("EXEC", env, name, arg)
var out strings.Builder
cmd := PrepareCommand(env, name, arg, nil, &out, os.Stderr)
err := cmd.Run()
return out.String(), err
}
func ExecuteFilter(data []byte, env []string, name string, arg ...string) (string, string, error) {
LogCommand("EXEC", env, name, arg)
var stdout, stderr strings.Builder
cmd := PrepareCommand(env, name, arg, bytes.NewReader(data), &stdout, &stderr)
err := cmd.Run()
return stdout.String(), stderr.String(), err
}
func PrepareCommand(env []string, name string, arg []string, in io.Reader, stdout, stderr io.Writer) *exec.Cmd {
if len(env) > 0 {
env = append(env, os.Environ()...)
}
cmd := exec.Command(name, arg...)
cmd.Env = env
cmd.Stdin = in
cmd.Stdout = stdout
cmd.Stderr = stderr
return cmd
}
func LogCommand(exec string, env []string, name string, arg []string) {
if Verbose {
if len(env) > 0 {
for i, e := range env {
fmt.Fprintf(os.Stderr, "ENV%d %v\n", i+1, e)
}
}
fmt.Fprintln(os.Stderr, exec, name, arg)
}
}
func Check(forRelease bool) error {
if err := CheckGoTest("./..."); err != nil {
return err
}
if err := checkGoVet(); err != nil {
return err
}
if err := checkShadow(forRelease); err != nil {
return err
}
if err := checkStaticcheck(); err != nil {
return err
}
if err := checkUnparam(forRelease); err != nil {
return err
}
if forRelease {
if err := checkGoVulncheck(); err != nil {
return err
}
}
return checkFossilExtra()
}
func CheckGoTest(pkg string, testParams ...string) error {
var env []string
env = append(env, EnvDirectProxy...)
env = append(env, EnvGoVCS...)
args := []string{"test", pkg}
args = append(args, testParams...)
out, err := ExecuteCommand(env, "go", args...)
if err != nil {
for _, line := range strfun.SplitLines(out) {
if strings.HasPrefix(line, "ok") || strings.HasPrefix(line, "?") {
continue
}
fmt.Fprintln(os.Stderr, line)
}
}
return err
}
func checkGoVet() error {
out, err := ExecuteCommand(EnvGoVCS, "go", "vet", "./...")
if err != nil {
fmt.Fprintln(os.Stderr, "Some checks failed")
if len(out) > 0 {
fmt.Fprintln(os.Stderr, out)
}
}
return err
}
func checkShadow(forRelease bool) error {
path, err := findExecStrict("shadow", forRelease)
if path == "" {
return err
}
out, err := ExecuteCommand(EnvGoVCS, path, "-strict", "./...")
if err != nil {
fmt.Fprintln(os.Stderr, "Some shadowed variables found")
if len(out) > 0 {
fmt.Fprintln(os.Stderr, out)
}
}
return err
}
func checkStaticcheck() error {
out, err := ExecuteCommand(EnvGoVCS, "staticcheck", "./...")
if err != nil {
fmt.Fprintln(os.Stderr, "Some staticcheck problems found")
if len(out) > 0 {
fmt.Fprintln(os.Stderr, out)
}
}
return err
}
func checkUnparam(forRelease bool) error {
path, err := findExecStrict("unparam", forRelease)
if path == "" {
return err
}
out, err := ExecuteCommand(EnvGoVCS, path, "./...")
if err != nil {
fmt.Fprintln(os.Stderr, "Some unparam problems found")
if len(out) > 0 {
fmt.Fprintln(os.Stderr, out)
}
}
if forRelease {
if out2, err2 := ExecuteCommand(nil, path, "-exported", "-tests", "./..."); err2 != nil {
fmt.Fprintln(os.Stderr, "Some optional unparam problems found")
if len(out2) > 0 {
fmt.Fprintln(os.Stderr, out2)
}
}
}
return err
}
func checkGoVulncheck() error {
out, err := ExecuteCommand(EnvGoVCS, "govulncheck", "./...")
if err != nil {
fmt.Fprintln(os.Stderr, "Some checks failed")
if len(out) > 0 {
fmt.Fprintln(os.Stderr, out)
}
}
return err
}
func findExec(cmd string) string {
if path, err := ExecuteCommand(nil, "which", cmd); err == nil && path != "" {
return strings.TrimSpace(path)
}
return ""
}
func findExecStrict(cmd string, forRelease bool) (string, error) {
path := findExec(cmd)
if path != "" || !forRelease {
return path, nil
}
return "", errors.New("Command '" + cmd + "' not installed, but required for release")
}
func checkFossilExtra() error {
out, err := ExecuteCommand(nil, "fossil", "extra")
if err != nil {
fmt.Fprintln(os.Stderr, "Unable to execute 'fossil extra'")
return err
}
if len(out) > 0 {
fmt.Fprint(os.Stderr, "Warning: unversioned file(s):")
for i, extra := range strfun.SplitLines(out) {
if i > 0 {
fmt.Fprint(os.Stderr, ",")
}
fmt.Fprintf(os.Stderr, " %q", extra)
}
fmt.Fprintln(os.Stderr)
}
return nil
}
|
Changes to usecase/authenticate.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | | 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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" "math/rand/v2" "net/http" "time" "zettelstore.de/client.fossil/api" "zettelstore.de/z/auth" "zettelstore.de/z/auth/cred" "zettelstore.de/z/logger" |
| ︙ | ︙ | |||
83 84 85 86 87 88 89 |
"$2a$10$WHcSO3G9afJ3zlOYQR1suuf83bCXED2jmzjti/MH4YH4l2mivDuze", id.Invalid, "", "")
}
// addDelay after credential checking to allow some CPU time for other tasks.
// durDelay is the normal delay, if time spend for checking is smaller than
// the minimum delay minDelay. In addition some jitter (+/- 50 ms) is added.
func addDelay(start time.Time, durDelay, minDelay time.Duration) {
| | | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
"$2a$10$WHcSO3G9afJ3zlOYQR1suuf83bCXED2jmzjti/MH4YH4l2mivDuze", id.Invalid, "", "")
}
// addDelay after credential checking to allow some CPU time for other tasks.
// durDelay is the normal delay, if time spend for checking is smaller than
// the minimum delay minDelay. In addition some jitter (+/- 50 ms) is added.
func addDelay(start time.Time, durDelay, minDelay time.Duration) {
jitter := time.Duration(rand.IntN(100)-50) * time.Millisecond
if elapsed := time.Since(start); elapsed+minDelay < durDelay {
time.Sleep(durDelay - elapsed + jitter)
} else {
time.Sleep(minDelay + jitter)
}
}
|
| ︙ | ︙ | |||
126 127 128 129 130 131 132 |
IsAuthenticatedAndValid
IsAuthenticatedAndInvalid
)
// Run executes the use case.
func (uc *IsAuthenticated) Run(ctx context.Context) IsAuthenticatedResult {
if !uc.authz.WithAuth() {
| | | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
IsAuthenticatedAndValid
IsAuthenticatedAndInvalid
)
// Run executes the use case.
func (uc *IsAuthenticated) Run(ctx context.Context) IsAuthenticatedResult {
if !uc.authz.WithAuth() {
uc.log.Info().Str("auth", "disabled").Msg("IsAuthenticated")
return IsAuthenticatedDisabled
}
if uc.port.GetUser(ctx) == nil {
uc.log.Info().Msg("IsAuthenticated is false")
return IsAuthenticatedAndInvalid
}
uc.log.Info().Msg("IsAuthenticated is true")
return IsAuthenticatedAndValid
}
|
Changes to usecase/create_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" "time" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" "time" |
| ︙ | ︙ | |||
112 113 114 115 116 117 118 |
content.TrimSpace()
return zettel.Zettel{Meta: m, Content: content}
}
func updateMetaRoleTagsSyntax(m, orig *meta.Meta) {
m.SetNonEmpty(api.KeyRole, orig.GetDefault(api.KeyRole, ""))
m.SetNonEmpty(api.KeyTags, orig.GetDefault(api.KeyTags, ""))
| | | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
content.TrimSpace()
return zettel.Zettel{Meta: m, Content: content}
}
func updateMetaRoleTagsSyntax(m, orig *meta.Meta) {
m.SetNonEmpty(api.KeyRole, orig.GetDefault(api.KeyRole, ""))
m.SetNonEmpty(api.KeyTags, orig.GetDefault(api.KeyTags, ""))
m.SetNonEmpty(api.KeySyntax, orig.GetDefault(api.KeySyntax, meta.DefaultSyntax))
}
func prependTitle(title, s0, s1 string) string {
if len(title) > 0 {
return s1 + title
}
return s0
|
| ︙ | ︙ |
Changes to usecase/delete_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" |
| ︙ | ︙ |
Changes to usecase/evaluate.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" |
| ︙ | ︙ |
Changes to usecase/get_all_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" |
| ︙ | ︙ |
Changes to usecase/get_special_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2023-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" |
| ︙ | ︙ |
Changes to usecase/get_user.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" |
| ︙ | ︙ |
Changes to usecase/get_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" |
| ︙ | ︙ |
Changes to usecase/lists.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" |
| ︙ | ︙ |
Changes to usecase/parse_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" |
| ︙ | ︙ |
Changes to usecase/query.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" "errors" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" "errors" |
| ︙ | ︙ | |||
113 114 115 116 117 118 119 |
func (uc *Query) processContextDirective(ctx context.Context, spec *query.ContextSpec, metaSeq []*meta.Meta) []*meta.Meta {
return spec.Execute(ctx, metaSeq, uc.port)
}
func (uc *Query) processItemsDirective(ctx context.Context, _ *query.ItemsSpec, metaSeq []*meta.Meta) []*meta.Meta {
result := make([]*meta.Meta, 0, len(metaSeq))
for _, m := range metaSeq {
| | | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
func (uc *Query) processContextDirective(ctx context.Context, spec *query.ContextSpec, metaSeq []*meta.Meta) []*meta.Meta {
return spec.Execute(ctx, metaSeq, uc.port)
}
func (uc *Query) processItemsDirective(ctx context.Context, _ *query.ItemsSpec, metaSeq []*meta.Meta) []*meta.Meta {
result := make([]*meta.Meta, 0, len(metaSeq))
for _, m := range metaSeq {
zn, err := uc.ucEvaluate.Run(ctx, m.Zid, m.GetDefault(api.KeySyntax, meta.DefaultSyntax))
if err != nil {
continue
}
for _, ref := range collect.Order(zn) {
if collectedZid, err2 := id.Parse(ref.URL.Path); err2 == nil {
if z, err3 := uc.port.GetZettel(ctx, collectedZid); err3 == nil {
result = append(result, z.Meta)
|
| ︙ | ︙ | |||
203 204 205 206 207 208 209 |
ast.Walk(&v, &is)
if v.found {
result = append(result, cand)
continue candLoop
}
}
| | | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
ast.Walk(&v, &is)
if v.found {
result = append(result, cand)
continue candLoop
}
}
syntax := zettel.Meta.GetDefault(api.KeySyntax, meta.DefaultSyntax)
if !parser.IsASTParser(syntax) {
continue
}
zn := uc.ucEvaluate.RunZettel(ctx, zettel, syntax)
ast.Walk(&v, &zn.Ast)
if v.found {
result = append(result, cand)
|
| ︙ | ︙ |
Changes to usecase/refresh.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" |
| ︙ | ︙ |
Changes to usecase/reindex.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2023-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" |
| ︙ | ︙ | |||
32 33 34 35 36 37 38 |
func NewReIndex(log *logger.Logger, port ReIndexPort) ReIndex {
return ReIndex{log: log, port: port}
}
// Run executes the use case.
func (uc *ReIndex) Run(ctx context.Context, zid id.Zid) error {
err := uc.port.ReIndex(ctx, zid)
| | | 35 36 37 38 39 40 41 42 43 44 |
func NewReIndex(log *logger.Logger, port ReIndexPort) ReIndex {
return ReIndex{log: log, port: port}
}
// Run executes the use case.
func (uc *ReIndex) Run(ctx context.Context, zid id.Zid) error {
err := uc.port.ReIndex(ctx, zid)
uc.log.Info().User(ctx).Err(err).Zid(zid).Msg("ReIndex zettel")
return err
}
|
Changes to usecase/rename_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" |
| ︙ | ︙ |
Changes to usecase/update_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "context" |
| ︙ | ︙ | |||
66 67 68 69 70 71 72 |
}
if !hasContent {
zettel.Content = oldZettel.Content
}
zettel.Content.TrimSpace()
err = uc.port.UpdateZettel(ctx, zettel)
| | | 69 70 71 72 73 74 75 76 77 78 |
}
if !hasContent {
zettel.Content = oldZettel.Content
}
zettel.Content.TrimSpace()
err = uc.port.UpdateZettel(ctx, zettel)
uc.log.Info().User(ctx).Zid(m.Zid).Err(err).Msg("Update zettel")
return err
}
|
Changes to usecase/usecase.go.
1 2 3 4 5 6 7 8 9 10 11 12 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 provides (business) use cases for the zettelstore. package usecase | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package usecase provides (business) use cases for the zettelstore. package usecase |
Changes to usecase/version.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 ( "regexp" "strconv" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package usecase import ( "regexp" "strconv" |
| ︙ | ︙ |
Added web/adapter/adapter.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) 2024-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2024-present Detlef Stern
//-----------------------------------------------------------------------------
// Package adapter provides handlers for web requests, and some helper tools.
package adapter
import (
"context"
"zettelstore.de/client.fossil/api"
"zettelstore.de/z/usecase"
"zettelstore.de/z/zettel/meta"
)
// TryReIndex executes a re-index if the appropriate query action is given.
func TryReIndex(ctx context.Context, actions []string, metaSeq []*meta.Meta, reIndex *usecase.ReIndex) ([]string, error) {
if lenActions := len(actions); lenActions > 0 {
tempActions := make([]string, 0, lenActions)
hasReIndex := false
for _, act := range actions {
if !hasReIndex && act == api.ReIndexAction {
hasReIndex = true
var errAction error
for _, m := range metaSeq {
if err := reIndex.Run(ctx, m.Zid); err != nil {
errAction = err
}
}
if errAction != nil {
return nil, errAction
}
continue
}
tempActions = append(tempActions, act)
}
return tempActions, nil
}
return nil, nil
}
|
Changes to web/adapter/api/api.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "bytes" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package api provides api handlers for web requests. package api import ( "bytes" |
| ︙ | ︙ | |||
51 52 53 54 55 56 57 | policy: pol, tokenLifetime: kernel.Main.GetConfig(kernel.WebService, kernel.WebTokenLifetimeAPI).(time.Duration), } return a } | < < < | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
policy: pol,
tokenLifetime: kernel.Main.GetConfig(kernel.WebService, kernel.WebTokenLifetimeAPI).(time.Duration),
}
return a
}
// NewURLBuilder creates a new URL builder object with the given key.
func (a *API) NewURLBuilder(key byte) *api.URLBuilder { return a.b.NewURLBuilder(key) }
func (a *API) getAuthData(ctx context.Context) *server.AuthData {
return server.GetAuthData(ctx)
}
func (a *API) withAuth() bool { return a.authz.WithAuth() }
|
| ︙ | ︙ |
Changes to web/adapter/api/command.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "context" "net/http" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package api import ( "context" "net/http" |
| ︙ | ︙ |
Changes to web/adapter/api/create_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package api import ( "net/http" |
| ︙ | ︙ | |||
49 50 51 52 53 54 55 |
if err != nil {
a.reportUsecaseError(w, err)
return
}
var result []byte
var contentType string
| | | | 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 |
if err != nil {
a.reportUsecaseError(w, err)
return
}
var result []byte
var contentType string
location := a.NewURLBuilder('z').SetZid(newZid.ZettelID())
switch enc {
case api.EncoderPlain:
result = newZid.Bytes()
contentType = content.PlainText
case api.EncoderData:
result = []byte(sx.Int64(newZid).String())
contentType = content.SXPF
default:
panic(encStr)
}
h := adapter.PrepareHeader(w, contentType)
h.Set(api.HeaderLocation, location.String())
w.WriteHeader(http.StatusCreated)
if _, err = w.Write(result); err != nil {
a.log.Error().Err(err).Zid(newZid).Msg("Create Zettel")
}
}
}
|
Changes to web/adapter/api/delete_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package api import ( "net/http" |
| ︙ | ︙ |
Changes to web/adapter/api/get_data.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package api import ( "net/http" |
| ︙ | ︙ |
Changes to web/adapter/api/get_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "bytes" "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package api import ( "bytes" "context" |
| ︙ | ︙ | |||
23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
"zettelstore.de/z/box"
"zettelstore.de/z/encoder"
"zettelstore.de/z/parser"
"zettelstore.de/z/usecase"
"zettelstore.de/z/web/adapter"
"zettelstore.de/z/web/content"
"zettelstore.de/z/zettel/id"
)
// MakeGetZettelHandler creates a new HTTP handler to return a zettel in various encodings.
func (a *API) MakeGetZettelHandler(getZettel usecase.GetZettel, parseZettel usecase.ParseZettel, evaluate usecase.Evaluate) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
zid, err := id.Parse(r.URL.Path[1:])
if err != nil {
| > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
"zettelstore.de/z/box"
"zettelstore.de/z/encoder"
"zettelstore.de/z/parser"
"zettelstore.de/z/usecase"
"zettelstore.de/z/web/adapter"
"zettelstore.de/z/web/content"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
// MakeGetZettelHandler creates a new HTTP handler to return a zettel in various encodings.
func (a *API) MakeGetZettelHandler(getZettel usecase.GetZettel, parseZettel usecase.ParseZettel, evaluate usecase.Evaluate) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
zid, err := id.Parse(r.URL.Path[1:])
if err != nil {
|
| ︙ | ︙ | |||
91 92 93 94 95 96 97 | } case partMeta: contentType = content.PlainText _, err = z.Meta.Write(&buf) case partContent: | | | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
}
case partMeta:
contentType = content.PlainText
_, err = z.Meta.Write(&buf)
case partContent:
contentType = content.MIMEFromSyntax(z.Meta.GetDefault(api.KeySyntax, meta.DefaultSyntax))
_, err = z.Content.Write(&buf)
}
if err != nil {
a.log.Error().Err(err).Zid(zid).Msg("Unable to store plain zettel/part in buffer")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
if err = writeBuffer(w, &buf, contentType); err != nil {
a.log.Error().Err(err).Zid(zid).Msg("Write Plain data")
}
}
|
| ︙ | ︙ | |||
159 160 161 162 163 164 165 |
_, err = encdr.WriteZettel(&buf, zn, evalMeta)
case partMeta:
_, err = encdr.WriteMeta(&buf, zn.InhMeta, evalMeta)
case partContent:
_, err = encdr.WriteContent(&buf, zn)
}
if err != nil {
| | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
_, err = encdr.WriteZettel(&buf, zn, evalMeta)
case partMeta:
_, err = encdr.WriteMeta(&buf, zn.InhMeta, evalMeta)
case partContent:
_, err = encdr.WriteContent(&buf, zn)
}
if err != nil {
a.log.Error().Err(err).Zid(zn.Zid).Msg("Unable to store data in buffer")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
if buf.Len() == 0 {
w.WriteHeader(http.StatusNoContent)
return
}
if err = writeBuffer(w, &buf, content.MIMEFromEncoding(enc)); err != nil {
a.log.Error().Err(err).Zid(zn.Zid).Msg("Write Encoded Zettel")
}
}
|
Changes to web/adapter/api/login.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" "time" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package api import ( "net/http" "time" |
| ︙ | ︙ |
Changes to web/adapter/api/query.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 ( "bytes" "fmt" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package api import ( "bytes" "fmt" |
| ︙ | ︙ | |||
31 32 33 34 35 36 37 |
)
// MakeQueryHandler creates a new HTTP handler to perform a query.
func (a *API) MakeQueryHandler(queryMeta *usecase.Query, tagZettel *usecase.TagZettel, roleZettel *usecase.RoleZettel, reIndex *usecase.ReIndex) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
urlQuery := r.URL.Query()
| | < < < < > > > > > > > > > > > > > > > > > > < | | | | | | < < < < < < < | 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 |
)
// MakeQueryHandler creates a new HTTP handler to perform a query.
func (a *API) MakeQueryHandler(queryMeta *usecase.Query, tagZettel *usecase.TagZettel, roleZettel *usecase.RoleZettel, reIndex *usecase.ReIndex) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
urlQuery := r.URL.Query()
if a.handleTagZettel(w, r, tagZettel, urlQuery) || a.handleRoleZettel(w, r, roleZettel, urlQuery) {
return
}
sq := adapter.GetQuery(urlQuery)
metaSeq, err := queryMeta.Run(ctx, sq)
if err != nil {
a.reportUsecaseError(w, err)
return
}
actions, err := adapter.TryReIndex(ctx, sq.Actions(), metaSeq, reIndex)
if err != nil {
a.reportUsecaseError(w, err)
return
}
if len(actions) > 0 {
if len(metaSeq) > 0 {
for _, act := range actions {
if act == api.RedirectAction {
zid := metaSeq[0].Zid
ub := a.NewURLBuilder('z').SetZid(zid.ZettelID())
a.redirectFound(w, r, ub, zid)
return
}
}
}
}
var encoder zettelEncoder
var contentType string
switch enc, _ := getEncoding(r, urlQuery); enc {
case api.EncoderPlain:
encoder = &plainZettelEncoder{}
contentType = content.PlainText
case api.EncoderData:
encoder = &dataZettelEncoder{
sq: sq,
getRights: func(m *meta.Meta) api.ZettelRights { return a.getRights(ctx, m) },
}
contentType = content.SXPF
default:
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
var buf bytes.Buffer
err = queryAction(&buf, encoder, metaSeq, actions)
if err != nil {
a.log.Error().Err(err).Str("query", sq.String()).Msg("execute query action")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
if err = writeBuffer(w, &buf, contentType); err != nil {
a.log.Error().Err(err).Msg("write result buffer")
}
}
}
func queryAction(w io.Writer, enc zettelEncoder, ml []*meta.Meta, actions []string) error {
min, max := -1, -1
if len(actions) > 0 {
acts := make([]string, 0, len(actions))
for _, act := range actions {
if strings.HasPrefix(act, api.MinAction) {
if num, err := strconv.Atoi(act[3:]); err == nil && num > 0 {
min = num
continue
}
}
if strings.HasPrefix(act, api.MaxAction) {
if num, err := strconv.Atoi(act[3:]); err == nil && num > 0 {
max = num
continue
}
}
acts = append(acts, act)
}
for _, act := range acts {
if act == api.KeysAction {
return encodeKeysArrangement(w, enc, ml, act)
}
switch key := strings.ToLower(act); meta.Type(key) {
case meta.TypeWord, meta.TypeTagSet:
return encodeMetaKeyArrangement(w, enc, ml, key, min, max)
}
}
}
|
| ︙ | ︙ | |||
184 185 186 187 188 189 190 |
return err
}
}
return nil
}
type dataZettelEncoder struct {
| < < | | | | | | < | | | | | < | | < < < | < | > > > > > > | | < | 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 |
return err
}
}
return nil
}
type dataZettelEncoder struct {
sq *query.Query
getRights func(*meta.Meta) api.ZettelRights
}
func (dze *dataZettelEncoder) writeMetaList(w io.Writer, ml []*meta.Meta) error {
result := make(sx.Vector, len(ml)+1)
result[0] = sx.SymbolList
symID, symZettel := sx.MakeSymbol("id"), sx.MakeSymbol("zettel")
for i, m := range ml {
msz := sexp.EncodeMetaRights(api.MetaRights{
Meta: m.Map(),
Rights: dze.getRights(m),
})
msz = sx.Cons(sx.MakeList(symID, sx.Int64(m.Zid)), msz.Cdr()).Cons(symZettel)
result[i+1] = msz
}
_, err := sx.Print(w, sx.MakeList(
sx.MakeSymbol("meta-list"),
sx.MakeList(sx.MakeSymbol("query"), sx.String(dze.sq.String())),
sx.MakeList(sx.MakeSymbol("human"), sx.String(dze.sq.Human())),
sx.MakeList(result...),
))
return err
}
func (dze *dataZettelEncoder) writeArrangement(w io.Writer, act string, arr meta.Arrangement) error {
result := sx.Nil()
for aggKey, metaList := range arr {
sxMeta := sx.Nil()
for i := len(metaList) - 1; i >= 0; i-- {
sxMeta = sxMeta.Cons(sx.Int64(metaList[i].Zid))
}
sxMeta = sxMeta.Cons(sx.String(aggKey))
result = result.Cons(sxMeta)
}
_, err := sx.Print(w, sx.MakeList(
sx.MakeSymbol("aggregate"),
sx.String(act),
sx.MakeList(sx.MakeSymbol("query"), sx.String(dze.sq.String())),
sx.MakeList(sx.MakeSymbol("human"), sx.String(dze.sq.Human())),
result.Cons(sx.SymbolList),
))
return err
}
func (a *API) handleTagZettel(w http.ResponseWriter, r *http.Request, tagZettel *usecase.TagZettel, vals url.Values) bool {
tag := vals.Get(api.QueryKeyTag)
if tag == "" {
return false
}
ctx := r.Context()
z, err := tagZettel.Run(ctx, tag)
if err != nil {
a.reportUsecaseError(w, err)
return true
}
zid := z.Meta.Zid
newURL := a.NewURLBuilder('z').SetZid(zid.ZettelID())
for key, slVals := range vals {
if key == api.QueryKeyTag {
continue
}
for _, val := range slVals {
newURL.AppendKVQuery(key, val)
}
}
a.redirectFound(w, r, newURL, zid)
return true
}
func (a *API) handleRoleZettel(w http.ResponseWriter, r *http.Request, roleZettel *usecase.RoleZettel, vals url.Values) bool {
role := vals.Get(api.QueryKeyRole)
if role == "" {
return false
}
ctx := r.Context()
z, err := roleZettel.Run(ctx, role)
if err != nil {
a.reportUsecaseError(w, err)
return true
}
zid := z.Meta.Zid
newURL := a.NewURLBuilder('z').SetZid(zid.ZettelID())
for key, slVals := range vals {
if key == api.QueryKeyRole {
continue
}
for _, val := range slVals {
newURL.AppendKVQuery(key, val)
}
}
a.redirectFound(w, r, newURL, zid)
return true
}
func (a *API) redirectFound(w http.ResponseWriter, r *http.Request, ub *api.URLBuilder, zid id.Zid) {
w.Header().Set(api.HeaderContentType, content.PlainText)
http.Redirect(w, r, ub.String(), http.StatusFound)
if _, err := io.WriteString(w, zid.String()); err != nil {
a.log.Error().Err(err).Msg("redirect body")
}
}
|
Changes to web/adapter/api/rename_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" "net/url" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package api import ( "net/http" "net/url" |
| ︙ | ︙ |
Changes to web/adapter/api/request.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "io" "net/http" "net/url" "zettelstore.de/client.fossil/api" | > > > | | | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package api
import (
"io"
"net/http"
"net/url"
"zettelstore.de/client.fossil/api"
"zettelstore.de/client.fossil/input"
"zettelstore.de/client.fossil/sexp"
"zettelstore.de/sx.fossil/sxreader"
"zettelstore.de/z/zettel"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
// getEncoding returns the data encoding selected by the caller.
func getEncoding(r *http.Request, q url.Values) (api.EncodingEnum, string) {
|
| ︙ | ︙ |
Changes to web/adapter/api/response.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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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 (
"bytes"
"net/http"
"zettelstore.de/sx.fossil"
"zettelstore.de/z/web/content"
"zettelstore.de/z/zettel/id"
)
func (a *API) writeObject(w http.ResponseWriter, zid id.Zid, obj sx.Object) error {
var buf bytes.Buffer
if _, err := sx.Print(&buf, obj); err != nil {
| > > > | | 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-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package api
import (
"bytes"
"net/http"
"zettelstore.de/sx.fossil"
"zettelstore.de/z/web/content"
"zettelstore.de/z/zettel/id"
)
func (a *API) writeObject(w http.ResponseWriter, zid id.Zid, obj sx.Object) error {
var buf bytes.Buffer
if _, err := sx.Print(&buf, obj); err != nil {
msg := a.log.Error().Err(err)
if msg != nil {
if zid.IsValid() {
msg = msg.Zid(zid)
}
msg.Msg("Unable to store object in buffer")
}
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return nil
}
return writeBuffer(w, &buf, content.SXPF)
}
|
Changes to web/adapter/api/update_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package api import ( "net/http" |
| ︙ | ︙ |
Changes to web/adapter/errors.go.
1 2 3 4 5 6 7 8 9 10 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. //----------------------------------------------------------------------------- | > > > < < < < < < < < < < < | 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-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package adapter
import "net/http"
// BadRequest signals HTTP status code 400.
func BadRequest(w http.ResponseWriter, text string) {
http.Error(w, text, http.StatusBadRequest)
}
// ErrResourceNotFound is signalled when a web resource was not found.
type ErrResourceNotFound struct{ Path string }
func (ernf ErrResourceNotFound) Error() string { return "resource not found: " + ernf.Path }
|
Changes to web/adapter/request.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "net/http" "net/url" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package adapter import ( "net/http" "net/url" |
| ︙ | ︙ |
Changes to web/adapter/response.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "errors" "fmt" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package adapter import ( "errors" "fmt" |
| ︙ | ︙ |
Changes to web/adapter/webui/const.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 // WebUI related constants. const queryKeyAction = "_action" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package webui // WebUI related constants. const queryKeyAction = "_action" |
| ︙ | ︙ |
Changes to web/adapter/webui/create_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "bytes" "context" |
| ︙ | ︙ | |||
140 141 142 143 144 145 146 |
newZid, err := createZettel.Run(ctx, zettel)
if err != nil {
wui.reportError(ctx, w, err)
return
}
if reEdit {
| | | > | | 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 |
newZid, err := createZettel.Run(ctx, zettel)
if err != nil {
wui.reportError(ctx, w, err)
return
}
if reEdit {
wui.redirectFound(w, r, wui.NewURLBuilder('e').SetZid(newZid.ZettelID()))
} else {
wui.redirectFound(w, r, wui.NewURLBuilder('h').SetZid(newZid.ZettelID()))
}
}
}
// MakeGetZettelFromListHandler creates a new HTTP handler to store content of
// an existing zettel.
func (wui *WebUI) MakeGetZettelFromListHandler(
queryMeta *usecase.Query, evaluate *usecase.Evaluate,
ucListRoles usecase.ListRoles, ucListSyntax usecase.ListSyntax) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
q := adapter.GetQuery(r.URL.Query())
ctx := r.Context()
metaSeq, err := queryMeta.Run(box.NoEnrichQuery(ctx, q), q)
if err != nil {
wui.reportError(ctx, w, err)
return
}
entries, _ := evaluator.QueryAction(ctx, q, metaSeq, wui.rtConfig)
bns := evaluate.RunBlockNode(ctx, entries)
enc := zmkenc.Create()
var zmkContent bytes.Buffer
_, err = enc.WriteBlocks(&zmkContent, &bns)
if err != nil {
wui.reportError(ctx, w, err)
return
}
|
| ︙ | ︙ |
Changes to web/adapter/webui/delete_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "net/http" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "net/http" |
| ︙ | ︙ |
Changes to web/adapter/webui/edit_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "net/http" | > > > < | 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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "net/http" "zettelstore.de/z/box" "zettelstore.de/z/usecase" "zettelstore.de/z/web/adapter" "zettelstore.de/z/zettel/id" ) // MakeEditGetZettelHandler creates a new HTTP handler to display the |
| ︙ | ︙ | |||
68 69 70 71 72 73 74 |
}
if err = updateZettel.Run(r.Context(), zettel, hasContent); err != nil {
wui.reportError(ctx, w, err)
return
}
if reEdit {
| | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 |
}
if err = updateZettel.Run(r.Context(), zettel, hasContent); err != nil {
wui.reportError(ctx, w, err)
return
}
if reEdit {
wui.redirectFound(w, r, wui.NewURLBuilder('e').SetZid(zid.ZettelID()))
} else {
wui.redirectFound(w, r, wui.NewURLBuilder('h').SetZid(zid.ZettelID()))
}
}
}
|
Changes to web/adapter/webui/favicon.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) 2022-present 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 (
"io"
"net/http"
"os"
"path/filepath"
"zettelstore.de/z/web/adapter"
)
func (wui *WebUI) MakeFaviconHandler(baseDir string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
filename := filepath.Join(baseDir, "favicon.ico")
f, err := os.Open(filename)
if err != nil {
| > > > | | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2022-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2022-present Detlef Stern
//-----------------------------------------------------------------------------
package webui
import (
"io"
"net/http"
"os"
"path/filepath"
"zettelstore.de/z/web/adapter"
)
func (wui *WebUI) MakeFaviconHandler(baseDir string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
filename := filepath.Join(baseDir, "favicon.ico")
f, err := os.Open(filename)
if err != nil {
wui.log.Debug().Err(err).Msg("Favicon not found")
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
defer f.Close()
data, err := io.ReadAll(f)
if err != nil {
wui.log.Error().Err(err).Msg("Unable to read favicon data")
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
if err = adapter.WriteData(w, data, ""); err != nil {
wui.log.Error().Err(err).Msg("Write favicon")
}
}
}
|
Changes to web/adapter/webui/forms.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-present 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" "errors" "io" "net/http" "regexp" "strings" "unicode" "zettelstore.de/client.fossil/api" | > > > | | 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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "bytes" "errors" "io" "net/http" "regexp" "strings" "unicode" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/input" "zettelstore.de/z/kernel" "zettelstore.de/z/parser" "zettelstore.de/z/web/content" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/meta" ) |
| ︙ | ︙ |
Changes to web/adapter/webui/forms_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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 "testing"
func TestRemoveEmptyLines(t *testing.T) {
| > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package webui
import "testing"
func TestRemoveEmptyLines(t *testing.T) {
|
| ︙ | ︙ |
Changes to web/adapter/webui/get_info.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "context" "net/http" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "context" "net/http" |
| ︙ | ︙ | |||
79 80 81 82 83 84 85 |
}
unlinkedMeta, err := ucQuery.Run(ctx, createUnlinkedQuery(zid, phrase))
if err != nil {
wui.reportError(ctx, w, err)
return
}
| > | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
}
unlinkedMeta, err := ucQuery.Run(ctx, createUnlinkedQuery(zid, phrase))
if err != nil {
wui.reportError(ctx, w, err)
return
}
entries, _ := evaluator.QueryAction(ctx, nil, unlinkedMeta, wui.rtConfig)
bns := ucEvaluate.RunBlockNode(ctx, entries)
unlinkedContent, _, err := enc.BlocksSxn(&bns)
if err != nil {
wui.reportError(ctx, w, err)
return
}
encTexts := encodingTexts()
shadowLinks := getShadowLinks(ctx, zid, ucGetAllMeta)
|
| ︙ | ︙ | |||
166 167 168 169 170 171 172 |
return encTexts
}
var apiParts = []string{api.PartZettel, api.PartMeta, api.PartContent}
func (wui *WebUI) infoAPIMatrix(zid id.Zid, parseOnly bool, encTexts []string) *sx.Pair {
matrix := sx.Nil()
| | | | 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 |
return encTexts
}
var apiParts = []string{api.PartZettel, api.PartMeta, api.PartContent}
func (wui *WebUI) infoAPIMatrix(zid id.Zid, parseOnly bool, encTexts []string) *sx.Pair {
matrix := sx.Nil()
u := wui.NewURLBuilder('z').SetZid(zid.ZettelID())
for ip := len(apiParts) - 1; ip >= 0; ip-- {
part := apiParts[ip]
row := sx.Nil()
for je := len(encTexts) - 1; je >= 0; je-- {
enc := encTexts[je]
if parseOnly {
u.AppendKVQuery(api.QueryKeyParseOnly, "")
}
u.AppendKVQuery(api.QueryKeyPart, part)
u.AppendKVQuery(api.QueryKeyEncoding, enc)
row = row.Cons(sx.Cons(sx.String(enc), sx.String(u.String())))
u.ClearQuery()
}
matrix = matrix.Cons(sx.Cons(sx.String(part), row))
}
return matrix
}
func (wui *WebUI) infoAPIMatrixParsed(zid id.Zid, encTexts []string) *sx.Pair {
matrix := wui.infoAPIMatrix(zid, true, encTexts)
u := wui.NewURLBuilder('z').SetZid(zid.ZettelID())
for i, row := 0, matrix; i < len(apiParts) && row != nil; row = row.Tail() {
line, isLine := sx.GetPair(row.Car())
if !isLine || line == nil {
continue
}
last := line.LastPair()
|
| ︙ | ︙ |
Changes to web/adapter/webui/get_zettel.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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "context" "net/http" "zettelstore.de/client.fossil/api" "zettelstore.de/sx.fossil" "zettelstore.de/z/box" "zettelstore.de/z/config" "zettelstore.de/z/parser" "zettelstore.de/z/usecase" "zettelstore.de/z/web/server" "zettelstore.de/z/zettel/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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "context" "net/http" "strings" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/shtml" "zettelstore.de/sx.fossil" "zettelstore.de/z/box" "zettelstore.de/z/config" "zettelstore.de/z/parser" "zettelstore.de/z/usecase" "zettelstore.de/z/web/server" "zettelstore.de/z/zettel/id" |
| ︙ | ︙ | |||
52 53 54 55 56 57 58 | } user := server.GetUser(ctx) getTextTitle := wui.makeGetTextTitle(ctx, getZettel) title := parser.NormalizedSpacedText(zn.InhMeta.GetTitle()) env, rb := wui.createRenderEnv(ctx, "zettel", wui.rtConfig.Get(ctx, zn.InhMeta, api.KeyLang), title, user) | | > | 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 |
}
user := server.GetUser(ctx)
getTextTitle := wui.makeGetTextTitle(ctx, getZettel)
title := parser.NormalizedSpacedText(zn.InhMeta.GetTitle())
env, rb := wui.createRenderEnv(ctx, "zettel", wui.rtConfig.Get(ctx, zn.InhMeta, api.KeyLang), title, user)
rb.bindSymbol(symMetaHeader, metaObj)
rb.bindString("heading", sx.String(title))
if role, found := zn.InhMeta.Get(api.KeyRole); found && role != "" {
rb.bindString("role-url", sx.String(wui.NewURLBuilder('h').AppendQuery(api.KeyRole+api.SearchOperatorHas+role).String()))
}
if folgeRole, found := zn.InhMeta.Get(api.KeyFolgeRole); found && folgeRole != "" {
rb.bindString("folge-role-url", sx.String(wui.NewURLBuilder('h').AppendQuery(api.KeyRole+api.SearchOperatorHas+folgeRole).String()))
}
rb.bindString("tag-refs", wui.transformTagSet(api.KeyTags, meta.ListFromValue(zn.InhMeta.GetDefault(api.KeyTags, ""))))
rb.bindString("predecessor-refs", wui.identifierSetAsLinks(zn.InhMeta, api.KeyPredecessor, getTextTitle))
rb.bindString("precursor-refs", wui.identifierSetAsLinks(zn.InhMeta, api.KeyPrecursor, getTextTitle))
rb.bindString("superior-refs", wui.identifierSetAsLinks(zn.InhMeta, api.KeySuperior, getTextTitle))
rb.bindString("urls", metaURLAssoc(zn.InhMeta))
rb.bindString("content", content)
rb.bindString("endnotes", endnotes)
wui.bindLinks(ctx, &rb, "folge", zn.InhMeta, api.KeyFolge, config.KeyShowFolgeLinks, getTextTitle)
wui.bindLinks(ctx, &rb, "subordinate", zn.InhMeta, api.KeySubordinates, config.KeyShowSubordinateLinks, getTextTitle)
wui.bindLinks(ctx, &rb, "back", zn.InhMeta, api.KeyBack, config.KeyShowBackLinks, getTextTitle)
wui.bindLinks(ctx, &rb, "successor", zn.InhMeta, api.KeySuccessors, config.KeyShowSuccessorLinks, getTextTitle)
if role, found := zn.InhMeta.Get(api.KeyRole); found && role != "" {
|
| ︙ | ︙ | |||
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
func (wui *WebUI) identifierSetAsLinks(m *meta.Meta, key string, getTextTitle getTextTitleFunc) *sx.Pair {
if values, ok := m.GetList(key); ok {
return wui.transformIdentifierSet(values, getTextTitle)
}
return nil
}
func (wui *WebUI) bindLinks(ctx context.Context, rb *renderBinder, varPrefix string, m *meta.Meta, key, configKey string, getTextTitle getTextTitleFunc) {
varLinks := varPrefix + "-links"
var symOpen *sx.Symbol
switch wui.rtConfig.Get(ctx, m, configKey) {
case "false":
rb.bindString(varLinks, sx.Nil())
return
case "close":
default:
| > > > > > > > > > > > > | | 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 |
func (wui *WebUI) identifierSetAsLinks(m *meta.Meta, key string, getTextTitle getTextTitleFunc) *sx.Pair {
if values, ok := m.GetList(key); ok {
return wui.transformIdentifierSet(values, getTextTitle)
}
return nil
}
func metaURLAssoc(m *meta.Meta) *sx.Pair {
var result sx.ListBuilder
for _, p := range m.PairsRest() {
if key := p.Key; strings.HasSuffix(key, meta.SuffixKeyURL) {
if val := p.Value; val != "" {
result.Add(sx.Cons(sx.String(capitalizeMetaKey(key)), sx.String(val)))
}
}
}
return result.List()
}
func (wui *WebUI) bindLinks(ctx context.Context, rb *renderBinder, varPrefix string, m *meta.Meta, key, configKey string, getTextTitle getTextTitleFunc) {
varLinks := varPrefix + "-links"
var symOpen *sx.Symbol
switch wui.rtConfig.Get(ctx, m, configKey) {
case "false":
rb.bindString(varLinks, sx.Nil())
return
case "close":
default:
symOpen = shtml.SymAttrOpen
}
lstLinks := wui.zettelLinksSxn(m, key, getTextTitle)
rb.bindString(varLinks, lstLinks)
if sx.IsNil(lstLinks) {
return
}
rb.bindString(varPrefix+"-open", symOpen)
|
| ︙ | ︙ | |||
129 130 131 132 133 134 135 |
for i := len(values) - 1; i >= 0; i-- {
val := values[i]
zid, err := id.Parse(val)
if err != nil {
continue
}
if title, found := getTextTitle(zid); found > 0 {
| | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
for i := len(values) - 1; i >= 0; i-- {
val := values[i]
zid, err := id.Parse(val)
if err != nil {
continue
}
if title, found := getTextTitle(zid); found > 0 {
url := sx.String(wui.NewURLBuilder('h').SetZid(zid.ZettelID()).String())
if title == "" {
lst = lst.Cons(sx.Cons(sx.String(val), url))
} else {
lst = lst.Cons(sx.Cons(sx.String(title), url))
}
}
}
return lst
}
|
Changes to web/adapter/webui/goaction.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "net/http" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "net/http" |
| ︙ | ︙ |
Changes to web/adapter/webui/home.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "context" "errors" "net/http" | > > > < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "context" "errors" "net/http" "zettelstore.de/z/box" "zettelstore.de/z/config" "zettelstore.de/z/web/adapter" "zettelstore.de/z/web/server" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" ) |
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if p := r.URL.Path; p != "/" {
wui.reportError(ctx, w, adapter.ErrResourceNotFound{Path: p})
return
}
homeZid, _ := id.Parse(wui.rtConfig.Get(ctx, nil, config.KeyHomeZettel))
| | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if p := r.URL.Path; p != "/" {
wui.reportError(ctx, w, adapter.ErrResourceNotFound{Path: p})
return
}
homeZid, _ := id.Parse(wui.rtConfig.Get(ctx, nil, config.KeyHomeZettel))
apiHomeZid := homeZid.ZettelID()
if homeZid != id.DefaultHomeZid {
if _, err := s.GetZettel(ctx, homeZid); err == nil {
wui.redirectFound(w, r, wui.NewURLBuilder('h').SetZid(apiHomeZid))
return
}
homeZid = id.DefaultHomeZid
}
|
| ︙ | ︙ |
Changes to web/adapter/webui/htmlgen.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 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 ( "net/url" "strings" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/attrs" "zettelstore.de/client.fossil/maps" "zettelstore.de/client.fossil/shtml" "zettelstore.de/client.fossil/sz" "zettelstore.de/sx.fossil" "zettelstore.de/z/ast" "zettelstore.de/z/encoder" "zettelstore.de/z/encoder/szenc" "zettelstore.de/z/strfun" "zettelstore.de/z/zettel/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 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "net/url" "strings" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/attrs" "zettelstore.de/client.fossil/maps" "zettelstore.de/client.fossil/shtml" "zettelstore.de/client.fossil/sz" "zettelstore.de/sx.fossil" "zettelstore.de/sx.fossil/sxhtml" "zettelstore.de/z/ast" "zettelstore.de/z/encoder" "zettelstore.de/z/encoder/szenc" "zettelstore.de/z/strfun" "zettelstore.de/z/zettel/meta" ) |
| ︙ | ︙ | |||
37 38 39 40 41 42 43 |
tx *szenc.Transformer
th *shtml.Evaluator
lang string
symAt *sx.Symbol
}
func (wui *WebUI) createGenerator(builder urlBuilder, lang string) *htmlGenerator {
| | < < < < < < < < | | | > > > > > > > > > > > > > > > > > > > > > > > > < | < < < | | | < < < | | < < < < < < < < < < < < < < < < < | | | | | | | | | | | < | | | | | | < | | | | 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 179 180 181 182 183 184 185 |
tx *szenc.Transformer
th *shtml.Evaluator
lang string
symAt *sx.Symbol
}
func (wui *WebUI) createGenerator(builder urlBuilder, lang string) *htmlGenerator {
th := shtml.NewEvaluator(1)
findA := func(obj sx.Object) (attr, assoc, rest *sx.Pair) {
pair, isPair := sx.GetPair(obj)
if !isPair || !shtml.SymA.IsEqual(pair.Car()) {
return nil, nil, nil
}
rest = pair.Tail()
if rest == nil {
return nil, nil, nil
}
objA := rest.Car()
attr, isPair = sx.GetPair(objA)
if !isPair || !sxhtml.SymAttr.IsEqual(attr.Car()) {
return nil, nil, nil
}
return attr, attr.Tail(), rest.Tail()
}
linkZettel := func(obj sx.Object) sx.Object {
attr, assoc, rest := findA(obj)
if attr == nil {
return obj
}
hrefP := assoc.Assoc(shtml.SymAttrHref)
if hrefP == nil {
return obj
}
href, ok := sx.GetString(hrefP.Cdr())
if !ok {
return obj
}
zid, fragment, hasFragment := strings.Cut(string(href), "#")
u := builder.NewURLBuilder('h').SetZid(api.ZettelID(zid))
if hasFragment {
u = u.SetFragment(fragment)
}
assoc = assoc.Cons(sx.Cons(shtml.SymAttrHref, sx.String(u.String())))
return rest.Cons(assoc.Cons(sxhtml.SymAttr)).Cons(shtml.SymA)
}
rebind(th, sz.SymLinkZettel, linkZettel)
rebind(th, sz.SymLinkFound, linkZettel)
rebind(th, sz.SymLinkBased, func(obj sx.Object) sx.Object {
attr, assoc, rest := findA(obj)
if attr == nil {
return obj
}
hrefP := assoc.Assoc(shtml.SymAttrHref)
if hrefP == nil {
return obj
}
href, ok := sx.GetString(hrefP.Cdr())
if !ok {
return obj
}
u := builder.NewURLBuilder('/').SetRawLocal(string(href))
assoc = assoc.Cons(sx.Cons(shtml.SymAttrHref, sx.String(u.String())))
return rest.Cons(assoc.Cons(sxhtml.SymAttr)).Cons(shtml.SymA)
})
rebind(th, sz.SymLinkQuery, func(obj sx.Object) sx.Object {
attr, assoc, rest := findA(obj)
if attr == nil {
return obj
}
hrefP := assoc.Assoc(shtml.SymAttrHref)
if hrefP == nil {
return obj
}
href, ok := sx.GetString(hrefP.Cdr())
if !ok {
return obj
}
ur, err := url.Parse(string(href))
if err != nil {
return obj
}
q := ur.Query().Get(api.QueryKeyQuery)
if q == "" {
return obj
}
u := builder.NewURLBuilder('h').AppendQuery(q)
assoc = assoc.Cons(sx.Cons(shtml.SymAttrHref, sx.String(u.String())))
return rest.Cons(assoc.Cons(sxhtml.SymAttr)).Cons(shtml.SymA)
})
rebind(th, sz.SymLinkExternal, func(obj sx.Object) sx.Object {
attr, assoc, rest := findA(obj)
if attr == nil {
return obj
}
assoc = assoc.Cons(sx.Cons(shtml.SymAttrClass, sx.String("external"))).
Cons(sx.Cons(shtml.SymAttrTarget, sx.String("_blank"))).
Cons(sx.Cons(shtml.SymAttrRel, sx.String("noopener noreferrer")))
return rest.Cons(assoc.Cons(sxhtml.SymAttr)).Cons(shtml.SymA)
})
rebind(th, sz.SymEmbed, func(obj sx.Object) sx.Object {
pair, isPair := sx.GetPair(obj)
if !isPair || !shtml.SymIMG.IsEqual(pair.Car()) {
return obj
}
attr, isPair := sx.GetPair(pair.Tail().Car())
if !isPair || !sxhtml.SymAttr.IsEqual(attr.Car()) {
return obj
}
srcP := attr.Tail().Assoc(shtml.SymAttrSrc)
if srcP == nil {
return obj
}
src, isString := sx.GetString(srcP.Cdr())
if !isString {
return obj
}
zid := api.ZettelID(src)
if !zid.IsValid() {
return obj
}
u := builder.NewURLBuilder('z').SetZid(zid)
imgAttr := attr.Tail().Cons(sx.Cons(shtml.SymAttrSrc, sx.String(u.String()))).Cons(sxhtml.SymAttr)
return pair.Tail().Tail().Cons(imgAttr).Cons(shtml.SymIMG)
})
return &htmlGenerator{
tx: szenc.NewTransformer(),
th: th,
lang: lang,
}
}
func rebind(ev *shtml.Evaluator, sym *sx.Symbol, fn func(sx.Object) sx.Object) {
prevFn := ev.ResolveBinding(sym)
ev.Rebind(sym, func(args sx.Vector, env *shtml.Environment) sx.Object {
obj := prevFn(args, env)
if env.GetError() == nil {
return fn(obj)
}
return sx.Nil()
})
}
|
| ︙ | ︙ | |||
229 230 231 232 233 234 235 |
for aelem := att.Tail(); aelem != nil; aelem = aelem.Tail() {
if p, ok := sx.GetPair(aelem.Car()); ok {
key := p.Car()
val := p.Cdr()
if tail, isTail := sx.GetPair(val); isTail {
val = tail.Car()
}
| | | 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
for aelem := att.Tail(); aelem != nil; aelem = aelem.Tail() {
if p, ok := sx.GetPair(aelem.Car()); ok {
key := p.Car()
val := p.Cdr()
if tail, isTail := sx.GetPair(val); isTail {
val = tail.Car()
}
a = a.Set(sz.GoValue(key), sz.GoValue(val))
}
}
name, found := a.Get("name")
if !found || ignore.Has(name) {
continue
}
|
| ︙ | ︙ |
Changes to web/adapter/webui/htmlmeta.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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "context" "errors" "zettelstore.de/client.fossil/api" "zettelstore.de/sx.fossil" "zettelstore.de/sx.fossil/sxhtml" "zettelstore.de/z/ast" "zettelstore.de/z/box" "zettelstore.de/z/parser" "zettelstore.de/z/usecase" "zettelstore.de/z/zettel/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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "context" "errors" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/shtml" "zettelstore.de/sx.fossil" "zettelstore.de/sx.fossil/sxhtml" "zettelstore.de/z/ast" "zettelstore.de/z/box" "zettelstore.de/z/parser" "zettelstore.de/z/usecase" "zettelstore.de/z/zettel/id" |
| ︙ | ︙ | |||
37 38 39 40 41 42 43 | case meta.TypeEmpty: return sx.String(value) case meta.TypeID: return wui.transformIdentifier(value, getTextTitle) case meta.TypeIDSet: return wui.transformIdentifierSet(meta.ListFromValue(value), getTextTitle) case meta.TypeNumber: | | | | | | | < < | | | | | | | | | | | | > > > | | < > | | | > | | < > | | | | | | 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 |
case meta.TypeEmpty:
return sx.String(value)
case meta.TypeID:
return wui.transformIdentifier(value, getTextTitle)
case meta.TypeIDSet:
return wui.transformIdentifierSet(meta.ListFromValue(value), getTextTitle)
case meta.TypeNumber:
return wui.transformKeyValueText(key, value, value)
case meta.TypeString:
return sx.String(value)
case meta.TypeTagSet:
return wui.transformTagSet(key, meta.ListFromValue(value))
case meta.TypeTimestamp:
if ts, ok := meta.TimeValue(value); ok {
return sx.MakeList(
sx.MakeSymbol("time"),
sx.MakeList(
sxhtml.SymAttr,
sx.Cons(sx.MakeSymbol("datetime"), sx.String(ts.Format("2006-01-02T15:04:05"))),
),
sx.MakeList(sxhtml.SymNoEscape, sx.String(ts.Format("2006-01-02 15:04:05"))),
)
}
return sx.Nil()
case meta.TypeURL:
return wui.url2html(sx.String(value))
case meta.TypeWord:
return wui.transformKeyValueText(key, value, value)
case meta.TypeZettelmarkup:
return wui.transformZmkMetadata(value, evalMetadata, gen)
default:
return sx.MakeList(shtml.SymSTRONG, sx.String("Unhandled type: "), sx.String(kt.Name))
}
}
func (wui *WebUI) transformIdentifier(val string, getTextTitle getTextTitleFunc) sx.Object {
text := sx.String(val)
zid, err := id.Parse(val)
if err != nil {
return text
}
title, found := getTextTitle(zid)
switch {
case found > 0:
ub := wui.NewURLBuilder('h').SetZid(zid.ZettelID())
attrs := sx.Nil()
if title != "" {
attrs = attrs.Cons(sx.Cons(shtml.SymAttrTitle, sx.String(title)))
}
attrs = attrs.Cons(sx.Cons(shtml.SymAttrHref, sx.String(ub.String()))).Cons(sxhtml.SymAttr)
return sx.Nil().Cons(sx.String(zid.String())).Cons(attrs).Cons(shtml.SymA)
case found == 0:
return sx.MakeList(sx.MakeSymbol("s"), text)
default: // case found < 0:
return text
}
}
func (wui *WebUI) transformIdentifierSet(vals []string, getTextTitle getTextTitleFunc) *sx.Pair {
if len(vals) == 0 {
return nil
}
const space = sx.String(" ")
text := make(sx.Vector, 0, 2*len(vals))
for _, val := range vals {
text = append(text, space, wui.transformIdentifier(val, getTextTitle))
}
return sx.MakeList(text[1:]...).Cons(shtml.SymSPAN)
}
func (wui *WebUI) transformTagSet(key string, tags []string) *sx.Pair {
if len(tags) == 0 {
return nil
}
const space = sx.String(" ")
text := make(sx.Vector, 0, 2*len(tags)+2)
for _, tag := range tags {
text = append(text, space, wui.transformKeyValueText(key, tag, tag))
}
if len(tags) > 1 {
text = append(text, space, wui.transformKeyValuesText(key, tags, "(all)"))
}
return sx.MakeList(text[1:]...).Cons(shtml.SymSPAN)
}
func (wui *WebUI) transformKeyValueText(key, value, text string) *sx.Pair {
ub := wui.NewURLBuilder('h').AppendQuery(key + api.SearchOperatorHas + value)
return buildHref(ub, text)
}
func (wui *WebUI) transformKeyValuesText(key string, values []string, text string) *sx.Pair {
ub := wui.NewURLBuilder('h')
for _, val := range values {
ub = ub.AppendQuery(key + api.SearchOperatorHas + val)
}
return buildHref(ub, text)
}
func buildHref(ub *api.URLBuilder, text string) *sx.Pair {
return sx.MakeList(
shtml.SymA,
sx.MakeList(
sxhtml.SymAttr,
sx.Cons(shtml.SymAttrHref, sx.String(ub.String())),
),
sx.String(text),
)
}
type evalMetadataFunc = func(string) ast.InlineSlice
|
| ︙ | ︙ | |||
160 161 162 163 164 165 166 |
}
return parser.NormalizedSpacedText(z.Meta.GetTitle()), 1
}
}
func (wui *WebUI) transformZmkMetadata(value string, evalMetadata evalMetadataFunc, gen *htmlGenerator) sx.Object {
is := evalMetadata(value)
| | | 166 167 168 169 170 171 172 173 174 |
}
return parser.NormalizedSpacedText(z.Meta.GetTitle()), 1
}
}
func (wui *WebUI) transformZmkMetadata(value string, evalMetadata evalMetadataFunc, gen *htmlGenerator) sx.Object {
is := evalMetadata(value)
return gen.InlinesSxHTML(&is).Cons(shtml.SymSPAN)
}
|
Changes to web/adapter/webui/lists.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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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 (
"context"
"io"
"net/http"
"net/url"
"slices"
"strconv"
"strings"
"zettelstore.de/client.fossil/api"
"zettelstore.de/sx.fossil"
"zettelstore.de/z/ast"
"zettelstore.de/z/encoding/atom"
"zettelstore.de/z/encoding/rss"
"zettelstore.de/z/encoding/xml"
"zettelstore.de/z/evaluator"
"zettelstore.de/z/query"
"zettelstore.de/z/usecase"
"zettelstore.de/z/web/adapter"
"zettelstore.de/z/web/server"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
// MakeListHTMLMetaHandler creates a HTTP handler for rendering the list of zettel as HTML.
func (wui *WebUI) MakeListHTMLMetaHandler(queryMeta *usecase.Query, tagZettel *usecase.TagZettel, roleZettel *usecase.RoleZettel, reIndex *usecase.ReIndex) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
urlQuery := r.URL.Query()
| > > > > > | < < | | < < < < | | | | > > > > > > > < < < < | | | | | | | | | | > | > | | > > > | | | | | > | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package webui
import (
"context"
"io"
"net/http"
"net/url"
"slices"
"strconv"
"strings"
"zettelstore.de/client.fossil/api"
"zettelstore.de/client.fossil/shtml"
"zettelstore.de/sx.fossil"
"zettelstore.de/sx.fossil/sxhtml"
"zettelstore.de/z/ast"
"zettelstore.de/z/encoding/atom"
"zettelstore.de/z/encoding/rss"
"zettelstore.de/z/encoding/xml"
"zettelstore.de/z/evaluator"
"zettelstore.de/z/query"
"zettelstore.de/z/usecase"
"zettelstore.de/z/web/adapter"
"zettelstore.de/z/web/server"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
// MakeListHTMLMetaHandler creates a HTTP handler for rendering the list of zettel as HTML.
func (wui *WebUI) MakeListHTMLMetaHandler(queryMeta *usecase.Query, tagZettel *usecase.TagZettel, roleZettel *usecase.RoleZettel, reIndex *usecase.ReIndex) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
urlQuery := r.URL.Query()
if wui.handleTagZettel(w, r, tagZettel, urlQuery) ||
wui.handleRoleZettel(w, r, roleZettel, urlQuery) {
return
}
q := adapter.GetQuery(urlQuery)
q = q.SetDeterministic()
ctx := r.Context()
metaSeq, err := queryMeta.Run(ctx, q)
if err != nil {
wui.reportError(ctx, w, err)
return
}
actions, err := adapter.TryReIndex(ctx, q.Actions(), metaSeq, reIndex)
if err != nil {
wui.reportError(ctx, w, err)
return
}
if len(actions) > 0 {
if len(metaSeq) > 0 {
for _, act := range actions {
if act == api.RedirectAction {
ub := wui.NewURLBuilder('h').SetZid(metaSeq[0].Zid.ZettelID())
wui.redirectFound(w, r, ub)
return
}
}
}
switch actions[0] {
case api.AtomAction:
wui.renderAtom(w, q, metaSeq)
return
case api.RSSAction:
wui.renderRSS(ctx, w, q, metaSeq)
return
}
}
var content, endnotes *sx.Pair
numEntries := 0
if bn, cnt := evaluator.QueryAction(ctx, q, metaSeq, wui.rtConfig); bn != nil {
enc := wui.getSimpleHTMLEncoder(wui.rtConfig.Get(ctx, nil, api.KeyLang))
content, endnotes, err = enc.BlocksSxn(&ast.BlockSlice{bn})
if err != nil {
wui.reportError(ctx, w, err)
return
}
numEntries = cnt
}
user := server.GetUser(ctx)
env, rb := wui.createRenderEnv(
ctx, "list",
wui.rtConfig.Get(ctx, nil, api.KeyLang),
wui.rtConfig.GetSiteName(), user)
if q == nil {
rb.bindString("heading", sx.String(wui.rtConfig.GetSiteName()))
} else {
var sb strings.Builder
q.PrintHuman(&sb)
rb.bindString("heading", sx.String(sb.String()))
}
rb.bindString("query-value", sx.String(q.String()))
if tzl := q.GetMetaValues(api.KeyTags, false); len(tzl) > 0 {
sxTzl, sxNoTzl := wui.transformTagZettelList(ctx, tagZettel, tzl)
if !sx.IsNil(sxTzl) {
rb.bindString("tag-zettel", sxTzl)
}
if !sx.IsNil(sxNoTzl) && wui.canCreate(ctx, user) {
rb.bindString("create-tag-zettel", sxNoTzl)
}
}
if rzl := q.GetMetaValues(api.KeyRole, false); len(rzl) > 0 {
sxRzl, sxNoRzl := wui.transformRoleZettelList(ctx, roleZettel, rzl)
if !sx.IsNil(sxRzl) {
rb.bindString("role-zettel", sxRzl)
}
if !sx.IsNil(sxNoRzl) && wui.canCreate(ctx, user) {
rb.bindString("create-role-zettel", sxNoRzl)
}
}
rb.bindString("content", content)
rb.bindString("endnotes", endnotes)
rb.bindString("num-entries", sx.Int64(numEntries))
rb.bindString("num-meta", sx.Int64(len(metaSeq)))
apiURL := wui.NewURLBuilder('z').AppendQuery(q.String())
seed, found := q.GetSeed()
if found {
apiURL = apiURL.AppendKVQuery(api.QueryKeySeed, strconv.Itoa(seed))
} else {
seed = 0
}
if len(metaSeq) > 0 {
rb.bindString("plain-url", sx.String(apiURL.String()))
rb.bindString("data-url", sx.String(apiURL.AppendKVQuery(api.QueryKeyEncoding, api.EncodingData).String()))
if wui.canCreate(ctx, user) {
rb.bindString("create-url", sx.String(wui.createNewURL))
rb.bindString("seed", sx.Int64(seed))
}
}
if rb.err == nil {
err = wui.renderSxnTemplate(ctx, w, id.ListTemplateZid, env)
} else {
err = rb.err
}
if err != nil {
|
| ︙ | ︙ | |||
172 173 174 175 176 177 178 |
}
}
return withZettel, withoutZettel
}
func (wui *WebUI) prependZettelLink(sxZtl *sx.Pair, name string, u *api.URLBuilder) *sx.Pair {
link := sx.MakeList(
| | | | | | | 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 |
}
}
return withZettel, withoutZettel
}
func (wui *WebUI) prependZettelLink(sxZtl *sx.Pair, name string, u *api.URLBuilder) *sx.Pair {
link := sx.MakeList(
shtml.SymA,
sx.MakeList(
sxhtml.SymAttr,
sx.Cons(shtml.SymAttrHref, sx.String(u.String())),
),
sx.String(name),
)
if sxZtl != nil {
sxZtl = sxZtl.Cons(sx.String(", "))
}
return sxZtl.Cons(link)
}
func (wui *WebUI) renderRSS(ctx context.Context, w http.ResponseWriter, q *query.Query, ml []*meta.Meta) {
var rssConfig rss.Configuration
rssConfig.Setup(ctx, wui.rtConfig)
if actions := q.Actions(); len(actions) > 2 && actions[1] == api.TitleAction {
rssConfig.Title = strings.Join(actions[2:], " ")
}
data := rssConfig.Marshal(q, ml)
adapter.PrepareHeader(w, rss.ContentType)
w.WriteHeader(http.StatusOK)
var err error
if _, err = io.WriteString(w, xml.Header); err == nil {
_, err = w.Write(data)
}
if err != nil {
wui.log.Error().Err(err).Msg("unable to write RSS data")
}
}
func (wui *WebUI) renderAtom(w http.ResponseWriter, q *query.Query, ml []*meta.Meta) {
var atomConfig atom.Configuration
atomConfig.Setup(wui.rtConfig)
if actions := q.Actions(); len(actions) > 2 && actions[1] == api.TitleAction {
atomConfig.Title = strings.Join(actions[2:], " ")
}
data := atomConfig.Marshal(q, ml)
adapter.PrepareHeader(w, atom.ContentType)
w.WriteHeader(http.StatusOK)
var err error
|
| ︙ | ︙ | |||
234 235 236 237 238 239 240 |
}
ctx := r.Context()
z, err := tagZettel.Run(ctx, tag)
if err != nil {
wui.reportError(ctx, w, err)
return true
}
| | | | 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 |
}
ctx := r.Context()
z, err := tagZettel.Run(ctx, tag)
if err != nil {
wui.reportError(ctx, w, err)
return true
}
wui.redirectFound(w, r, wui.NewURLBuilder('h').SetZid(z.Meta.Zid.ZettelID()))
return true
}
func (wui *WebUI) handleRoleZettel(w http.ResponseWriter, r *http.Request, roleZettel *usecase.RoleZettel, vals url.Values) bool {
role := vals.Get(api.QueryKeyRole)
if role == "" {
return false
}
ctx := r.Context()
z, err := roleZettel.Run(ctx, role)
if err != nil {
wui.reportError(ctx, w, err)
return true
}
wui.redirectFound(w, r, wui.NewURLBuilder('h').SetZid(z.Meta.Zid.ZettelID()))
return true
}
|
Changes to web/adapter/webui/login.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "context" "net/http" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "context" "net/http" |
| ︙ | ︙ |
Added web/adapter/webui/meta.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) 2024-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2024-present Detlef Stern
//-----------------------------------------------------------------------------
package webui
import (
"strings"
"unicode"
"unicode/utf8"
)
func capitalizeMetaKey(key string) string {
var sb strings.Builder
for i, word := range strings.Split(key, "-") {
if i > 0 {
sb.WriteByte(' ')
}
if newWord, isSpecial := specialWords[word]; isSpecial {
if newWord == "" {
sb.WriteString(strings.ToTitle(word))
} else {
sb.WriteString(newWord)
}
continue
}
r, size := utf8.DecodeRuneInString(word)
if r == utf8.RuneError {
sb.WriteString(word)
continue
}
sb.WriteRune(unicode.ToTitle(r))
sb.WriteString(word[size:])
}
return sb.String()
}
var specialWords = map[string]string{
"css": "",
"html": "",
"github": "GitHub",
"http": "",
"https": "",
"pdf": "",
"svg": "",
"url": "",
}
|
Added web/adapter/webui/meta_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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2024-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2024-present Detlef Stern
//-----------------------------------------------------------------------------
package webui
import "testing"
func TestCapitalizeMetaKey(t *testing.T) {
var testcases = []struct {
key string
exp string
}{
{"", ""},
{"alt-url", "Alt URL"},
{"author", "Author"},
{"back", "Back"},
{"box-number", "Box Number"},
{"cite-key", "Cite Key"},
{"fedi-url", "Fedi URL"},
{"github-url", "GitHub URL"},
{"hshn-bib", "Hshn Bib"},
{"job-url", "Job URL"},
{"new-user-id", "New User Id"},
{"origin-zid", "Origin Zid"},
{"site-url", "Site URL"},
}
for _, tc := range testcases {
t.Run(tc.key, func(t *testing.T) {
got := capitalizeMetaKey(tc.key)
if got != tc.exp {
t.Errorf("capitalize(%q) == %q, but got %q", tc.key, tc.exp, got)
}
})
}
}
|
Changes to web/adapter/webui/rename_zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "fmt" "net/http" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "fmt" "net/http" |
| ︙ | ︙ | |||
93 94 95 96 97 98 99 |
return
}
if err = renameZettel.Run(r.Context(), curZid, newZid); err != nil {
wui.reportError(ctx, w, err)
return
}
| | | 96 97 98 99 100 101 102 103 104 105 |
return
}
if err = renameZettel.Run(r.Context(), curZid, newZid); err != nil {
wui.reportError(ctx, w, err)
return
}
wui.redirectFound(w, r, wui.NewURLBuilder('h').SetZid(newZid.ZettelID()))
}
}
|
Changes to web/adapter/webui/response.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "net/http" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package webui import ( "net/http" |
| ︙ | ︙ |
Changes to web/adapter/webui/sxn_code.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) 2023-present 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 ( "context" "fmt" "io" "zettelstore.de/client.fossil/api" "zettelstore.de/sx.fossil/sxeval" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/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 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2023-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2023-present Detlef Stern
//-----------------------------------------------------------------------------
package webui
import (
"context"
"fmt"
"io"
"zettelstore.de/client.fossil/api"
"zettelstore.de/sx.fossil/sxeval"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
func (wui *WebUI) loadAllSxnCodeZettel(ctx context.Context) (id.Digraph, *sxeval.Binding, error) {
// getMeta MUST currently use GetZettel, because GetMeta just uses the
// Index, which might not be current.
getMeta := func(ctx context.Context, zid id.Zid) (*meta.Meta, error) {
z, err := wui.box.GetZettel(ctx, zid)
if err != nil {
return nil, err
}
return z.Meta, nil
}
dg := buildSxnCodeDigraph(ctx, id.StartSxnZid, getMeta)
if dg == nil {
return nil, wui.rootBinding, nil
}
dg = dg.AddVertex(id.BaseSxnZid).AddEdge(id.StartSxnZid, id.BaseSxnZid)
dg = dg.AddVertex(id.PreludeSxnZid).AddEdge(id.BaseSxnZid, id.PreludeSxnZid)
dg = dg.TransitiveClosure(id.StartSxnZid)
if zid, isDAG := dg.IsDAG(); !isDAG {
return nil, nil, fmt.Errorf("zettel %v is part of a dependency cycle", zid)
}
bind := wui.rootBinding.MakeChildBinding("zettel", 128)
for _, zid := range dg.SortReverse() {
if err := wui.loadSxnCodeZettel(ctx, zid, bind); err != nil {
return nil, nil, err
}
}
return dg, bind, nil
}
type getMetaFunc func(context.Context, id.Zid) (*meta.Meta, error)
func buildSxnCodeDigraph(ctx context.Context, startZid id.Zid, getMeta getMetaFunc) id.Digraph {
m, err := getMeta(ctx, startZid)
if err != nil {
|
| ︙ | ︙ | |||
81 82 83 84 85 86 87 | } } } } return dg } | | > | | | 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 |
}
}
}
}
return dg
}
func (wui *WebUI) loadSxnCodeZettel(ctx context.Context, zid id.Zid, bind *sxeval.Binding) error {
rdr, err := wui.makeZettelReader(ctx, zid)
if err != nil {
return err
}
env := sxeval.MakeExecutionEnvironment(bind)
for {
form, err2 := rdr.Read()
if err2 != nil {
if err2 == io.EOF {
return nil
}
return err2
}
wui.log.Debug().Zid(zid).Str("form", form.String()).Msg("Loaded sxn code")
if _, err2 = env.Eval(form); err2 != nil {
return err2
}
}
}
|
Changes to web/adapter/webui/template.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-present 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" "net/url" "zettelstore.de/client.fossil/api" "zettelstore.de/sx.fossil" "zettelstore.de/sx.fossil/sxbuiltins" "zettelstore.de/sx.fossil/sxeval" "zettelstore.de/sx.fossil/sxhtml" "zettelstore.de/sx.fossil/sxreader" "zettelstore.de/z/box" "zettelstore.de/z/collect" "zettelstore.de/z/config" "zettelstore.de/z/parser" "zettelstore.de/z/web/adapter" "zettelstore.de/z/web/server" "zettelstore.de/z/zettel" "zettelstore.de/z/zettel/id" "zettelstore.de/z/zettel/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 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package webui
import (
"bytes"
"context"
"fmt"
"net/http"
"net/url"
"zettelstore.de/client.fossil/api"
"zettelstore.de/client.fossil/shtml"
"zettelstore.de/sx.fossil"
"zettelstore.de/sx.fossil/sxbuiltins"
"zettelstore.de/sx.fossil/sxeval"
"zettelstore.de/sx.fossil/sxhtml"
"zettelstore.de/sx.fossil/sxreader"
"zettelstore.de/z/box"
"zettelstore.de/z/collect"
"zettelstore.de/z/config"
"zettelstore.de/z/parser"
"zettelstore.de/z/web/adapter"
"zettelstore.de/z/web/server"
"zettelstore.de/z/zettel"
"zettelstore.de/z/zettel/id"
"zettelstore.de/z/zettel/meta"
)
func (wui *WebUI) createRenderBinding() *sxeval.Binding {
root := sxeval.MakeRootBinding(len(specials) + len(builtins) + 3)
for _, syntax := range specials {
root.BindSpecial(syntax)
}
for _, b := range builtins {
root.BindBuiltin(b)
}
root.BindBuiltin(&sxeval.Builtin{
Name: "url-to-html",
MinArity: 1,
MaxArity: 1,
TestPure: sxeval.AssertPure,
Fn: func(_ *sxeval.Environment, args sx.Vector) (sx.Object, error) {
text, err := sxbuiltins.GetString(args, 0)
if err != nil {
return nil, err
}
return wui.url2html(text), nil
},
})
root.BindBuiltin(&sxeval.Builtin{
Name: "zid-content-path",
MinArity: 1,
MaxArity: 1,
TestPure: sxeval.AssertPure,
Fn: func(_ *sxeval.Environment, args sx.Vector) (sx.Object, error) {
s, err := sxbuiltins.GetString(args, 0)
if err != nil {
return nil, err
}
zid, err := id.Parse(string(s))
if err != nil {
return nil, fmt.Errorf("parsing zettel identifier %q: %w", s, err)
}
ub := wui.NewURLBuilder('z').SetZid(zid.ZettelID())
return sx.String(ub.String()), nil
},
})
root.BindBuiltin(&sxeval.Builtin{
Name: "query->url",
MinArity: 1,
MaxArity: 1,
TestPure: sxeval.AssertPure,
Fn: func(_ *sxeval.Environment, args sx.Vector) (sx.Object, error) {
qs, err := sxbuiltins.GetString(args, 0)
if err != nil {
return nil, err
}
u := wui.NewURLBuilder('h').AppendQuery(string(qs))
return sx.String(u.String()), nil
},
})
root.Freeze()
return root
}
var (
specials = []*sxeval.Special{
&sxbuiltins.QuoteS, &sxbuiltins.QuasiquoteS, // quote, quasiquote
&sxbuiltins.UnquoteS, &sxbuiltins.UnquoteSplicingS, // unquote, unquote-splicing
&sxbuiltins.DefVarS, &sxbuiltins.DefConstS, // defvar, defconst
&sxbuiltins.DefunS, &sxbuiltins.LambdaS, // defun, lambda
&sxbuiltins.SetXS, // set!
&sxbuiltins.IfS, // if
&sxbuiltins.BeginS, // begin
&sxbuiltins.DefMacroS, // defmacro
}
builtins = []*sxeval.Builtin{
&sxbuiltins.Equal, // =
&sxbuiltins.NumGreater, // >
&sxbuiltins.NullP, // null?
&sxbuiltins.PairP, // pair?
&sxbuiltins.Car, &sxbuiltins.Cdr, // car, cdr
&sxbuiltins.Caar, &sxbuiltins.Cadr, &sxbuiltins.Cdar, &sxbuiltins.Cddr,
&sxbuiltins.Caaar, &sxbuiltins.Caadr, &sxbuiltins.Cadar, &sxbuiltins.Caddr,
&sxbuiltins.Cdaar, &sxbuiltins.Cdadr, &sxbuiltins.Cddar, &sxbuiltins.Cdddr,
&sxbuiltins.List, // list
&sxbuiltins.Append, // append
&sxbuiltins.Assoc, // assoc
&sxbuiltins.Map, // map
&sxbuiltins.Apply, // apply
&sxbuiltins.Concat, // concat
&sxbuiltins.BoundP, // bound?
&sxbuiltins.Defined, // defined?
&sxbuiltins.CurrentBinding, // current-binding
&sxbuiltins.BindingLookup, // binding-lookup
}
)
func (wui *WebUI) url2html(text sx.String) sx.Object {
if u, errURL := url.Parse(string(text)); errURL == nil {
if us := u.String(); us != "" {
return sx.MakeList(
shtml.SymA,
sx.MakeList(
sxhtml.SymAttr,
sx.Cons(shtml.SymAttrHref, sx.String(us)),
sx.Cons(shtml.SymAttrTarget, sx.String("_blank")),
sx.Cons(shtml.SymAttrRel, sx.String("noopener noreferrer")),
),
text)
}
}
return text
}
func (wui *WebUI) getParentEnv(ctx context.Context) (*sxeval.Binding, error) {
wui.mxZettelBinding.Lock()
defer wui.mxZettelBinding.Unlock()
if parentEnv := wui.zettelBinding; parentEnv != nil {
return parentEnv, nil
}
dag, zettelEnv, err := wui.loadAllSxnCodeZettel(ctx)
if err != nil {
wui.log.Error().Err(err).Msg("loading zettel sxn")
return nil, err
}
wui.dag = dag
wui.zettelBinding = zettelEnv
return zettelEnv, nil
}
// createRenderEnv creates a new environment and populates it with all relevant data for the base template.
func (wui *WebUI) createRenderEnv(ctx context.Context, name, lang, title string, user *meta.Meta) (*sxeval.Binding, renderBinder) {
userIsValid, userZettelURL, userIdent := wui.getUserRenderData(user)
parentEnv, err := wui.getParentEnv(ctx)
bind := parentEnv.MakeChildBinding(name, 128)
rb := makeRenderBinder(bind, err)
rb.bindString("lang", sx.String(lang))
rb.bindString("css-base-url", sx.String(wui.cssBaseURL))
rb.bindString("css-user-url", sx.String(wui.cssUserURL))
rb.bindString("title", sx.String(title))
rb.bindString("home-url", sx.String(wui.homeURL))
rb.bindString("with-auth", sx.MakeBoolean(wui.withAuth))
rb.bindString("user-is-valid", sx.MakeBoolean(userIsValid))
|
| ︙ | ︙ | |||
183 184 185 186 187 188 189 |
}
rb.bindString("new-zettel-links", wui.fetchNewTemplatesSxn(ctx, user))
rb.bindString("search-url", sx.String(wui.searchURL))
rb.bindString("query-key-query", sx.String(api.QueryKeyQuery))
rb.bindString("query-key-seed", sx.String(api.QueryKeySeed))
rb.bindString("FOOTER", wui.calculateFooterSxn(ctx)) // TODO: use real footer
rb.bindString("debug-mode", sx.MakeBoolean(wui.debug))
| | | | | | < | | | < < < < < | | < < | | < | < < < | 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 |
}
rb.bindString("new-zettel-links", wui.fetchNewTemplatesSxn(ctx, user))
rb.bindString("search-url", sx.String(wui.searchURL))
rb.bindString("query-key-query", sx.String(api.QueryKeyQuery))
rb.bindString("query-key-seed", sx.String(api.QueryKeySeed))
rb.bindString("FOOTER", wui.calculateFooterSxn(ctx)) // TODO: use real footer
rb.bindString("debug-mode", sx.MakeBoolean(wui.debug))
rb.bindSymbol(symMetaHeader, sx.Nil())
rb.bindSymbol(symDetail, sx.Nil())
return bind, rb
}
func (wui *WebUI) getUserRenderData(user *meta.Meta) (bool, string, string) {
if user == nil {
return false, "", ""
}
return true, wui.NewURLBuilder('h').SetZid(user.Zid.ZettelID()).String(), user.GetDefault(api.KeyUserID, "")
}
type renderBinder struct {
err error
binding *sxeval.Binding
}
func makeRenderBinder(bind *sxeval.Binding, err error) renderBinder {
return renderBinder{binding: bind, err: err}
}
func (rb *renderBinder) bindString(key string, obj sx.Object) {
if rb.err == nil {
rb.err = rb.binding.Bind(sx.MakeSymbol(key), obj)
}
}
func (rb *renderBinder) bindSymbol(sym *sx.Symbol, obj sx.Object) {
if rb.err == nil {
rb.err = rb.binding.Bind(sym, obj)
}
}
func (rb *renderBinder) bindKeyValue(key string, value string) {
rb.bindString("meta-"+key, sx.String(value))
if kt := meta.Type(key); kt.IsSet {
rb.bindString("set-meta-"+key, makeStringList(meta.ListFromValue(value)))
}
}
func (rb *renderBinder) rebindResolved(key, defKey string) {
if rb.err == nil {
if obj, found := rb.binding.Resolve(sx.MakeSymbol(key)); found {
rb.bindString(defKey, obj)
}
}
}
func (wui *WebUI) bindCommonZettelData(ctx context.Context, rb *renderBinder, user, m *meta.Meta, content *zettel.Content) {
strZid := m.Zid.String()
apiZid := api.ZettelID(strZid)
newURLBuilder := wui.NewURLBuilder
|
| ︙ | ︙ | |||
267 268 269 270 271 272 273 |
}
if wui.canDelete(ctx, user, m) {
rb.bindString("delete-url", sx.String(newURLBuilder('d').SetZid(apiZid).String()))
}
if val, found := m.Get(api.KeyUselessFiles); found {
rb.bindString("useless", sx.Cons(sx.String(val), nil))
}
| > | > > | > | | < | < | | 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 |
}
if wui.canDelete(ctx, user, m) {
rb.bindString("delete-url", sx.String(newURLBuilder('d').SetZid(apiZid).String()))
}
if val, found := m.Get(api.KeyUselessFiles); found {
rb.bindString("useless", sx.Cons(sx.String(val), nil))
}
queryContext := strZid + " " + api.ContextDirective
rb.bindString("context-url", sx.String(newURLBuilder('h').AppendQuery(queryContext).String()))
queryContext += " " + api.FullDirective
rb.bindString("context-full-url", sx.String(newURLBuilder('h').AppendQuery(queryContext).String()))
if wui.canRefresh(user) {
rb.bindString("reindex-url", sx.String(newURLBuilder('h').AppendQuery(
strZid+" "+api.IdentDirective+api.ActionSeparator+api.ReIndexAction).String()))
}
// Ensure to have title, role, tags, and syntax included as "meta-*"
rb.bindKeyValue(api.KeyTitle, m.GetDefault(api.KeyTitle, ""))
rb.bindKeyValue(api.KeyRole, m.GetDefault(api.KeyRole, ""))
rb.bindKeyValue(api.KeyTags, m.GetDefault(api.KeyTags, ""))
rb.bindKeyValue(api.KeySyntax, m.GetDefault(api.KeySyntax, meta.DefaultSyntax))
var metaPairs sx.ListBuilder
for _, p := range m.ComputedPairs() {
key, value := p.Key, p.Value
metaPairs.Add(sx.Cons(sx.String(key), sx.String(value)))
rb.bindKeyValue(key, value)
}
rb.bindString("metapairs", metaPairs.List())
}
func (wui *WebUI) fetchNewTemplatesSxn(ctx context.Context, user *meta.Meta) (lst *sx.Pair) {
if !wui.canCreate(ctx, user) {
return nil
}
ctx = box.NoEnrichContext(ctx)
|
| ︙ | ︙ | |||
311 312 313 314 315 316 317 |
if err2 != nil {
continue
}
if !wui.policy.CanRead(user, z.Meta) {
continue
}
text := sx.String(parser.NormalizedSpacedText(z.Meta.GetTitle()))
| | | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
if err2 != nil {
continue
}
if !wui.policy.CanRead(user, z.Meta) {
continue
}
text := sx.String(parser.NormalizedSpacedText(z.Meta.GetTitle()))
link := sx.String(wui.NewURLBuilder('c').SetZid(zid.ZettelID()).
AppendKVQuery(queryKeyAction, valueActionNew).String())
lst = lst.Cons(sx.Cons(text, link))
}
return lst
}
func (wui *WebUI) calculateFooterSxn(ctx context.Context) *sx.Pair {
|
| ︙ | ︙ | |||
333 334 335 336 337 338 339 | return content } } } return nil } | | > | | | | | > | | | | | | | > > | | > | | 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 |
return content
}
}
}
return nil
}
func (wui *WebUI) getSxnTemplate(ctx context.Context, zid id.Zid, bind *sxeval.Binding) (sxeval.Expr, error) {
if t := wui.getSxnCache(zid); t != nil {
return t, nil
}
reader, err := wui.makeZettelReader(ctx, zid)
if err != nil {
return nil, err
}
objs, err := reader.ReadAll()
if err != nil {
wui.log.Error().Err(err).Zid(zid).Msg("reading sxn template")
return nil, err
}
if len(objs) != 1 {
return nil, fmt.Errorf("expected 1 expression in template, but got %d", len(objs))
}
env := sxeval.MakeExecutionEnvironment(bind)
t, err := env.Compile(objs[0])
if err != nil {
return nil, err
}
wui.setSxnCache(zid, t)
return t, nil
}
func (wui *WebUI) makeZettelReader(ctx context.Context, zid id.Zid) (*sxreader.Reader, error) {
ztl, err := wui.box.GetZettel(ctx, zid)
if err != nil {
return nil, err
}
reader := sxreader.MakeReader(bytes.NewReader(ztl.Content.AsBytes()))
return reader, nil
}
func (wui *WebUI) evalSxnTemplate(ctx context.Context, zid id.Zid, bind *sxeval.Binding) (sx.Object, error) {
templateExpr, err := wui.getSxnTemplate(ctx, zid, bind)
if err != nil {
return nil, err
}
env := sxeval.MakeExecutionEnvironment(bind)
return env.Run(templateExpr)
}
func (wui *WebUI) renderSxnTemplate(ctx context.Context, w http.ResponseWriter, templateID id.Zid, bind *sxeval.Binding) error {
return wui.renderSxnTemplateStatus(ctx, w, http.StatusOK, templateID, bind)
}
func (wui *WebUI) renderSxnTemplateStatus(ctx context.Context, w http.ResponseWriter, code int, templateID id.Zid, bind *sxeval.Binding) error {
detailObj, err := wui.evalSxnTemplate(ctx, templateID, bind)
if err != nil {
return err
}
bind.Bind(symDetail, detailObj)
pageObj, err := wui.evalSxnTemplate(ctx, id.BaseTemplateZid, bind)
if err != nil {
return err
}
if msg := wui.log.Debug(); msg != nil {
// pageObj.String() can be expensive to calculate.
msg.Str("page", pageObj.String()).Msg("render")
}
gen := sxhtml.NewGenerator(sxhtml.WithNewline)
var sb bytes.Buffer
_, err = gen.WriteHTML(&sb, pageObj)
if err != nil {
return err
}
wui.prepareAndWriteHeader(w, code)
if _, err = w.Write(sb.Bytes()); err != nil {
|
| ︙ | ︙ |
Changes to web/adapter/webui/webui.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 ( "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package webui provides web-UI handlers for web requests. package webui import ( "context" |
| ︙ | ︙ | |||
60 61 62 63 64 65 66 | refreshURL string withAuth bool loginURL string logoutURL string searchURL string createNewURL string | < | | | | | < < < < < < < < | 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 |
refreshURL string
withAuth bool
loginURL string
logoutURL string
searchURL string
createNewURL string
rootBinding *sxeval.Binding
mxZettelBinding sync.Mutex
zettelBinding *sxeval.Binding
dag id.Digraph
genHTML *sxhtml.Generator
}
// webuiBox contains all box methods that are needed for WebUI operation.
//
// Note: these function must not do auth checking.
type webuiBox interface {
CanCreateZettel(context.Context) bool
GetZettel(context.Context, id.Zid) (zettel.Zettel, error)
GetMeta(context.Context, id.Zid) (*meta.Meta, error)
CanUpdateZettel(context.Context, zettel.Zettel) bool
AllowRenameZettel(context.Context, id.Zid) bool
CanDeleteZettel(context.Context, id.Zid) bool
}
// New creates a new WebUI struct.
func New(log *logger.Logger, ab server.AuthBuilder, authz auth.AuthzManager, rtConfig config.Config, token auth.TokenManager,
mgr box.Manager, pol auth.Policy, evalZettel *usecase.Evaluate) *WebUI {
loginoutBase := ab.NewURLBuilder('i')
wui := &WebUI{
log: log,
debug: kernel.Main.GetConfig(kernel.CoreService, kernel.CoreDebug).(bool),
ab: ab,
rtConfig: rtConfig,
authz: authz,
|
| ︙ | ︙ | |||
121 122 123 124 125 126 127 |
refreshURL: ab.NewURLBuilder('g').AppendKVQuery("_c", "r").String(),
withAuth: authz.WithAuth(),
loginURL: loginoutBase.String(),
logoutURL: loginoutBase.AppendKVQuery("logout", "").String(),
searchURL: ab.NewURLBuilder('h').String(),
createNewURL: ab.NewURLBuilder('c').String(),
| < | | < < < < < < < | > > > > > | | | | 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 |
refreshURL: ab.NewURLBuilder('g').AppendKVQuery("_c", "r").String(),
withAuth: authz.WithAuth(),
loginURL: loginoutBase.String(),
logoutURL: loginoutBase.AppendKVQuery("logout", "").String(),
searchURL: ab.NewURLBuilder('h').String(),
createNewURL: ab.NewURLBuilder('c').String(),
zettelBinding: nil,
genHTML: sxhtml.NewGenerator(sxhtml.WithNewline),
}
wui.rootBinding = wui.createRenderBinding()
wui.observe(box.UpdateInfo{Box: mgr, Reason: box.OnReload, Zid: id.Invalid})
mgr.RegisterObserver(wui.observe)
return wui
}
var (
symDetail = sx.MakeSymbol("DETAIL")
symMetaHeader = sx.MakeSymbol("META-HEADER")
)
func (wui *WebUI) observe(ci box.UpdateInfo) {
wui.mxCache.Lock()
if ci.Reason == box.OnReload {
clear(wui.templateCache)
} else {
delete(wui.templateCache, ci.Zid)
}
wui.mxCache.Unlock()
wui.mxZettelBinding.Lock()
if ci.Reason == box.OnReload || wui.dag.HasVertex(ci.Zid) {
wui.zettelBinding = nil
wui.dag = nil
}
wui.mxZettelBinding.Unlock()
}
func (wui *WebUI) setSxnCache(zid id.Zid, expr sxeval.Expr) {
wui.mxCache.Lock()
wui.templateCache[zid] = expr
wui.mxCache.Unlock()
}
|
| ︙ | ︙ |
Changes to web/content/content.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 content manages content handling within the web package. // It translates syntax values into content types, and vice versa. package content import ( | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- // Package content manages content handling within the web package. // It translates syntax values into content types, and vice versa. package content import ( |
| ︙ | ︙ |
Changes to web/content/content_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 content_test import ( "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package content_test import ( "testing" |
| ︙ | ︙ |
Changes to web/server/impl/http.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "context" "net" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package impl import ( "context" "net" |
| ︙ | ︙ | |||
24 25 26 27 28 29 30 |
writeTimeout = 10 * time.Second
idleTimeout = 120 * time.Second
)
// httpServer is a HTTP server.
type httpServer struct {
http.Server
| < < | 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 |
writeTimeout = 10 * time.Second
idleTimeout = 120 * time.Second
)
// httpServer is a HTTP server.
type httpServer struct {
http.Server
}
// initializeHTTPServer creates a new HTTP server object.
func (srv *httpServer) initializeHTTPServer(addr string, handler http.Handler) {
if addr == "" {
addr = ":http"
}
srv.Server = http.Server{
Addr: addr,
Handler: handler,
// See: https://blog.cloudflare.com/exposing-go-on-the-internet/
ReadTimeout: readTimeout,
WriteTimeout: writeTimeout,
IdleTimeout: idleTimeout,
}
}
// SetDebug enables debugging goroutines that are started by the server.
// Basically, just the timeout values are reset. This method should be called
// before running the server.
func (srv *httpServer) SetDebug() {
srv.ReadTimeout = 0
|
| ︙ | ︙ |
Changes to web/server/impl/impl.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 Zettelstore web service. package impl import ( "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- // Package impl provides the Zettelstore web service. package impl import ( "context" |
| ︙ | ︙ |
Changes to web/server/impl/router.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 ( "io" "net/http" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package impl import ( "io" "net/http" |
| ︙ | ︙ | |||
182 183 184 185 186 187 188 |
}
if len(t) == 0 {
rt.log.Debug().Msg("no auth token found in request") // IP already logged: ServeHTTP
return r
}
tokenData, err := rt.auth.CheckToken(t, k)
if err != nil {
| | | | 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
}
if len(t) == 0 {
rt.log.Debug().Msg("no auth token found in request") // IP already logged: ServeHTTP
return r
}
tokenData, err := rt.auth.CheckToken(t, k)
if err != nil {
rt.log.Info().Err(err).HTTPIP(r).Msg("invalid auth token")
return r
}
ctx := r.Context()
user, err := rt.ur.GetUser(ctx, tokenData.Zid, tokenData.Ident)
if err != nil {
rt.log.Info().Zid(tokenData.Zid).Str("ident", tokenData.Ident).Err(err).HTTPIP(r).Msg("auth user not found")
return r
}
return r.WithContext(updateContext(ctx, user, &tokenData))
}
func getSessionToken(r *http.Request) []byte {
cookie, err := r.Cookie(sessionName)
|
| ︙ | ︙ |
Changes to web/server/server.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 server provides the Zettelstore web service. package server import ( "context" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package server provides the Zettelstore web service. package server import ( "context" |
| ︙ | ︙ |
Changes to www/build.md.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 | Let's assume, you have created `$HOME/fossils`. 1. Clone the repository: `fossil clone https://zettelstore.de/ $HOME/fossils/zettelstore.fossil`. 1. Create a working directory. Let's assume, you have created `$HOME/zettelstore`. 1. Change into this directory: `cd $HOME/zettelstore`. 1. Open development: `fossil open $HOME/fossils/zettelstore.fossil`. | | | | | | < < < | | > > > > > > > > > > > > | 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 | Let's assume, you have created `$HOME/fossils`. 1. Clone the repository: `fossil clone https://zettelstore.de/ $HOME/fossils/zettelstore.fossil`. 1. Create a working directory. Let's assume, you have created `$HOME/zettelstore`. 1. Change into this directory: `cd $HOME/zettelstore`. 1. Open development: `fossil open $HOME/fossils/zettelstore.fossil`. ## Tools to build, test, and manage In the directory `tools` there are some Go files to automate most aspects of building and testing, (hopefully) platform-independent. The build script is called as: ``` go run tools/build/build.go [-v] COMMAND ``` The flag `-v` enables the verbose mode. It outputs all commands called by the tool. Some important `COMMAND`s are: * `build`: builds the software with correct version information and puts it into a freshly created directory `bin`. * `check`: checks the current state of the working directory to be ready for release (or commit). * `version`: prints the current version information. Therefore, the easiest way to build your own version of the Zettelstore software is to execute the command ``` go run tools/build/build.go build ``` In case of errors, please send the output of the verbose execution: ``` go run tools/build/build.go -v build ``` Other tools are: * `go run tools/clean/clean.go` cleans your Go development worspace. * `go run tools/check/check.go` executes all linters and unit tests. If you add the option `-r` linters are more strict, to be used for a release version. * `go run tools/devtools/devtools.go` install all needed software (see above). * `go run tools/htmllint/htmllint.go [URL]` checks all generated HTML of a Zettelstore accessible at the given URL (default: http://localhost:23123). * `go run tools/testapi/testapi.go` tests the API against a running Zettelstore, which is started automatically. ## A note on the use of Fossil Zettelstore is managed by the Fossil version control system. Fossil is an alternative to the ubiquitous Git version control system. However, Go seems to prefer Git and popular platforms that just support Git. Some dependencies of Zettelstore, namely [Zettelstore client](https://zettelstore.de/client) and [sx](https://zettelstore.de/sx), are |
| ︙ | ︙ |
Changes to www/changes.wiki.
1 2 3 | <title>Change Log</title> <a id="0_17"></a> | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
<title>Change Log</title>
<a id="0_18"></a>
<h2>Changes for Version 0.18.0 (pending)</h2>
<a id="0_17"></a>
<h2>Changes for Version 0.17.0 (2024-03-04)</h2>
* Context search operates only on explicit references. Add the directive
<code>FULL</code> to follow zettel tags additionally.
(breaking)
* Context cost calculation has been changed. Prepare to retrieve different
result.
(breaking)
* Remove metadata type WordSet. It was never implemented completely, and
nobody complained about this.
(breaking)
* Remove logging level “sense”, “warn”,
“fatal”, and “panic”.
(breaking)
* Add query action <code>REDIRECT</code> which redirects to zettel that is
the first in the query result list.
(minor: api, webui)
* Add link to <code>CONTEXT FULL</code> in the zettel info page.
(minor: webui)
* When generating HTML code to query set based metadata (esp. tags), also
generate a query that matches all values.
(minor: webui)
* Show all metadata with key ending “-url” on zettel view.
(minor: webui)
* Make WebUI form elements a little bit more accessible by using HTML
<code>search</code> tag and <code>inputmode</code> attribute.
(minor: webui)
* Add UI action for role zettel, similar to tag zettel. Obviously forgotten
in release 0.16.0, but thanks to the bug fix v0.16.1 detected.
(minor: webui)
* If an action, which is written in uppercase letters, results in an empty
list, the list of selected zettel is returned instead. This allows some
backward compatibility if a new action is introduced.
(minor)
* Only when query list is not empty, allow to show data and plain encoding,
an optionally show the “Save As Zettel” button.
(minor: webui)
* If query list is greater than three elements, show the number of elements
at bottom (before other encodings).
(minor: webui)
* Zettel with syntax “sxn” are pretty-printed during evaluation.
This allows to retrieve parsed zettel content, which checked for syntax,
but is not pretty-printed.
(minor)
* Some smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_16"></a>
<h2>Changes for Version 0.16.1 (2023-12-28)</h2>
* Fix some Sxn definitions to allow role-based UI customizations.
(minor: webui)
<h2>Changes for Version 0.16.0 (2023-11-30)</h2>
* Sx function <code>define</code> is removed, as announced for version
0.15.0. Use <code>defvar</code> (to define variables) or
<code>defun</code> (to define functions) instead. In addition
<code>defunconst</code> defines a constant function, which ensures a fixed
binding of its name to its function body (performance optimization).
(breaking: webui)
|
| ︙ | ︙ | |||
25 26 27 28 29 30 31 |
timestamps, e.g. for YYYY or YYYYMM.
(minor)
* SHTML encoder fixed w.r.t inline quoting. Previously, an <q> tag was
used, which is inappropriate. Restored smart quotes from version 0.3, but
with new SxHTML infrastructure. This affect the html encoder and the WebUI
too. Now, an empty quote should not result in a warning by HTML linters.
(minor: api, webui)
| | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | 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 |
timestamps, e.g. for YYYY or YYYYMM.
(minor)
* SHTML encoder fixed w.r.t inline quoting. Previously, an <q> tag was
used, which is inappropriate. Restored smart quotes from version 0.3, but
with new SxHTML infrastructure. This affect the html encoder and the WebUI
too. Now, an empty quote should not result in a warning by HTML linters.
(minor: api, webui)
* Add new zettelmarkup inline formatting: <code>##Text##</code> will mark /
highlight the given Text. It is typically used to highlight some text,
which is important for you, but not for the original author. When rendered
as HTML, the <mark> tag is used.
(minor: zettelmarkup)
* Add configuration keys to show, not to show, or show the closed list of
referencing zettel in the web user interface. You can set these
configurations system-wide, per user, or per zettel. Often it is used to
ensure a “clean” home zettel. Affects the list of incoming
/ back links, folge zettel, subordinate zettel, and successor zettel.
(minor: webui)
* Some smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_15"></a>
<h2>Changes for Version 0.15.0 (2023-10-26)</h2>
* Sx function <code>define</code> is now deprecated. It will be removed in
version 0.16. Use <code>defvar</code> or <code>defun</code> instead.
Otherwise the WebUI will not work in version 0.16.
(major: webui, deprecated)
* Zettel can be re-indexed via WebUI or API query action
<code>REINDEX</code>. The info page of a zettel contains a link to
re-index the zettel. In a query transclusion, this action is ignored.
(major: api, webui).
* Allow to determine a tag zettel for a given tag.
(major: api, webui)
* Present user the option to create a (missing) tag zettel (in list view).
Results in a new predefined zettel with identifier 00000000090003, which
is a template for new tag zettel.
(minor: webui)
* ZIP file with manual now contains a zettel 00001000000000 that contains
its build date (metadata key <code>created</code>) and version (in the
zettel content)
(minor)
* If an error page cannot be created due to template errors (or similar), a
plain text error page is delivered instead. It shows the original error
and the error that occured durng rendering the original error page.
(minor: webui)
* Some smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_14"></a>
<h2>Changes for Version 0.14.0 (2023-09-22)</h2>
* Remove support for JSON. This was marked deprecated in version 0.12.0. Use
the <code>data</code> encoding instead, a form of symbolic expressions.
(breaking: api; minor: webui)
* Remove deprecated syntax for a context list: <code>CONTEXT zid</code>. Use
<code>zid CONTEXT</code> instead. It was deprecated in version 0.13.0.
(breaking: api, webui, zettelmarkup)
* Replace CSS-role-map mechanism with a more general Sx-based one: user
specific code may generates parts of resulting HTML document.
(breaking: webui)
* Allow meta-tags, i.e. zettel for a specific tag. Meta-tags have the tag
name as a title and specify the role "tag".
(major: webui)
* Allow to load sx code from multiple zettel; dependencies are specified
using <code>precursor</code> metadata.
(major: webui)
* Allow sx code to change WebUI for zettel with specified role.
(major: webui)
* Some minor usability improvements.
(minor: webui)
* Some smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_13"></a>
<h2>Changes for Version 0.13.0 (2023-08-07)</h2>
* There are for new search operators: less, not less, greater, not greater.
These use the same syntax as the operators prefix, not prefix, suffix, not
suffix. The latter are now denoted as <code>[</code>, <code>![</code>,
<code>]</code>, and <code>!]</code>. The first may operate numerically for
metadata like numbers, timestamps, and zettel identifier. They are not
supported for full-text search.
(breaking: api, webui)
* The API endpoint <code>/o/{ID}</code> (order of zettel ID) is no longer
available. Please use the query expression <code>{ID} ITEMS</code>
instead.
(breaking: api)
* The API endpoint <code>/u/{ID}</code> (unlinked references of zettel ID)
is no longer available. Please use the query expression <code>{ID}
UNLINKED</code> instead.
(breaking: api)
* All API endpoints allow to encode zettel data with the <code>data</code>
encodings, incl. creating, updating, retrieving, and querying zettel.
(major: api)
* Change syntax for context query to <code>zid ... CONTEXT</code>. This will
allow to add more directives that operate on zettel identifier. Old syntax
<code>CONTEXT zid</code> will be removed in 0.14.
(major, deprecated)
* Add query directive <code>ITEMS</code> that will produce a list of
metadata of all zettel that are referenced by the originating zettel in
a top-level list. It replaces the API endpoint <code>/o/{ID}</code> (and
makes it more useful).
(major: api, webui)
* Add query directive <code>UNLINKED</code> that will produce a list of
metadata of all zettel that are mentioning the originating zettel in
a top-level, but do not mention them. It replaces the API endpoint
<code>/u/{ID}</code> (and makes it more useful).
(major: api, webui)
* Add query directive <code>IDENT</code> to distinguish a search for
a zettel identifier (“{ID}”), that will list all metadata of
zettel containing that zettel identifier, and a request to just list the
metadata of given zettel (“{ID} IDENT”). The latter could be
filtered further.
(minor: api, webui)
* Add support for metadata key <code>folge-role</code>.
(minor)
* Allow to create a child from a given zettel.
(minor: webui)
* Make zettel entry/edit form a little friendlier: auto-prepend missing '#'
to tags; ensure that role and syntax receive just a word.
(minor: webui)
* Use a zettel that defines builtins for evaluating WebUI templates.
(minor: webui)
* Add links to retrieve result of a query in other formats.
(minor: webui)
* Always log the found configuration file.
(minor: server)
* The use of the <code>json</code> zettel encoding is deprecated (since
version 0.12.0). Support for this encoding will be removed in version
0.14.0. Please use the new <code>data</code> encoding instead.
(deprecated: api)
* Some smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_12"></a>
<h2>Changes for Version 0.12.0 (2023-06-05)</h2>
* Syntax of templates for the web user interface are changed from Mustache
|
| ︙ | ︙ | |||
170 171 172 173 174 175 176 |
that is the short form for "symbolic expression for HTML".
(breaking)
* Render footer zettel on all WebUI pages.
(fix: webui)
* Query search operator "=" now compares for equality, ":" compares
depending on the value type.
(minor: api, webui)
| | | | | > | | | | > | > | | | | | < | | | | | | | | | | | | > | | | | | | > | | | | | | > | | | | | | | | | | | | | | 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 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 |
that is the short form for "symbolic expression for HTML".
(breaking)
* Render footer zettel on all WebUI pages.
(fix: webui)
* Query search operator "=" now compares for equality, ":" compares
depending on the value type.
(minor: api, webui)
* Search term <code>PICK</code> now respects the original sort order. This
makes it more useful and orthogonal to <code>RANDOM</code> and
<code>LIMIT</code>. As a side effect, zettel lists retrieved via the API
are no longer sorted. In case you want a specific order, you must specify
it explicit.
(minor: api, webui)
* New metadata key <code>expire</code> records a timestamp when a zettel
should be treated as, well, expired.
(minor)
* New metadata keys <code>superior</code> and <code>subordinate</code>
(calculated from <code>superior</code>) allow to specify a hierarchy
between zettel.
(minor)
* Metadata keys with suffix <code>-date</code> and <code>-time</code> are
treated as
timestamp values.
(minor)
* <code>sexpr</code> zettel encoding is now documented in the manual.
(minor: manual)
* Build tool allows to install / update external Go tools needed to build
the software.
(minor)
* Show only useful metadata on WebUI, not the internal metadata.
(minor: webui)
* The use of the <code>json</code> zettel encoding is deprecated. Support
for this encoding may be removed in future versions. Please use the new
<code>data</code> encoding instead.
(deprecated: api)
* Some smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_11"></a>
<h2>Changes for Version 0.11.2 (2023-04-16)</h2>
* Render footer zettel on all WebUI pages. Backported from 0.12.0. Many
thanks to HK for reporting it!
(fix: webui)
<h2>Changes for Version 0.11.1 (2023-03-28)</h2>
* Make <code>PICK</code> search term a little bit more deterministic so that
the “Save As Zettel” button produces the same list.
(fix: webui)
<h2>Changes for Version 0.11.0 (2023-03-27)</h2>
* Remove ZJSON encoding. It was announced in version 0.10.0. Use Sexpr
encoding instead.
(breaking)
* Title of a zettel is no longer interpreted as Zettelmarkup text. Now it is
just a plain string, possibly empty. Therefore, no inline formatting (like
bold text), no links, no footnotes, no citations (the latter made
rendering the title often questionable, in some contexts). If you used
special entities, please use the unicode characters directly. However, as
a good practice, it is often the best to printable ASCII characters.
(breaking)
* Remove runtime configuration <code>marker-external</code>. It was added in
version [#0_0_6|0.0.6] and updated in [#0_0_10|0.0.10]. If you want to
change the marker for an external URL, you could modify zettel
00000000020001 (Zettelstore Base CSS) or zettel 00000000025001
(Zettelstore User CSS, preferred) by changing / adding a rule to add some
content after an external <code><a ...></code> tag.
(breaking: webui)
* Add SHTML encoding. This allows to ensure the quality of generated HTML
code. In addition, clients might use it, because it is easier to parse and
manipulate than ordinary HTML. In the future, HTML template zettel will
probably also use SHTML, deprecating the current Mustache syntax (which
was added in [#0_0_9|0.0.9]).
(major)
* Search term <code>PICK n</code>, where <code>n</code> is an integer value
greater zero, will pick randomly <code>n</code> elements from the search
result list. Somehow similar (and faster) as <code>RANDOM LIMIT n</code>,
but allows also later ordering of the resulting list.
(minor)
* Changed cost model for zettel context: a zettel with more
outgoing/incoming references has higher cost than a zettel with less
references. Also added support for traversing tags, with a similar cost
model. As an effect, zettel hubs (in many cases your home zettel) will
less likely add its references. Same for often used tags. The cost model
might change in some details in the future, but the idea of a penalty
applied to zettel / tags with many references will hold.
(minor)
* Some smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_10"></a>
<h2>Changes for Version 0.10.1 (2023-01-30)</h2>
* Show button to save a query into a zettel only when the current user has
authorization to do it.
(fix: webui)
<h2>Changes for Version 0.10.0 (2023-01-24)</h2>
* Remove support for endpoints <code>/j, /m, /q, /p, /v</code>. Their
functions are merged into endpoint <code>/z</code>. This was announced in
version 0.9.0. Please use only client library with at least version 0.10.0
too.
(breaking: api)
* Remove support for runtime configuration key <code>footer-html</code>. Use
<code>footer-zettel</code> instead. Deprecated in version 0.9.0.
(breaking: webui)
* Save a query into a zettel to freeze it.
(major: webui)
* Allow to show all used metadata keys, linked with their occurrences and
their values.
(minor: webui)
* Mark ZJSON encoding as deprecated for v0.11.0. Please use Sexpr encoding
instead.
(deprecated)
* Some smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_9"></a>
<h2>Changes for Version 0.9.0 (2022-12-12)</h2>
* Remove support syntax <code>pikchr</code>. Although it was a nice idea to
include it into Zettelstore, the implementation is too brittle (w.r.t. the
expected long lifetime of Zettelstore). There should be other ways to
support SVG front-ends.
(breaking)
* Allow to upload content when creating / updating a zettel.
(major: webui)
* Add syntax “draw” (again)
(minor: zettelmarkup)
* Allow to encode zettel in Markdown. Please note: not every aspect of
a zettel can be encoded in Markdown. Those aspects will be ignored.
(minor: api)
* Enhance zettel context by raising the importance of folge zettel (and
similar).
(minor: api, webui)
* Interpret zettel files with extension <code>.webp</code> as an binary
image file format.
(minor)
* Allow to specify service specific log level via statup configuration and
via command line.
(minor)
* Allow to specify a zettel to serve footer content via runtime
comfiguration <code>footer-zettel</code>. Can be overwritten by user
zettel.
(minor: webui)
* Footer data is automatically separated by a thematic break / horizontal
rule. If you do not like it, you have to update the base template.
(minor: webui)
* Allow to set runtime configuration <code>home-zettel</code> in the user
zettel to make it user-specific.
(minor: webui)
* Serve favicon.ico from the asset directory.
(minor: webui)
* Zettelmarkup cheat sheet
(minor: manual)
* Runtime configuration key <code>footer-html</code> will be removed in
Version 0.10.0. Please use <code>footer-zettel</code> instead.
(deprecated: webui)
* In the next version 0.10.0, the API endpoints for a zettel
(<code>/j</code>, <code>/p</code>, <code>/v</code>) will be merged with
endpoint <code>/z</code>. Basically, the previous endpoint will be
refactored as query parameter of endpoint <code>/z</code>. To reduce
errors, there will be no version, where the previous endpoint are still
available and the new funnctionality is still there. This is a warning to
prepare for some breaking changes in v0.10.0. This also affects the API
client implementation.
(warning: api)
* Some smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_8"></a>
<h2>Changes for Version 0.8.0 (2022-10-20)</h2>
* Remove support for tags within zettel content. Removes also property
metadata keys <code>all-tags</code> and <code>computed-tags</code>.
Deprecated in version 0.7.0.
(breaking: zettelmarkup, api, webui)
* Remove API endpoint <code>/m</code>, which retrieve aggregated (tags,
roles) zettel identifier. Deprecated in version 0.7.0.
(breaking: api)
* Remove support for URL query parameter starting with an underscore.
Deprecated in version 0.7.0.
(breaking: api, webui)
* Ignore HTML content by default, and allow HTML gradually by setting
startup value <code>insecure-html</code>.
(breaking: markup)
* Endpoint <code>/q</code> returns list of full metadata, if no query action
is specified. A HTTP call <code>GET /z</code> (retrieving metadata of all
or some zettel) is now an alias for <code>GET /q</code>.
(major: api)
* Allow to create a zettel that acts as the new version of an existing
zettel. Useful if you want to have access to older, outdated content.
(minor: webui)
* Allow transclusion to reference local image via URL.
(minor: zettelmarkup, webui)
* Add categories in RSS feed, based on zettel tags.
|
| ︙ | ︙ | |||
372 373 374 375 376 377 378 |
<a id="0_7"></a>
<h2>Changes for Version 0.7.1 (2022-09-18)</h2>
* Produce a RSS feed compatible to Miniflux.
(minor)
* Make sure to always produce a pubdata in RSS feed.
(bug)
* Prefix search for data that looks like a zettel identifier may end with a
| | | | | | | | | > | | | | | | | | | | | | > | | | | | | | | | | | | | 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 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 503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
<a id="0_7"></a>
<h2>Changes for Version 0.7.1 (2022-09-18)</h2>
* Produce a RSS feed compatible to Miniflux.
(minor)
* Make sure to always produce a pubdata in RSS feed.
(bug)
* Prefix search for data that looks like a zettel identifier may end with a
<code>0</code>.
(bug)
* Fix glitch on manual zettel.
(bug)
<h2>Changes for Version 0.7.0 (2022-09-17)</h2>
* Removes support for URL query parameter to search for metadata values,
sorting, offset, and limit a zettel list. Deprecated in version 0.6.0
(breaking: api, webui)
* Allow to search for the existence / non-existence of a metadata key with
the "?" operator: <code>key?</code> and <code>key!?</code>. Previously,
the ":" operator was used for this by specifying an empty search value.
Now you can use the ":" operator to find empty / non-empty metadata
values. If you specify a search operator for metadata, the specified key
is assumed to exist.
(breaking: api, webui)
* Rename “search expression” into “query
expressions”. Similar, the reference prefix <code>search:</code> to
specify a query link or a query transclusion is renamed to
<code>query:</code>
(breaking: zettelmarkup)
* Rename query parameter for query expression from <code>_s</code> to
<code>q</code>.
(breaking: api, webui)
* Cleanup names for HTTP query parameters in WebUI. Update your bookmarks
if you used them. (For API: see below)
(breaking: webui)
* Allow search terms to be OR-ed. This allows to specify any search
expression in disjunctive normal form. Therefore, the NEGATE term is not
needed any more.
(breaking: api, webui)
* Replace runtime configuration <code>default-lang</code> with
<code>lang</code>. Additionally, <code>lang</code> set at the zettel of
the current user, will provide a default value for the current user,
overwriting the global default value.
(breaking)
* Add new syntax <code>pikchr</code>, a markup language for diagrams in
technical documentation.
(major)
* Add endpoint <code>/q</code> to query the zettelstore and aggregate
resulting values. This is done by extending the query syntax.
(major: api)
* Add support for query actions. Actions may aggregate w.r.t. some metadata
keys, or produce an RSS feed.
(major: api, webui)
* Query results can be ordered for more than one metadata key. Ordering by
zettel identifier is an implicit last order expression to produce stable
results.
(minor: api, webui)
* Add support for an asset directory, accessible via URL prefix
<code>/assests/</code>.
(minor: server)
* Add support for metadata key <code>created</code>, a timestamp when the
zettel was created. Since key <code>published</code> is now either
<code>created</code> or <code>modified</code>, it will now always contains
a valid time stamp.
(minor)
* Add support for metadata key <code>author</code>. It will be displayed on
a zettel, if set.
(minor: webui)
* Remove CSS for lists. The browsers default value for
<code>padding-left</code> will be used.
(minor: webui)
* Removed templates for rendering roles and tags lists. This is now done by
query actions.
(minor: webui)
* Tags within zettel content are deprecated in version 0.8. This affects the
computed metadata keys <code>content-tags</code> and
<code>all-tags</code>. They will be removed. The number sign of a content
tag introduces unintended tags, esp. in the english language; content tags
may occur within links → links within links, when rendered as HTML;
content tags may occur in the title of a zettel; naming of content tags,
zettel tags, and their union is confusing for many. Migration: use zettel
tags or replace content tag with a search.
(deprecated: zettelmarkup)
* Cleanup names for HTTP query parameter for API calls. Essentially,
underscore characters in front are removed. Please use new names, old
names will be deprecated in version 0.8.
(deprecated: api)
* Some smaller bug fixes and improvements, to the software and to the
documentation.
|
| ︙ | ︙ | |||
515 516 517 518 519 520 521 |
* If authentication is enabled, a secret of at least 16 bytes must be set in
the startup configuration.
(breaking)
* “Sexpr” encoding replaces “Native” encoding. Sexpr
encoding is much easier to parse, compared with native and ZJSON encoding.
In most cases it is smaller than ZJSON.
(breaking: api)
| | | | | | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 |
* If authentication is enabled, a secret of at least 16 bytes must be set in
the startup configuration.
(breaking)
* “Sexpr” encoding replaces “Native” encoding. Sexpr
encoding is much easier to parse, compared with native and ZJSON encoding.
In most cases it is smaller than ZJSON.
(breaking: api)
* Endpoint <code>/r</code> is changed to <code>/m?_key=role</code> and
returns now a map of role names to the list of zettel having this role.
Endpoint <code>/t</code> is changed to <code>/m?_key=tags</code>. It
already returned mapping described before.
(breaking: api)
* Remove support for a default value for metadata key title, role, and
syntax. Title and role are now allowed to be empty, an empty syntax value
defaults to “plain”.
(breaking)
* Add support for an “evaluation block” syntax in Zettelmarkup
to allow interpretation of content by external software.
|
| ︙ | ︙ | |||
563 564 565 566 567 568 569 |
documentation.
<a id="0_4"></a>
<h2>Changes for Version 0.4 (2022-03-08)</h2>
* Encoding “djson” renamed to “zjson” (<em>zettel
json</em>).
(breaking: api; minor: webui)
| | | | 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 |
documentation.
<a id="0_4"></a>
<h2>Changes for Version 0.4 (2022-03-08)</h2>
* Encoding “djson” renamed to “zjson” (<em>zettel
json</em>).
(breaking: api; minor: webui)
* Remove inline quotation syntax <code><<...<<</code>. Now,
<code>""...""</code> generates the equivalent code.
Typographical quotes are generated by the browser, not by Zettelstore.
(breaking: Zettelmarkup)
* Remove inline formatting for monospace. Its syntax is now used by the
similar syntax element of literal computer input. Monospace was just
a visual element with no semantic association. Now, the syntax
<kbd>++...++</kbd> is obsolete.
(breaking: Zettelmarkup).
|
| ︙ | ︙ | |||
595 596 597 598 599 600 601 |
interpreted as Zettelmarkup. Similar, the suffix <kbd>-set</kbd> denotes
a set/list of words and the suffix <kbd>-zids</kbd> a set/list of zettel
identifier.
(minor: api, webui)
* Change generated URLs for zettel-creation forms. If you have bookmarked
them, e.g. to create a new zettel, you should update.
(minor: webui)
| | | | 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 |
interpreted as Zettelmarkup. Similar, the suffix <kbd>-set</kbd> denotes
a set/list of words and the suffix <kbd>-zids</kbd> a set/list of zettel
identifier.
(minor: api, webui)
* Change generated URLs for zettel-creation forms. If you have bookmarked
them, e.g. to create a new zettel, you should update.
(minor: webui)
* Remove support for metadata key <code>no-index</code> to suppress indexing
selected zettel. It was introduced in <a href="#0_0_11">v0.0.11</a>, but
disallows some future optimizations for searching zettel.
(minor: api, webui)
* Make some metadata-based searches a little bit faster by executing
a (in-memory-based) full-text search first. Now only those zettel are
loaded from file that contain the metdata value.
(minor: api, webui)
* Add an API call to retrieve the version of the Zettelstore.
(minor: api)
* Limit the amount of zettel and bytes to be stored in a memory box. Allows
to use it with public access.
(minor: box)
* Disallow to cache the authentication cookie. Will remove most unexpected
log-outs when using a mobile device.
(minor: webui)
* Many smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_3"></a>
<h2>Changes for Version 0.3 (2022-02-09)</h2>
* Zettel files with extension <code>.meta</code> are now treated as content
files. Previoulsy, they were interpreted as metadata files. The
interpretation as metadata files was deprecated in version 0.2.
(breaking: directory and file/zip box)
* Add syntax “draw” to produce some graphical representations.
(major)
* Add Zettelmarkup syntax to specify full transclusion of other zettel.
(major: Zettelmarkup)
|
| ︙ | ︙ | |||
639 640 641 642 643 644 645 |
(minor: directory and file/zip box)
* Many smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_2"></a>
<h2>Changes for Version 0.2 (2022-01-19)</h2>
* v0.2.1 (2021-02-01) updates the license year in some documents
| | | | | | | | | | | | > | | | | | | | | | > | | | | | | | | | | > | | | | 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 |
(minor: directory and file/zip box)
* Many smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_2"></a>
<h2>Changes for Version 0.2 (2022-01-19)</h2>
* v0.2.1 (2021-02-01) updates the license year in some documents
* Remove support for <code>;;small text;;</code> Zettelmarkup.
(breaking: Zettelmarkup)
* On macOS, the downloadable executable program is now called
“zettelstore”, as on all other Unix-like platforms.
(possibly breaking: macOS)
* External metadata (e.g. for zettel with file extension other than
<code>.zettel</code>) are stored in files without an extension. Metadata
files with extension <code>.meta</code> are still recognized, but result
in a warning message. In a future version (probably v0.3),
<code>.meta</code> files will be treated as ordinary content files,
possibly resulting in duplicate content. In other words: usage of
<code>.meta</code> files for storing metadata is deprecated.
(possibly breaking: directory and file box)
* 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 <code>/u/{ID}</code> to retrieve unlinked references.
(major: api)
* Provide a logging facility.
Log messages are written to standard output. Messages with level
“information” are also written to a circular buffer (of length
8192) which can be retrieved via a computed zettel. There is a command
line flag <code>-l LEVEL</code> to specify an application global logging
level on startup (default: “information”). Logging level can
also be changed via the administrator console, even for specific (sub-)
services.
(major)
* The internal handling of zettel files is rewritten. This allows less
reloads ands detects when the directory containing the zettel files is
removed. The API, WebUI, and the admin console allow to manually refresh
the internal state on demand.
(major: box, webui)
* <code>.zettel</code> files with YAML header are now correctly written.
(bug)
* 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 <code>00</code> by using the query
<code>id=!<00</code>.
(minor: api, webui)
* Remove support for <code>//deprecated emphasized//</code> 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)
* Add API call to check for enabled authentication.
(minor: api)
* Renewing an API access token works even if authentication is not enabled.
This corresponds to the behaviour of optaining an access token.
(minor: api)
* If there is nothing to return, use HTTP status code 204, instead of 200 +
<code>Content-Length: 0</code>.
(minor: api)
* Metadata key <code>duplicates</code> stores the duplicate file names,
instead of just a boolean value that there were duplicate file names.
(minor)
* Document autostarting Zettelstore on Windows, macOS, and Linux.
(minor)
* Many smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_1"></a><a id="0_1_0"></a>
<h2>Changes for Version 0.1 (2021-11-11)</h2>
* v0.1.3 (2021-12-15) fixes a bug where the modification date could be set
when a new zettel is created.
* 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 <code>//</code> 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
<code>>>inserted>></code>, while its previous syntax now
denotes <em>emphasized text</em>: <code>__emphasized__</code>. The
previous syntax for emphasized text is now deprecated: <code>//deprecated
emphasized//</code>. Starting with Version 0.2.0, the deprecated
syntax will not be supported. The reason is the collision with URLs that
also contain the characters <code>//</code>. The ZMK encoding of a zettel
may help with the transition
(<code>/v/{ZettelID}?_part=zettel&_enc=zmk</code>, on the Info page of
each zettel in the WebUI). Additionally, all deprecated uses of
<code>//</code> will be rendered with a dashed box within the WebUI.
(breaking: Zettelmarkup).
* API client software is now a [https://zettelstore.de/client/|separate]
project.
(breaking)
* Initial support for HTTP security headers (Content-Security-Policy,
Permissions-Policy, Referrer-Policy, X-Content-Type-Options,
X-Frame-Options). Header values are currently some constant values.
(possibly breaking: api, webui)
* Remove visual Zettelmarkup (bold, striketrough). Semantic Zettelmarkup
(strong, delete) is still allowed and replaces the visual elements
syntactically. The visual appearance should not change (depends on your
changes / additions to CSS zettel).
(possibly breaking: Zettelmarkup).
* Add API endpoint <code>POST /v</code> to retrieve HTMl and text encoded
strings from given ZettelMarkup encoded values. This will be used to
render a HTML page from a given zettel: in many cases the title of
a zettel must be treated separately.
(minor: api)
* Add API endpoint <code>/m</code> to retrieve only the metadata of
a zettel.
(minor: api)
* New metadata value <code>content-tags</code> contains the tags that were
given in the zettel content. To put it simply, <code>all-tags</code>
= <code>tags</code> + <code>content-tags</code>.
(minor)
* Calculating the context of a zettel stops at the home zettel.
(minor: api, webui)
* When renaming or deleting a zettel, a warning will be given, if other
zettel references the given zettel, or when “deleting” will
uncover zettel in overlay box.
(minor: webui)
|
| ︙ | ︙ | |||
770 771 772 773 774 775 776 |
(info)
* Many smaller bug fixes and improvements, to the software and to the
documentation.
<a id="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
| | > | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | > | | | | | | | > | > | | | > | | | > | | | | | | | | | | > | | > | | | > | > | > | | > | > | > | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 |
(info)
* Many smaller bug fixes and improvements, to the software and to the
documentation.
<a id="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 <code>/a</code> to
<code>/i</code> (login) and <code>/i?logout</code> (logout). API
authentication moves from <code>/v</code> to </code>/a</code>. JSON-based
basic zettel handling moves from <code>/z</code> to <code>/j</code> and
<code>/z/{ID}</code> to <code>/j/{ID}</code>. Since the API client is
updated too, this should not be a breaking change for most users.
(minor: api, webui; possibly breaking)
* Add API endpoint <code>/v/{ID}</code> to retrieve an evaluated zettel in
various encodings. Mostly replaces endpoint <code>/z/{ID}</code> for other
encodings except “json” and “raw”. Endpoint
<code>/j/{ID}</code> now only returns JSON data, endpoint
<code>/z/{ID}</code> is used to retrieve plain zettel data (previously
called “raw”). See documentation for details.
(major: api; breaking)
* Metadata values of type <em>tag set</em> (the metadata with key
<code>tags</code> is its most prominent example), are now compared in
a case-insensitive manner. Tags that only differ in upper / lower case
character are now treated identical. This might break your workflow, if
you depend on case-sensitive comparison of tag values. Tag values are
translated to their lower case equivalent before comparing them and when
you edit a zettel through Zettelstore. If you just modify the zettel
files, your tag values remain unchanged.
(major; breaking)
* Endpoint <code>/z/{ID}</code> allows the same methods as endpoint
<code>/j/{ID}</code>: <code>GET</code> retrieves zettel (see above),
<code>PUT</code> updates a zettel, <code>DELETE</code> deletes a zettel,
<code>MOVE</code> renames a zettel. In addtion, <code>POST /z</code> will
create a new zettel. When zettel data must be given, the format is plain
text, with metadata separated from content by an empty line. See
documentation for more details.
(major: api (plus WebUI for some details))
* Allows to transclude / expand the content of another zettel into a target
zettel when the zettel is rendered. By using the syntax of embedding an
image (which is some kind of expansion too), the first top-level paragraph
of a zettel may be transcluded into the target zettel. Endless recursion
is checked, as well as a possible “transclusion bomb ”
(similar to a XML bomb). See manual for details.
(major: zettelmarkup)
* The endpoint <code>/z</code> allows to list zettel in a simpler format
than endpoint <code>/j</code>: one line per zettel, and only zettel
identifier plus zettel title.
(minor: api)
* Folgezettel are now displayed with full title at the bottom of a page.
(minor: webui)
* Add API endpoint <code>/p/{ID}</code> to retrieve a parsed, but not
evaluated zettel in various encodings.
(minor: api)
* Fix: do not list a shadowed zettel that matches the select criteria.
(minor)
* Many smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_0_14"></a>
<h2>Changes for Version 0.0.14 (2021-07-23)</h2>
* Rename “place” into “box”. This also affects the
configuration keys to specify boxes <code>box-uri<em>X</em></code>
(previously <code>place-uri-<em>X</em></code>. Older changes documented
here are renamed too.
(breaking)
* Add API for creating, updating, renaming, and deleting zettel.
(major: api)
* Initial API client for Go.
(major: api)
* Remove support for paging of WebUI list. Runtime configuration key
<code>list-page-size</code> is removed. If you still specify it, it will
be ignored.
(major: webui)
* Use endpoint <code>/v</code> for user authentication via API. Endpoint
<code>/a</code> is now used for the web user interface only. Similar,
endpoint <code>/y</code> (“zettel context”) is renamed to
<code>/x</code>.
(minor, possibly breaking)
* Type of used-defined metadata is determined by suffix of key:
<code>-number</code>, <code>-url</code>, <code>-zid</code> will result the
values to be interpreted as a number, an URL, or a zettel identifier.
(minor, but possibly breaking if you already used a metadata key with
above suffixes, but as a string type)
* New <code>user-role</code> “creator”, which is only allowed to
create new zettel (except user zettel). This role may only read and update
public zettel or its own user zettel. Added to support future client
software (e.g. on a mobile device) that automatically creates new zettel
but, in case of a password loss, should not allow to read existing zettel.
(minor, possibly breaking, because new zettel template zettel must always
prepend the string <code>new-</code> before metdata keys that should be
transferred to the new zettel)
* New suported metadata key <code>box-number</code>, which gives an
indication from which box the zettel was loaded.
(minor)
* New supported syntax <code>html</code>.
(minor)
* New predefined zettel “User CSS” that can be used to redefine
some predefined CSS (without modifying the base CSS zettel).
(minor: webui)
* When a user moves a zettel file with additional characters into the box
directory, these characters are preserved when zettel is updated.
(bug)
* The phase “filtering a zettel list” is more precise
“selecting zettel”
(documentation)
* Many smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_0_13"></a>
<h2>Changes for Version 0.0.13 (2021-06-01)</h2>
* Startup configuration <code>box-<em>X</em>-uri</code> (where <em>X</em> is
a number greater than zero) has been renamed to
<code>box-uri-<em>X</em></code>.
(breaking)
* Web server processes startup configuration <code>url-prefix</code>. There
is no need for stripping the prefix by a front-end web server any more.
(breaking: webui, api)
* Administrator console (only optional accessible locally). Enable it only
on systems with a single user or with trusted users. It is disabled by
default.
(major: core)
* Remove visibility value “simple-expert” introduced in
[#0_0_8|version 0.0.8]. It was too complicated, esp. authorization. There
was a name collision with the “simple” directory box sub-type.
(major)
* For security reasons, HTML blocks are not encoded as HTML if they contain
certain snippets, such as <code><script</code> or
<code><iframe</code>. These may be caused by using CommonMark as
a zettel syntax.
(major)
* Full-text search can be a prefix search or a search for equal words, in
addition to the search whether a word just contains word of the search
term.
(minor: api, webui)
* Full-text search for URLs, with above additional operators.
(minor: api, webui)
* Add system zettel about license, contributors, and dependencies (and their
license).
For a nicer layout of zettel identifier, the zettel about environment
values and about runtime metrics got new zettel identifier. This affects
only user that referenced those zettel.
(minor)
* Local images that cannot be read (not found or no access rights) are
substituted with the new default image, a spinning emoji.
See [/file?name=box/constbox/emoji_spin.gif].
(minor: webui)
* Add zettelmarkup syntax for a table row that should be ignored:
<code>|%</code>. This allows to paste output of the administrator console
into a zettel.
(minor: zmk)
* Many smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_0_12"></a>
<h2>Changes for Version 0.0.12 (2021-04-16)</h2>
* Raise the per-process limit of open files on macOS to 1.048.576. This
allows most macOS users to use at least 500.000 zettel. That should be
enough for the near future.
(major)
* Mitigate the shortcomings of the macOS version by introducing types of
directory boxes. The original directory box type is now called "notify"
(the default value). There is a new type called "simple". This new type
does not notify Zettelstore when some of the underlying Zettel files
change.
(major)
* Add new startup configuration <code>default-dir-box-type</code>, which
gives the default value for specifying a directory box type. The default
value is “notify”. On macOS, the default value may be changed
“simple” if some errors occur while raising the per-process
limit of open files.
(minor)
<a id="0_0_11"></a>
<h2>Changes for Version 0.0.11 (2021-04-05)</h2>
* New box schema "file" allows to read zettel from a ZIP file.
A zettel collection can now be packaged and distributed easier.
(major: server)
* Non-restricted search is a full-text search. The search string will be
normalized according to Unicode NFKD. Every character that is not a letter
or a number will be ignored for the search. It is sufficient if the words
to be searched are part of words inside a zettel, both content and
metadata.
(major: api, webui)
* A zettel can be excluded from being indexed (and excluded from being found
in a search) if it contains the metadata <code>no-index: true</code>.
(minor: api, webui)
* Menu bar is shown when displaying error messages.
(minor: webui)
* When selecting zettel, it can be specified that a given value should
<em>not</em> match. Previously, only the whole select criteria could be
negated (which is still possible).
(minor: api, webui)
* You can select a zettel by specifying that specific metadata keys must
(or must not) be present.
(minor: api, webui)
* Context of a zettel (introduced in version 0.0.10) does not take tags into
account any more. Using some tags for determining the context resulted
into erratic, non-deterministic context lists.
(minor: api, webui)
* Selecting zettel depending on tag values can be both by comparing only the
prefix or the whole string. If a search value begins with '#', only zettel
with the exact tag will be returned. Otherwise a zettel will be returned
if the search string just matches the prefix of only one of its tags.
(minor: api, webui)
* Many smaller bug fixes and improvements, to the software and to the
documentation.
A note for users of macOS: in the current release and with macOS's default
values, a zettel directory must not contain more than approx. 250 files. There
are three options to mitigate this limitation temporarily:
# You update the per-process limit of open files on macOS.
# You setup a virtualization environment to run Zettelstore on Linux or
Windows.
# You wait for version 0.0.12 which addresses this issue.
<a id="0_0_10"></a>
<h2>Changes for Version 0.0.10 (2021-02-26)</h2>
* Menu item “Home” now redirects to a home zettel.
Its default identifier is <code>000100000000</code>. The identifier can be
changed with configuration key <code>home-zettel</code>, which supersedes
key <code>start</code>. The default home zettel contains some welcoming
information for the new user.
(major: webui)
* Show context of a zettel by following all backward and/or forward
reference up to a defined depth and list the resulting zettel.
Additionally, some zettel with similar tags as the initial zettel are also
taken into account.
(major: api, webui)
* A zettel that references other zettel within first-level list items, can
act as a “table of contents” zettel. The API endpoint
<code>/o/{ID}</code> allows to retrieve the referenced zettel in the same
order as they occur in the zettel.
(major: api)
* The zettel “New Menu” with identifier
<code>00000000090000</code> contains a list of all zettel that should act
as a template for new zettel. They are listed in the WebUIs
”New“ menu. This is an application of the previous item. It
supersedes the usage of a role <code>new-template</code> introduced in
[#0_0_6|version 0.0.6]. <b>Please update your zettel if you make use of
the now deprecated feature.</b>
(major: webui)
* A reference that starts with two slash characters
(“<code>//</code>”) it will be interpreted relative to the
value of <code>url-prefix</code>. For example, if <code>url-prefix</code>
has the value <code>/manual/</code>, the reference
<code>[[Zettel list|//h]]</code> will render as <code><a
href="/manual/h">Zettel list</a></code>.
(minor: syntax)
* Searching/selecting ignores the leading '#' character of tags.
(minor: api, webui)
* When result of selecting or searching is presented, the query is written
as the page heading.
(minor: webui)
* A reference to a zettel that contains a URL fragment, will now be
processed by the indexer.
(bug: server)
* Runtime configuration key <code>marker-external</code> now defaults to
“&#10138;” (“➚”). It is more beautiful
than the previous “&#8599;&#xfe0e;”
(“↗︎”), which also needed the additional
“&#xfe0e;” to disable the conversion to an emoji on
iPadOS.
(minor: webui)
* A pre-build binary for macOS ARM64 (also known as Apple silicon) is
available.
(minor: infrastructure)
* Many smaller bug fixes and improvements, to the software and to the
documentation.
<a id="0_0_9"></a>
<h2>Changes for Version 0.0.9 (2021-01-29)</h2>
This is the first version that is managed by [https://fossil-scm.org|Fossil]
instead of GitHub. To access older versions, use the Git repository under
[https://github.com/zettelstore/zettelstore-github|zettelstore-github].
<h3>Server / API</h3>
* (major) Support for property metadata.
Metadata key <code>published</code> is the first example of such
a property.
* (major) A background activity (called <i>indexer</i>) continuously
monitors zettel changes to establish the reverse direction of
found internal links. This affects the new metadata keys
<code>precursor</code> and <code>folge</code>. A user specifies
the precursor of a zettel and the indexer computes the property
metadata for
[https://forum.zettelkasten.de/discussion/996/definition-folgezettel|Folgezettel].
Metadata keys with type “Identifier” or
“IdentifierSet” that have no inverse key (like
<code>precursor</code> and <code>folge</code> with add to the key
<code>forward</code> that also collects all internal links within
the content. The computed inverse is <code>backward</code>, which
provides all backlinks. The key <code>back</code> is computed as
the value of <code>backward</code>, but without forward links.
Therefore, <code>back</code> is something like the list of
“smart backlinks”.
* (minor) If Zettelstore is being stopped, an appropriate message is written
in the console log.
* (minor) New computed zettel with environmental data, the list of supported
meta data keys, and statistics about all configured zettel boxes.
Some other computed zettel got a new identifier (to make room for
other variant).
* (minor) Remove zettel <code>00000000000004</code>, which contained the Go
version that produced the Zettelstore executable. It was too
specific to the current implementation. This information is now
included in zettel <code>00000000000006</code> (<i>Zettelstore
Environment Values</i>).
* (minor) Predefined templates for new zettel do not contain any value for
attribute <code>visibility</code> any more.
* (minor) Add a new metadata key type called “Zettelmarkup”.
It is a non-empty string, that will be formatted with
Zettelmarkup. <code>title</code> and <code>default-title</code>
have this type.
* (major) Rename zettel syntax “meta” to “none”.
Please update the <i>Zettelstore Runtime Configuration</i> and all
other zettel that previously used the value “meta”.
Other zettel are typically user zettel, used for authentication.
However, there is no real harm, if you do not update these zettel.
In this case, the metadata is just not presented when rendered.
Zettelstore will still work.
* (minor) Login will take at least 500 milliseconds to mitigate login
attacks. This affects both the API and the WebUI.
* (minor) Add a sort option “_random” to produce a zettel list
in random order. <code>_order</code> / <code>order</code> are now
an aliases for the query parameters <code>_sort</code>
/ <code>sort</code>.
<h3>WebUI</h3>
* (major) HTML template zettel for WebUI now use
[https://mustache.github.io/|Mustache] syntax instead of
previously used [https://golang.org/pkg/html/template/|Go
template] syntax. This allows these zettel to be used, even when
there is another Zettelstore implementation, in another
programming language. Mustache is available for approx. 48
programming languages, instead of only one for Go templates. <b>If
you modified your templates, you <i>must</i> adapt them to the new
syntax. Otherwise the WebUI will not work.</b>
* (major) Show zettel identifier of folgezettel and precursor zettel in the
header of a rendered zettel. If a zettel has real backlinks, they
are shown at the botton of the page (“Additional links to
this zettel”).
* (minor) All property metadata, even computed metadata is shown in the info
page of a zettel.
* (minor) Rendering of metadata keys <code>title</code> and
<code>default-title</code> in info page changed to a full HTML
output for these Zettelmarkup encoded values.
* (minor) Always show the zettel identifier on the zettel detail view.
Previously, the identifier was not shown if the zettel was not
editable.
* (minor) Do not show computed metadata in edit forms anymore.
<a id="0_0_8"></a>
<h2>Changes for Version 0.0.8 (2020-12-23)</h2>
<h3>Server / API</h3>
* (bug) Zettel files with extension <code>.jpg</code> and without metadata
will get a <code>syntax</code> value “jpg”. The internal
data structure got the same value internally, instead of
“jpeg”. This has been fixed for all possible alternative
syntax values.
* (bug) If a file, e.g. an image file like <code>20201130190200.jpg</code>,
is added to the directory box, its metadata are just calculated from
the information available. Updated metadata did not find its way
into the zettel box, because the <code>.meta</code> file was not
written.
* (bug) If just the <code>.meta</code> file was deleted manually, the zettel
was assumed to be missing. A workaround is to restart the software.
If the <code>.meta</code> file is deleted, metadata is now
calculated in the same way when the <code>.meta</code> file is
non-existing at the start of the software.
* (bug) A link to the current zettel, only using a fragment (e.g.
<code>[[Title|#title]]</code>) is now handled correctly as
a zettel link (and not as a link to external material).
* (minor) Allow zettel to be marked as “read only”.
This is done through the metadata key <code>read-only</code>.
* (bug) When renaming a zettel, check all boxes for the new zettel
identifier, not just the first one. Otherwise it will be possible to
shadow a read-only zettel from a next box, effectively modifying it.
* (minor) Add support for a configurable default value for metadata key
<code>visibility</code>.
* (bug) If <code>list-page-size</code> is set to a relatively small value
and the authenticated user is <i>not</i> the owner, some zettel were
not shown in the list of zettel or were not returned by the API.
* (minor) Add support for new visibility “expert”.
An owner becomes an expert, if the runtime configuration key
<code>expert-mode</code> is set to true.
* (major) Add support for computed zettel.
These zettel have an identifier less than
<code>0000000000100</code>. Most of them are only visible, if
<code>expert-mode</code> is enabled.
* (bug) Fixes a memory leak that results in too many open files after
approx. 125 reload operations.
* (major) Predefined templates for new zettel got an explicit value for
visibility: “login”. Please update these zettel if you
modified them.
* (major) Rename key <code>readonly</code> of <i>Zettelstore Startup
Configuration</i> to <code>read-only-mode</code>. This was done to
avoid some confusion with the the zettel metadata key
<code>read-only</code>. <b>Please adapt your startup configuration.
Otherwise your Zettelstore will be accidentally writable.</b>
* (minor) References starting with “./” and “../”
are treated as a local reference. Previously, only the prefix
“/” was treated as a local reference.
* (major) Metadata key <code>modified</code> will be set automatically to
the current local time if a zettel is updated through Zettelstore.
<b>If you used that key previously for your own, you should rename
it before you upgrade.</b>
* (minor) The new visibility value “simple-expert” ensures that
many computed zettel are shown for new users. This is to enable
them to send useful bug reports.
* (minor) When a zettel is stored as a file, its identifier is additionally
stored within the metadata. This helps for better robustness in
case the file names were corrupted. In addition, there could be
a tool that compares the identifier with the file name.
<h3>WebUI</h3>
* (minor) Remove list of tags in “List Zettel” and search
results. There was some feedback that the additional tags were not
helpful.
* (minor) Move zettel field "role" above "tags" and move "syntax" more to
"content".
* (minor) Rename zettel operation “clone” to “copy”.
* (major) All predefined HTML templates have now a visibility value
“expert”. If you want to see them as an non-expert
owner, you must temporary enable <code>expert-mode</code> and
change the <code>visibility</code> metadata value.
* (minor) Initial support for
[https://zettelkasten.de/posts/tags/folgezettel/|Folgezettel]. If
you click on “Folge” (detail view or info view), a new
zettel is created with a reference (<code>precursor</code>) to the
original zettel. Title, role, tags, and syntax are copied from the
original zettel.
* (major) Most predefined zettel have a title prefix of
“Zettelstore”.
* (minor) If started in simple mode, e.g. via double click or without any
command, some information for the new user is presented. In the
terminal, there is a hint about opening the web browser and use
|
| ︙ | ︙ | |||
1199 1200 1201 1202 1203 1204 1205 |
to stay with the old licenses (AGPLv3+, CC BY-SA 4.0), you are free to
fork from the previous version.
<a id="0_0_6"></a>
<h2>Changes for Version 0.0.6 (2020-11-23)</h2>
<h3>Server</h3>
* (major) Rename identifier of <i>Zettelstore Runtime Configuration</i> to
| | > | | | | | | | | | | 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 |
to stay with the old licenses (AGPLv3+, CC BY-SA 4.0), you are free to
fork from the previous version.
<a id="0_0_6"></a>
<h2>Changes for Version 0.0.6 (2020-11-23)</h2>
<h3>Server</h3>
* (major) Rename identifier of <i>Zettelstore Runtime Configuration</i> to
<code>00000000000100</code> (previously
<code>00000000000001</code>). This is done to gain some free
identifier with smaller number to be used internally. <b>If you
customized this zettel, please make sure to rename it to the new
identifier.</b>
* (major) Rename the two essential metadata keys of a user zettel to
<code>credential</code> and <code>user-id</code>. The previous
values were <code>cred</code> and <code>ident</code>. <b>If you
enabled user authentication and added some user zettel, make sure
to change them accordingly. Otherwise these users will not
authenticated any more.</b>
* (minor) Rename the scheme of the box URL where predefined zettel are
stored to “const”. The previous value was
“globals”.
<h3>Zettelmarkup</h3>
* (bug) Allow to specify a <i>fragment</i> in a reference to a zettel.
Used to link to an internal position within a zettel.
This applies to CommonMark too.
<h3>API</h3>
* (bug) Encoding binary content in format “json” now results
in valid JSON content.
* (bug) All query parameters of selecting zettel must be true, regardless
if a specific key occurs more than one or not.
* (minor) Encode all inherited meta values in all formats except
“raw”. A meta value is called <i>inherited</i> if
there is a key starting with <code>default-</code> in the
<i>Zettelstore Runtime Configuration</i>. Applies to WebUI also.
* (minor) Automatic calculated identifier for headings (only for
“html”, “djson”, “native”
format and for the Web user interface). You can use this to
provide a zettel reference that links to the heading, without
specifying an explicit mark (<code>[!mark]</code>).
* (major) Allow to retrieve all references of a given zettel.
|
| ︙ | ︙ | |||
1250 1251 1252 1253 1254 1255 1256 |
contrast to “zettel references” and “external
references”). When a local reference is displayed as an URL
on the WebUI, it will not opened in a new window/tab. They will
receive a <i>local</i> marker, when encoded as “djson”
or “native”. Local references are listed on the
<i>Info page</i> of each zettel.
* (minor) Change the default value for some visual sugar put after an
| | | | > | | | | | | | | | > | | | | | > | 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 |
contrast to “zettel references” and “external
references”). When a local reference is displayed as an URL
on the WebUI, it will not opened in a new window/tab. They will
receive a <i>local</i> marker, when encoded as “djson”
or “native”. Local references are listed on the
<i>Info page</i> of each zettel.
* (minor) Change the default value for some visual sugar put after an
external URL to <code>&\#8599;&\#xfe0e;</code>
(“↗︎”). This affects the former key
<code>icon-material</code> of the <i>Zettelstore Runtime
Configuration</i>, which is renamed to
<code>marker-external</code>.
* (major) Allow multiple zettel to act as templates for creating new zettel.
All zettel with a role value “new-template” act as
a template to create a new zettel. The WebUI menu item
“New” changed to a drop-down list with all those
zettel, ordered by their identifier. All metadata keys with the
prefix <code>new-</code> will be translated to a new or updated
keys/value without that prefix. You can use this mechanism to
specify a role for the new zettel, or a different title. The title
of the template zettel is used in the drop-down list. The initial
template zettel “New Zettel” has now a different
zettel identifier (now: <code>00000000091001</code>, was:
<code>00000000040001</code>). <b>Please update it, if you changed
that zettel.</b>
<br>Note: this feature was superseded in [#0_0_10|version 0.0.10]
by the “New Menu” zettel.
* (minor) When a page should be opened in a new windows (e.g. for external
references), the web browser is instructed to decouple the new
page from the previous one for privacy and security reasons. In
detail, the web browser is instructed to omit referrer information
and to omit a JS object linking to the page that contained the
external link.
* (minor) If the value of the <i>Zettelstore Runtime Configuration</i> key
<code>list-page-size</code> is greater than zero, the number of
WebUI list elements will be restricted and it is possible to
change to the next/previous page to list more elements.
* (minor) Change CSS to enhance reading: make <code>line-height</code>
a little smaller (previous: 1.6, now 1.4) and move list items to
the left.
<a id="0_0_5"></a>
<h2>Changes for Version 0.0.5 (2020-10-22)</h2>
* Application Programming Interface (API) to allow external software to
retrieve zettel data from the Zettelstore.
* Specify boxes, where zettel are stored, via an URL.
* Add support for a custom footer.
<a id="0_0_4"></a>
<h2>Changes for Version 0.0.4 (2020-09-11)</h2>
* Optional user authentication/authorization.
* New sub-commands <code>file</code> (use Zettelstore as a command line
filter), <code>password</code> (for authentication), and
<code>config</code>.
<a id="0_0_3"></a>
<h2>Changes for Version 0.0.3 (2020-08-31)</h2>
* Starting Zettelstore has been changed by introducing sub-commands.
This change is also reflected on the server installation procedures.
* Limitations on renaming zettel has been relaxed.
<a id="0_0_2"></a>
<h2>Changes for Version 0.0.2 (2020-08-28)</h2>
* Configuration zettel now has ID <code>00000000000001</code> (previously:
<code>00000000000000</code>).
* The zettel with ID <code>00000000000000</code> is no longer shown in any
zettel list. If you changed the configuration zettel, you should rename it
manually in its file directory.
* Creating a new zettel is now done by cloning an existing zettel.
To mimic the previous behaviour, a zettel with ID
<code>00000000040001</code> is introduced. You can change it if you need
a different template zettel.
<a id="0_0_1"></a>
<h2>Changes for Version 0.0.1 (2020-08-21)</h2>
* Initial public release.
|
Changes to www/download.wiki.
1 2 3 4 5 6 7 8 9 10 11 | <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> | | | | | | | | | 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.17.0</code> (2024-03-04). * [/uv/zettelstore-0.17.0-linux-amd64.zip|Linux] (amd64) * [/uv/zettelstore-0.17.0-linux-arm.zip|Linux] (arm6, e.g. Raspberry Pi) * [/uv/zettelstore-0.17.0-darwin-arm64.zip|macOS] (arm64) * [/uv/zettelstore-0.17.0-darwin-amd64.zip|macOS] (amd64) * [/uv/zettelstore-0.17.0-windows-amd64.zip|Windows] (amd64) 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.17.0.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.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 |
software, which often connects to Zettelstore via its API. Some of the
software packages may be experimental.
* [https://zettelstore.de/sx|Sx] provides an evaluator for symbolic
expressions, which is unsed for HTML templates and more.
[https://mastodon.social/tags/Zettelstore|Stay tuned] …
<hr>
| | | | | | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
software, which often connects to Zettelstore via its API. Some of the
software packages may be experimental.
* [https://zettelstore.de/sx|Sx] provides an evaluator for symbolic
expressions, which is unsed for HTML templates and more.
[https://mastodon.social/tags/Zettelstore|Stay tuned] …
<hr>
<h3>Latest Release: 0.17.0 (2024-03-04)</h3>
* [./download.wiki|Download]
* [./changes.wiki#0_17|Change summary]
* [/timeline?p=v0.17.0&bt=v0.16.0&y=ci|Check-ins for version 0.17.0],
[/vdiff?to=v0.17.0&from=v0.16.0|content diff]
* [/timeline?df=v0.17.0&y=ci|Check-ins derived from the 0.17.0 release],
[/vdiff?from=v0.17.0&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://go.dev/dl/|Go] and some Go-based tools. Please read
the [./build.md|instructions] for details.
|
| ︙ | ︙ |
Changes to zettel/content.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 zettel import ( "bytes" "encoding/base64" "errors" "io" "unicode" "unicode/utf8" | > > > | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package zettel
import (
"bytes"
"encoding/base64"
"errors"
"io"
"unicode"
"unicode/utf8"
"zettelstore.de/client.fossil/input"
)
// Content is just the content of a zettel.
type Content struct {
data []byte
isBinary bool
}
|
| ︙ | ︙ | |||
109 110 111 112 113 114 115 |
}
// IsBinary returns true if the given data appears to be non-text data.
func IsBinary(data []byte) bool {
if !utf8.Valid(data) {
return true
}
| | < | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
}
// IsBinary returns true if the given data appears to be non-text data.
func IsBinary(data []byte) bool {
if !utf8.Valid(data) {
return true
}
for i := range len(data) {
if data[i] == 0 {
return true
}
}
return false
}
|
Changes to zettel/content_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 zettel_test import ( "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package zettel_test import ( "testing" |
| ︙ | ︙ |
Changes to zettel/id/digraph.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 id import ( "maps" "slices" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2023-present Detlef Stern //----------------------------------------------------------------------------- package id import ( "maps" "slices" |
| ︙ | ︙ |
Changes to zettel/id/digraph_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 id_test import ( "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2023-present Detlef Stern //----------------------------------------------------------------------------- package id_test import ( "testing" |
| ︙ | ︙ |
Changes to zettel/id/edge.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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 id import "slices" // Edge is a pair of to vertices. | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2023-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2023-present Detlef Stern //----------------------------------------------------------------------------- package id import "slices" // Edge is a pair of to vertices. |
| ︙ | ︙ |
Changes to zettel/id/id.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 id provides zettel specific types, constants, and functions about // zettel identifier. package id import ( | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package id provides zettel specific types, constants, and functions about // zettel identifier. package id import ( |
| ︙ | ︙ | |||
96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
// 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[:]
}
| > > > | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
// Only defined for valid ids.
func (zid Zid) String() string {
var result [14]byte
zid.toByteArray(&result)
return string(result[:])
}
// ZettelID return the zettel identification as a api.ZettelID.
func (zid Zid) ZettelID() api.ZettelID { return api.ZettelID(zid.String()) }
// 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[:]
}
|
| ︙ | ︙ |
Changes to zettel/id/id_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 id_test provides unit tests for testing zettel id specific functions. package id_test import ( "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package id_test provides unit tests for testing zettel id specific functions. package id_test import ( "testing" |
| ︙ | ︙ | |||
68 69 70 71 72 73 74 |
}
}
var sResult string // to disable compiler optimization in loop below
func BenchmarkString(b *testing.B) {
var s string
| | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
}
}
var sResult string // to disable compiler optimization in loop below
func BenchmarkString(b *testing.B) {
var s string
for range b.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 range b.N {
bs = id.Zid(12345678901200).Bytes()
}
bResult = bs
}
|
Changes to zettel/id/set.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 id import ( "maps" "strings" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package id import ( "maps" "strings" |
| ︙ | ︙ |
Changes to zettel/id/set_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 id_test import ( "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package id_test import ( "testing" |
| ︙ | ︙ | |||
133 134 135 136 137 138 139 |
t.Errorf("%d: %v.Remove(%v) should be %v, but got %v", i, sl1, sl2, tc.exp, got)
}
}
}
// func BenchmarkSet(b *testing.B) {
// s := id.Set{}
| | | | 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
t.Errorf("%d: %v.Remove(%v) should be %v, but got %v", i, sl1, sl2, tc.exp, got)
}
}
}
// func BenchmarkSet(b *testing.B) {
// s := id.Set{}
// for range b.N {
// s[id.Zid(i)] = true
// }
// }
func BenchmarkSet(b *testing.B) {
s := id.Set{}
for i := range b.N {
s[id.Zid(i)] = struct{}{}
}
}
|
Changes to zettel/id/slice.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 id import ( "slices" "strings" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package id import ( "slices" "strings" |
| ︙ | ︙ |
Changes to zettel/id/slice_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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 id_test import ( "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2021-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2021-present Detlef Stern //----------------------------------------------------------------------------- package id_test import ( "testing" |
| ︙ | ︙ |
Changes to zettel/meta/collection.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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 meta import "sort" // Arrangement stores metadata within its categories. | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2022-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2022-present Detlef Stern //----------------------------------------------------------------------------- package meta import "sort" // Arrangement stores metadata within its categories. |
| ︙ | ︙ |
Changes to zettel/meta/meta.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-present 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 meta provides the zettel specific type 'meta'. package meta import ( "regexp" "sort" "strings" "unicode" "unicode/utf8" "zettelstore.de/client.fossil/api" | > > > | | | 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 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package meta provides the zettel specific type 'meta'. package meta import ( "regexp" "sort" "strings" "unicode" "unicode/utf8" "zettelstore.de/client.fossil/api" "zettelstore.de/client.fossil/input" "zettelstore.de/client.fossil/maps" "zettelstore.de/z/strfun" "zettelstore.de/z/zettel/id" ) type keyUsage int const ( |
| ︙ | ︙ | |||
44 45 46 47 48 49 50 |
// IsComputed returns true, if metadata is computed and not set by the user.
func (kd *DescriptionKey) IsComputed() bool { return kd.usage >= usageComputed }
// IsProperty returns true, if metadata is a computed property.
func (kd *DescriptionKey) IsProperty() bool { return kd.usage >= usageProperty }
| < < < | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
// IsComputed returns true, if metadata is computed and not set by the user.
func (kd *DescriptionKey) IsComputed() bool { return kd.usage >= usageComputed }
// IsProperty returns true, if metadata is a computed property.
func (kd *DescriptionKey) IsProperty() bool { return kd.usage >= usageProperty }
var registeredKeys = make(map[string]*DescriptionKey)
func registerKey(name string, t *DescriptionType, usage keyUsage, inverse string) {
if _, ok := registeredKeys[name]; ok {
panic("Key '" + name + "' already defined")
}
if inverse != "" {
|
| ︙ | ︙ |
Changes to zettel/meta/meta_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 meta import ( "strings" "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package meta import ( "strings" "testing" |
| ︙ | ︙ | |||
227 228 229 230 231 232 233 |
if m2.Equal(m1, true) {
t.Error("Different ID should differentiate")
}
}
func pairs2meta(pairs []string) *Meta {
m := New(testID)
| | | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
if m2.Equal(m1, true) {
t.Error("Different ID should differentiate")
}
}
func pairs2meta(pairs []string) *Meta {
m := New(testID)
for i := 0; i < len(pairs); i += 2 {
m.Set(pairs[i], pairs[i+1])
}
return m
}
func TestRemoveNonGraphic(t *testing.T) {
testCases := []struct {
|
| ︙ | ︙ |
Changes to zettel/meta/parse.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 meta import ( "strings" "zettelstore.de/client.fossil/api" | > > > | | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package meta
import (
"strings"
"zettelstore.de/client.fossil/api"
"zettelstore.de/client.fossil/input"
"zettelstore.de/client.fossil/maps"
"zettelstore.de/z/strfun"
"zettelstore.de/z/zettel/id"
)
// NewFromInput parses the meta data of a zettel.
func NewFromInput(zid id.Zid, inp *input.Input) *Meta {
if inp.Ch == '-' && inp.PeekN(0) == '-' && inp.PeekN(1) == '-' {
|
| ︙ | ︙ | |||
152 153 154 155 156 157 158 |
}
switch Type(key) {
case TypeTagSet:
addSet(m, key, strings.ToLower(v), func(s string) bool { return s[0] == '#' && len(s) > 1 })
case TypeWord:
m.Set(key, strings.ToLower(v))
| < < | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
}
switch Type(key) {
case TypeTagSet:
addSet(m, key, strings.ToLower(v), func(s string) bool { return s[0] == '#' && len(s) > 1 })
case TypeWord:
m.Set(key, strings.ToLower(v))
case TypeID:
if _, err := id.Parse(v); err == nil {
m.Set(key, v)
}
case TypeIDSet:
addSet(m, key, v, func(s string) bool {
_, err := id.Parse(s)
|
| ︙ | ︙ |
Changes to zettel/meta/parse_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 meta_test import ( "strings" "testing" "zettelstore.de/client.fossil/api" | > > > | | 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 |
//-----------------------------------------------------------------------------
// Copyright (c) 2020-present 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.
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------
package meta_test
import (
"strings"
"testing"
"zettelstore.de/client.fossil/api"
"zettelstore.de/client.fossil/input"
"zettelstore.de/z/zettel/meta"
)
func parseMetaStr(src string) *meta.Meta {
return meta.NewFromInput(testID, input.NewInput([]byte(src)))
}
|
| ︙ | ︙ | |||
133 134 135 136 137 138 139 |
}
}
func equalPairs(one, two []meta.Pair) bool {
if len(one) != len(two) {
return false
}
| | | 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
}
}
func equalPairs(one, two []meta.Pair) bool {
if len(one) != len(two) {
return false
}
for i := range len(one) {
if one[i].Key != two[i].Key || one[i].Value != two[i].Value {
return false
}
}
return true
}
|
| ︙ | ︙ |
Changes to zettel/meta/type.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 meta import ( "strconv" "strings" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package meta import ( "strconv" "strings" |
| ︙ | ︙ | |||
48 49 50 51 52 53 54 | TypeIDSet = registerType(api.MetaIDSet, true) TypeNumber = registerType(api.MetaNumber, false) TypeString = registerType(api.MetaString, false) TypeTagSet = registerType(api.MetaTagSet, true) TypeTimestamp = registerType(api.MetaTimestamp, false) TypeURL = registerType(api.MetaURL, false) TypeWord = registerType(api.MetaWord, false) | < > > > > > > | | | < | | | | | | | 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 |
TypeIDSet = registerType(api.MetaIDSet, true)
TypeNumber = registerType(api.MetaNumber, false)
TypeString = registerType(api.MetaString, false)
TypeTagSet = registerType(api.MetaTagSet, true)
TypeTimestamp = registerType(api.MetaTimestamp, false)
TypeURL = registerType(api.MetaURL, false)
TypeWord = registerType(api.MetaWord, false)
TypeZettelmarkup = registerType(api.MetaZettelmarkup, false)
)
// Type returns a type hint for the given key. If no type hint is specified,
// TypeUnknown is returned.
func (*Meta) Type(key string) *DescriptionType {
return Type(key)
}
// Some constants for key suffixes that determine a type.
const (
SuffixKeyRole = "-role"
SuffixKeyURL = "-url"
)
var (
cachedTypedKeys = make(map[string]*DescriptionType)
mxTypedKey sync.RWMutex
suffixTypes = map[string]*DescriptionType{
"-date": TypeTimestamp,
"-number": TypeNumber,
SuffixKeyRole: TypeWord,
"-time": TypeTimestamp,
"-title": TypeZettelmarkup,
SuffixKeyURL: TypeURL,
"-zettel": TypeID,
"-zid": TypeID,
"-zids": TypeIDSet,
}
)
// Type returns a type hint for the given key. If no type hint is specified,
// TypeEmpty is returned.
func Type(key string) *DescriptionType {
if k, ok := registeredKeys[key]; ok {
|
| ︙ | ︙ |
Changes to zettel/meta/type_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 meta_test import ( "strconv" "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package meta_test import ( "strconv" "testing" |
| ︙ | ︙ |
Changes to zettel/meta/values.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 meta import ( "fmt" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package meta import ( "fmt" |
| ︙ | ︙ | |||
31 32 33 34 35 36 37 38 39 40 41 42 43 44 | SyntaxPNG = "png" SyntaxSVG = api.ValueSyntaxSVG SyntaxSxn = api.ValueSyntaxSxn SyntaxText = api.ValueSyntaxText SyntaxTxt = "txt" SyntaxWebp = "webp" SyntaxZmk = api.ValueSyntaxZmk ) // Visibility enumerates the variations of the 'visibility' meta key. type Visibility int // Supported values for visibility. const ( | > > | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | SyntaxPNG = "png" SyntaxSVG = api.ValueSyntaxSVG SyntaxSxn = api.ValueSyntaxSxn SyntaxText = api.ValueSyntaxText SyntaxTxt = "txt" SyntaxWebp = "webp" SyntaxZmk = api.ValueSyntaxZmk DefaultSyntax = SyntaxPlain ) // Visibility enumerates the variations of the 'visibility' meta key. type Visibility int // Supported values for visibility. const ( |
| ︙ | ︙ |
Changes to zettel/meta/write.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 meta import "io" // Write writes metadata to a writer, excluding computed and propery values. | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package meta import "io" // Write writes metadata to a writer, excluding computed and propery values. |
| ︙ | ︙ |
Changes to zettel/meta/write_test.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 meta_test import ( "strings" "testing" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- package meta_test import ( "strings" "testing" |
| ︙ | ︙ |
Changes to zettel/zettel.go.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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 zettel provides specific types, constants, and functions for zettel. package zettel import "zettelstore.de/z/zettel/meta" | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //----------------------------------------------------------------------------- // Copyright (c) 2020-present 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. // // SPDX-License-Identifier: EUPL-1.2 // SPDX-FileCopyrightText: 2020-present Detlef Stern //----------------------------------------------------------------------------- // Package zettel provides specific types, constants, and functions for zettel. package zettel import "zettelstore.de/z/zettel/meta" |
| ︙ | ︙ |