Index: VERSION ================================================================== --- VERSION +++ VERSION @@ -1,1 +1,1 @@ -0.8.0 +0.7.0 Index: ast/inline.go ================================================================== --- ast/inline.go +++ ast/inline.go @@ -52,10 +52,22 @@ func (*TextNode) inlineNode() { /* Just a marker */ } // WalkChildren does nothing. func (*TextNode) WalkChildren(Visitor) { /* No children*/ } + +// -------------------------------------------------------------------------- + +// TagNode contains a tag. +type TagNode struct { + Tag string // The text itself. +} + +func (*TagNode) inlineNode() { /* Just a marker */ } + +// WalkChildren does nothing. +func (*TagNode) WalkChildren(Visitor) { /* No children*/ } // -------------------------------------------------------------------------- // SpaceNode tracks inter-word space characters. type SpaceNode struct { Index: box/constbox/base.css ================================================================== --- box/constbox/base.css +++ box/constbox/base.css @@ -182,10 +182,11 @@ border: 1px solid black; border-radius: .25rem; padding: .1rem .2rem; font-size: 95%; } + .zs-example { border-style: dotted !important } .zs-info { background-color: lightblue; padding: .5rem 1rem; } .zs-warning { Index: box/constbox/base.mustache ================================================================== --- box/constbox/base.mustache +++ box/constbox/base.mustache @@ -41,20 +41,20 @@ {{#CanRefresh}} Refresh {{/CanRefresh}} -{{#NewZettelLinks.Has}} +{{#HasNewZettelLinks}}
-{{/NewZettelLinks.Has}} +{{/HasNewZettelLinks}}
Index: box/constbox/constbox.go ================================================================== --- box/constbox/constbox.go +++ box/constbox/constbox.go @@ -184,11 +184,11 @@ api.KeySyntax: api.ValueSyntaxZmk, api.KeyLang: api.ValueLangEN, api.KeyReadOnly: api.ValueTrue, api.KeyVisibility: api.ValueVisibilityLogin, api.KeyCreated: "20210504135842", - api.KeyModified: "20221013105100", + api.KeyModified: "20220824161200", }, domain.NewContent(contentDependencies)}, id.BaseTemplateZid: { constHeader{ api.KeyTitle: "Zettelstore Base HTML Template", Index: box/constbox/delete.mustache ================================================================== --- box/constbox/delete.mustache +++ box/constbox/delete.mustache @@ -7,21 +7,21 @@

Infomation

If you delete this zettel, the previoulsy shadowed zettel from overlayed box {{ShadowedBox}} becomes available.

{{/HasShadows}} -{{#Incoming.Has}} +{{#HasIncoming}}

Warning!

If you delete this zettel, incoming references from the following zettel will become invalid.

-{{/Incoming.Has}} +{{/HasIncoming}} {{#HasUselessFiles}}

Warning!

Deleting this zettel will also delete the following files, so that they will not be interpreted as content for this zettel.

{{/HasLocLinks}} -{{#QueryLinks.Has}} +{{#HasQueryLinks}}

Queries

-{{/QueryLinks.Has}} +{{/HasQueryLinks}} {{#HasExtLinks}}

External

+ + +{{/HasBackLinks}} Index: box/manager/collect.go ================================================================== --- box/manager/collect.go +++ box/manager/collect.go @@ -21,16 +21,18 @@ type collectData struct { refs id.Set words store.WordSet urls store.WordSet + itags store.WordSet } func (data *collectData) initialize() { data.refs = id.NewSet() data.words = store.NewWordSet() data.urls = store.NewWordSet() + data.itags = store.NewWordSet() } func collectZettelIndexData(zn *ast.ZettelNode, data *collectData) { ast.Walk(data, &zn.Ast) } @@ -45,10 +47,13 @@ data.addText(string(n.Content)) case *ast.TranscludeNode: data.addRef(n.Ref) case *ast.TextNode: data.addText(n.Text) + case *ast.TagNode: + data.addText(n.Tag) + data.itags.Add("#" + strings.ToLower(n.Tag)) case *ast.LinkNode: data.addRef(n.Ref) case *ast.EmbedRefNode: data.addRef(n.Ref) case *ast.CiteNode: Index: box/manager/indexer.go ================================================================== --- box/manager/indexer.go +++ box/manager/indexer.go @@ -206,10 +206,11 @@ zi.AddDeadRef(ref) } } zi.SetWords(cData.words) zi.SetUrls(cData.urls) + zi.SetITags(cData.itags) } func (mgr *Manager) idxUpdateValue(ctx context.Context, inverseKey, value string, zi *store.ZettelIndex) { zid, err := id.Parse(value) if err != nil { Index: box/manager/memstore/memstore.go ================================================================== --- box/manager/memstore/memstore.go +++ box/manager/memstore/memstore.go @@ -36,10 +36,11 @@ forward id.Slice backward id.Slice meta map[string]metaRefs words []string urls []string + itags string // Inline tags } func (zi *zettelIndex) isEmpty() bool { if len(zi.forward) > 0 || len(zi.backward) > 0 || len(zi.dead) > 0 || len(zi.words) > 0 { return false @@ -109,10 +110,22 @@ } if len(back) > 0 { m.Set(api.KeyBack, back.String()) updated = true } + if itags := zi.itags; itags != "" { + m.Set(api.KeyContentTags, itags) + if tags, ok2 := m.Get(api.KeyTags); ok2 { + m.Set(api.KeyAllTags, tags+" "+itags) + } else { + m.Set(api.KeyAllTags, itags) + } + updated = true + } else if tags, ok2 := m.Get(api.KeyTags); ok2 { + m.Set(api.KeyAllTags, tags) + 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. @@ -146,24 +159,19 @@ defer ms.mx.RUnlock() result := ms.selectWithPred(prefix, strings.HasPrefix) l := len(prefix) if l > 14 { return result + } + minZid, err := id.Parse(prefix + "00000000000000"[:13-l] + "1") + if err != nil { + 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) } } @@ -281,10 +289,11 @@ ms.updateDeadReferences(zidx, zi) ms.updateForwardBackwardReferences(zidx, zi) ms.updateMetadataReferences(zidx, zi) zi.words = updateWordSet(zidx.Zid, ms.words, zi.words, zidx.GetWords()) zi.urls = updateWordSet(zidx.Zid, ms.urls, zi.urls, zidx.GetUrls()) + zi.itags = setITags(zidx.GetITags()) // Check if zi must be inserted into ms.idx if !ziExist && !zi.isEmpty() { ms.idx[zidx.Zid] = zi } @@ -373,10 +382,19 @@ } srefs[word] = refs2 } return next.Words() } + +func setITags(next store.WordSet) string { + itags := next.Words() + if len(itags) == 0 { + return "" + } + sort.Strings(itags) + return strings.Join(itags, " ") +} func (ms *memStore) getEntry(zid id.Zid) *zettelIndex { // Must only be called if ms.mx is write-locked! if zi, ok := ms.idx[zid]; ok { return zi Index: box/manager/store/zettel.go ================================================================== --- box/manager/store/zettel.go +++ box/manager/store/zettel.go @@ -18,10 +18,11 @@ backrefs id.Set // set of back references metarefs map[string]id.Set // references to inverse keys deadrefs id.Set // set of dead references words WordSet urls WordSet + itags WordSet } // NewZettelIndex creates a new zettel index. func NewZettelIndex(zid id.Zid) *ZettelIndex { return &ZettelIndex{ @@ -57,10 +58,13 @@ func (zi *ZettelIndex) SetWords(words WordSet) { zi.words = words } // SetUrls sets the words to the given value. func (zi *ZettelIndex) SetUrls(urls WordSet) { zi.urls = urls } +// SetITags sets the words to the given value. +func (zi *ZettelIndex) SetITags(itags WordSet) { zi.itags = itags } + // GetDeadRefs returns all dead references as a sorted list. func (zi *ZettelIndex) GetDeadRefs() id.Slice { return zi.deadrefs.Sorted() } @@ -84,5 +88,8 @@ // GetWords returns a reference to the set of words. It must not be modified. func (zi *ZettelIndex) GetWords() WordSet { return zi.words } // GetUrls returns a reference to the set of URLs. It must not be modified. func (zi *ZettelIndex) GetUrls() WordSet { return zi.urls } + +// GetITags returns a reference to the set of internal tags. It must not be modified. +func (zi *ZettelIndex) GetITags() WordSet { return zi.itags } Index: box/notify/fsdir.go ================================================================== --- box/notify/fsdir.go +++ box/notify/fsdir.go @@ -99,34 +99,28 @@ } func (fsdn *fsdirNotifier) readAndProcessEvent() bool { select { case <-fsdn.done: - fsdn.log.Trace().Int("i", 1).Msg("done with read and process events") return false default: } select { case <-fsdn.done: - fsdn.log.Trace().Int("i", 2).Msg("done with read and process events") return false case <-fsdn.refresh: - fsdn.log.Trace().Msg("refresh") listDirElements(fsdn.log, fsdn.fetcher, fsdn.events, fsdn.done) case err, ok := <-fsdn.base.Errors: - fsdn.log.Trace().Err(err).Bool("ok", ok).Msg("got errors") if !ok { return false } select { case fsdn.events <- Event{Op: Error, Err: err}: case <-fsdn.done: - fsdn.log.Trace().Int("i", 3).Msg("done with read and process events") return false } case ev, ok := <-fsdn.base.Events: - fsdn.log.Trace().Str("name", ev.Name).Str("op", ev.Op.String()).Bool("ok", ok).Msg("file event") if !ok { return false } if !fsdn.processEvent(&ev) { return false @@ -140,88 +134,67 @@ if len(ev.Name) == len(fsdn.path) { return fsdn.processDirEvent(ev) } return fsdn.processFileEvent(ev) } - fsdn.log.Trace().Str("path", fsdn.path).Str("name", ev.Name).Str("op", ev.Op.String()).Msg("event does not match") return true } func (fsdn *fsdirNotifier) processDirEvent(ev *fsnotify.Event) bool { - if ev.Has(fsnotify.Remove) || ev.Has(fsnotify.Rename) { + const deleteFsDirOps = fsnotify.Remove | fsnotify.Rename + + if ev.Op&deleteFsDirOps != 0 { fsdn.log.Debug().Str("name", fsdn.path).Msg("Directory removed") fsdn.base.Remove(fsdn.path) select { case fsdn.events <- Event{Op: Destroy}: case <-fsdn.done: - fsdn.log.Trace().Int("i", 1).Msg("done dir event processing") return false } - return true - } - - if ev.Has(fsnotify.Create) { + } else if ev.Op&fsnotify.Create != 0 { err := fsdn.base.Add(fsdn.path) if err != nil { fsdn.log.IfErr(err).Str("name", fsdn.path).Msg("Unable to add directory") select { case fsdn.events <- Event{Op: Error, Err: err}: case <-fsdn.done: - fsdn.log.Trace().Int("i", 2).Msg("done dir event processing") return false } } fsdn.log.Debug().Str("name", fsdn.path).Msg("Directory added") return listDirElements(fsdn.log, fsdn.fetcher, fsdn.events, fsdn.done) + } else { + fsdn.log.Trace().Str("name", ev.Name).Str("op", ev.Op.String()).Msg("Directory processed") } - - fsdn.log.Trace().Str("name", ev.Name).Str("op", ev.Op.String()).Msg("Directory processed") return true } func (fsdn *fsdirNotifier) processFileEvent(ev *fsnotify.Event) bool { - if ev.Has(fsnotify.Create) || ev.Has(fsnotify.Write) { - if fi, err := os.Lstat(ev.Name); err != nil || !fi.Mode().IsRegular() { - regular := err == nil && fi.Mode().IsRegular() - fsdn.log.Trace().Str("name", ev.Name).Str("op", ev.Op.String()).Err(err).Bool("regular", regular).Msg("error with file") - return true - } - fsdn.log.Trace().Str("name", ev.Name).Str("op", ev.Op.String()).Msg("File updated") - return fsdn.sendEvent(Update, filepath.Base(ev.Name)) - } - - if ev.Has(fsnotify.Rename) { - fi, err := os.Lstat(ev.Name) - if err != nil { - fsdn.log.Trace().Str("name", ev.Name).Str("op", ev.Op.String()).Msg("File deleted") - return fsdn.sendEvent(Delete, filepath.Base(ev.Name)) - } - if fi.Mode().IsRegular() { - fsdn.log.Trace().Str("name", ev.Name).Str("op", ev.Op.String()).Msg("File updated") - return fsdn.sendEvent(Update, filepath.Base(ev.Name)) - } - fsdn.log.Trace().Str("name", ev.Name).Msg("File not regular") - return true - } - - if ev.Has(fsnotify.Remove) { - fsdn.log.Trace().Str("name", ev.Name).Str("op", ev.Op.String()).Msg("File deleted") - return fsdn.sendEvent(Delete, filepath.Base(ev.Name)) - } - - fsdn.log.Trace().Str("name", ev.Name).Str("op", ev.Op.String()).Msg("File processed") - return true -} - -func (fsdn *fsdirNotifier) sendEvent(op EventOp, filename string) bool { - select { - case fsdn.events <- Event{Op: op, Name: filename}: - case <-fsdn.done: - fsdn.log.Trace().Msg("done file event processing") - return false + const deleteFsFileOps = fsnotify.Remove + const updateFsFileOps = fsnotify.Create | fsnotify.Write | fsnotify.Rename + + if ev.Op&updateFsFileOps != 0 { + if fi, err := os.Lstat(ev.Name); err != nil || !fi.Mode().IsRegular() { + return true + } + fsdn.log.Trace().Str("name", ev.Name).Str("op", ev.Op.String()).Msg("File updated") + select { + case fsdn.events <- Event{Op: Update, Name: filepath.Base(ev.Name)}: + case <-fsdn.done: + return false + } + } else if ev.Op&deleteFsFileOps != 0 { + fsdn.log.Trace().Str("name", ev.Name).Str("op", ev.Op.String()).Msg("File deleted") + select { + case fsdn.events <- Event{Op: Delete, Name: filepath.Base(ev.Name)}: + case <-fsdn.done: + return false + } + } else { + fsdn.log.Trace().Str("name", ev.Name).Str("op", ev.Op.String()).Msg("File processed") } return true } func (fsdn *fsdirNotifier) Close() { close(fsdn.done) } Index: cmd/cmd_run.go ================================================================== --- cmd/cmd_run.go +++ cmd/cmd_run.go @@ -71,10 +71,11 @@ ucParseZettel := usecase.NewParseZettel(rtConfig, ucGetZettel) ucListMeta := usecase.NewListMeta(protectedBoxManager) ucEvaluate := usecase.NewEvaluate(rtConfig, ucGetZettel, ucGetMeta, ucListMeta) ucListSyntax := usecase.NewListSyntax(protectedBoxManager) ucListRoles := usecase.NewListRoles(protectedBoxManager) + ucListTags := usecase.NewListTags(protectedBoxManager) ucZettelContext := usecase.NewZettelContext(protectedBoxManager, rtConfig) ucDelete := usecase.NewDeleteZettel(logUc, protectedBoxManager) ucUpdate := usecase.NewUpdateZettel(logUc, protectedBoxManager) ucRename := usecase.NewRenameZettel(logUc, protectedBoxManager) ucUnlinkedRefs := usecase.NewUnlinkedReferences(protectedBoxManager, rtConfig) @@ -113,12 +114,13 @@ ucZettelContext, &ucEvaluate)) // API webSrv.AddListRoute('a', server.MethodPost, a.MakePostLoginHandler(&ucAuthenticate)) webSrv.AddListRoute('a', server.MethodPut, a.MakeRenewAuthHandler()) - webSrv.AddListRoute('j', server.MethodGet, a.MakeQueryHandler(ucListMeta)) + webSrv.AddListRoute('j', server.MethodGet, a.MakeListMetaHandler(ucListMeta)) webSrv.AddZettelRoute('j', server.MethodGet, a.MakeGetZettelHandler(ucGetZettel)) + webSrv.AddListRoute('m', server.MethodGet, a.MakeListMapMetaHandler(ucListRoles, ucListTags)) webSrv.AddZettelRoute('m', server.MethodGet, a.MakeGetMetaHandler(ucGetMeta)) webSrv.AddZettelRoute('o', server.MethodGet, a.MakeGetOrderHandler( usecase.NewZettelOrder(protectedBoxManager, ucEvaluate))) webSrv.AddZettelRoute('p', server.MethodGet, a.MakeGetParsedZettelHandler(ucParseZettel)) webSrv.AddListRoute('q', server.MethodGet, a.MakeQueryHandler(ucListMeta)) Index: cmd/main.go ================================================================== --- cmd/main.go +++ cmd/main.go @@ -10,10 +10,11 @@ package cmd import ( "crypto/sha256" + "errors" "flag" "fmt" "net" "net/url" "os" @@ -120,13 +121,17 @@ func getConfig(fs *flag.FlagSet) *meta.Meta { cfg := fetchStartupConfiguration(fs) fs.Visit(func(flg *flag.Flag) { switch flg.Name { case "p": - cfg.Set(keyListenAddr, net.JoinHostPort("127.0.0.1", flg.Value.String())) + if portStr, err := parsePort(flg.Value.String()); err == nil { + cfg.Set(keyListenAddr, net.JoinHostPort("127.0.0.1", portStr)) + } case "a": - cfg.Set(keyAdminPort, flg.Value.String()) + if portStr, err := parsePort(flg.Value.String()); err == nil { + cfg.Set(keyAdminPort, portStr) + } case "d": val := flg.Value.String() if strings.HasPrefix(val, "/") { val = "dir://" + val } else { @@ -144,10 +149,19 @@ cfg.Set(keyVerbose, flg.Value.String()) } }) return cfg } + +func parsePort(s string) (string, error) { + port, err := net.LookupPort("tcp", s) + if err != nil { + fmt.Fprintf(os.Stderr, "Wrong port specification: %q", s) + return "", err + } + return strconv.Itoa(port), nil +} func deleteConfiguredBoxes(cfg *meta.Meta) { for _, p := range cfg.PairsRest() { if key := p.Key; strings.HasPrefix(key, kernel.BoxURIs) { cfg.Delete(key) @@ -160,11 +174,10 @@ keyAssetDir = "asset-dir" keyBaseURL = "base-url" keyDebug = "debug-mode" keyDefaultDirBoxType = "default-dir-box-type" keyInsecureCookie = "insecure-cookie" - keyInsecureHTML = "insecure-html" keyListenAddr = "listen-addr" keyLogLevel = "log-level" keyMaxRequestSize = "max-request-size" keyOwner = "owner" keyPersistentCookie = "persistent-cookie" @@ -174,74 +187,76 @@ keyTokenLifetimeAPI = "token-lifetime-api" keyURLPrefix = "url-prefix" keyVerbose = "verbose-mode" ) -func setServiceConfig(cfg *meta.Meta) bool { +func setServiceConfig(cfg *meta.Meta) error { debugMode := cfg.GetBool(keyDebug) if debugMode && kernel.Main.GetKernelLogger().Level() > logger.DebugLevel { kernel.Main.SetGlobalLogLevel(logger.DebugLevel) } if strLevel, found := cfg.Get(keyLogLevel); found { if level := logger.ParseLevel(strLevel); level.IsValid() { kernel.Main.SetGlobalLogLevel(level) } } - err := setConfigValue(nil, kernel.CoreService, kernel.CoreDebug, debugMode) - err = setConfigValue(err, kernel.CoreService, kernel.CoreVerbose, cfg.GetBool(keyVerbose)) + ok := setConfigValue(true, kernel.CoreService, kernel.CoreDebug, debugMode) + ok = setConfigValue(ok, kernel.CoreService, kernel.CoreVerbose, cfg.GetBool(keyVerbose)) if val, found := cfg.Get(keyAdminPort); found { - err = setConfigValue(err, kernel.CoreService, kernel.CorePort, val) - } - - err = setConfigValue(err, kernel.AuthService, kernel.AuthOwner, cfg.GetDefault(keyOwner, "")) - err = setConfigValue(err, kernel.AuthService, kernel.AuthReadonly, cfg.GetBool(keyReadOnly)) - - err = setConfigValue( - err, kernel.BoxService, kernel.BoxDefaultDirType, + ok = setConfigValue(ok, kernel.CoreService, kernel.CorePort, val) + } + + ok = setConfigValue(ok, kernel.AuthService, kernel.AuthOwner, cfg.GetDefault(keyOwner, "")) + ok = setConfigValue(ok, kernel.AuthService, kernel.AuthReadonly, cfg.GetBool(keyReadOnly)) + + ok = setConfigValue( + ok, kernel.BoxService, kernel.BoxDefaultDirType, cfg.GetDefault(keyDefaultDirBoxType, kernel.BoxDirTypeNotify)) - err = setConfigValue(err, kernel.BoxService, kernel.BoxURIs+"1", "dir:./zettel") + ok = setConfigValue(ok, kernel.BoxService, kernel.BoxURIs+"1", "dir:./zettel") for i := 1; ; i++ { key := kernel.BoxURIs + strconv.Itoa(i) val, found := cfg.Get(key) if !found { break } - err = setConfigValue(err, kernel.BoxService, key, val) + ok = setConfigValue(ok, kernel.BoxService, key, val) } - err = setConfigValue(err, kernel.ConfigService, kernel.ConfigInsecureHTML, cfg.GetDefault(keyInsecureHTML, kernel.ConfigSecureHTML)) - - err = setConfigValue(err, kernel.WebService, kernel.WebListenAddress, cfg.GetDefault(keyListenAddr, "127.0.0.1:23123")) + ok = setConfigValue( + ok, kernel.WebService, kernel.WebListenAddress, + cfg.GetDefault(keyListenAddr, "127.0.0.1:23123")) if val, found := cfg.Get(keyBaseURL); found { - err = setConfigValue(err, kernel.WebService, kernel.WebBaseURL, val) + ok = setConfigValue(ok, kernel.WebService, kernel.WebBaseURL, val) } if val, found := cfg.Get(keyURLPrefix); found { - err = setConfigValue(err, kernel.WebService, kernel.WebURLPrefix, val) + ok = setConfigValue(ok, kernel.WebService, kernel.WebURLPrefix, val) } - err = setConfigValue(err, kernel.WebService, kernel.WebSecureCookie, !cfg.GetBool(keyInsecureCookie)) - err = setConfigValue(err, kernel.WebService, kernel.WebPersistentCookie, cfg.GetBool(keyPersistentCookie)) + ok = setConfigValue(ok, kernel.WebService, kernel.WebSecureCookie, !cfg.GetBool(keyInsecureCookie)) + ok = setConfigValue(ok, kernel.WebService, kernel.WebPersistentCookie, cfg.GetBool(keyPersistentCookie)) if val, found := cfg.Get(keyMaxRequestSize); found { - err = setConfigValue(err, kernel.WebService, kernel.WebMaxRequestSize, val) + ok = setConfigValue(ok, kernel.WebService, kernel.WebMaxRequestSize, val) } - err = setConfigValue( - err, kernel.WebService, kernel.WebTokenLifetimeAPI, cfg.GetDefault(keyTokenLifetimeAPI, "")) - err = setConfigValue( - err, kernel.WebService, kernel.WebTokenLifetimeHTML, cfg.GetDefault(keyTokenLifetimeHTML, "")) + ok = setConfigValue( + ok, kernel.WebService, kernel.WebTokenLifetimeAPI, cfg.GetDefault(keyTokenLifetimeAPI, "")) + ok = setConfigValue( + ok, kernel.WebService, kernel.WebTokenLifetimeHTML, cfg.GetDefault(keyTokenLifetimeHTML, "")) if val, found := cfg.Get(keyAssetDir); found { - err = setConfigValue(err, kernel.WebService, kernel.WebAssetDir, val) + ok = setConfigValue(ok, kernel.WebService, kernel.WebAssetDir, val) + } + + if !ok { + return errors.New("unable to set configuration") } - return err == nil + return 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().Fatal().Str("key", key).Str("value", fmt.Sprint(val)).Err(err).Msg("Unable to set configuration") - } - } - return err +func setConfigValue(ok bool, subsys kernel.Service, key string, val interface{}) bool { + done := kernel.Main.SetConfig(subsys, key, fmt.Sprintf("%v", val)) + if !done { + kernel.Main.GetKernelLogger().Error().Str(key, fmt.Sprint(val)).Msg("Unable to set configuration") + } + return ok && done } func executeCommand(name string, args ...string) int { command, ok := Get(name) if !ok { @@ -252,12 +267,12 @@ if err := fs.Parse(args); err != nil { fmt.Fprintf(os.Stderr, "%s: unable to parse flags: %v %v\n", name, args, err) return 1 } cfg := getConfig(fs) - if !setServiceConfig(cfg) { - fs.Usage() + if err := setServiceConfig(cfg); err != nil { + fmt.Fprintf(os.Stderr, "%s: %v\n", name, err) return 2 } kern := kernel.Main var createManager kernel.CreateBoxManagerFunc Index: config/config.go ================================================================== --- config/config.go +++ config/config.go @@ -12,11 +12,10 @@ package config import ( "context" - "zettelstore.de/c/api" "zettelstore.de/z/domain/id" "zettelstore.de/z/domain/meta" ) // Key values that are supported by Config.Get @@ -41,14 +40,11 @@ GetSiteName() string // GetHomeZettel returns the value of the "home-zettel" key. GetHomeZettel() id.Zid - // GetHTMLInsecurity returns the current - GetHTMLInsecurity() HTMLInsecurity - - // GetMaxTransclusions returns the maximum number of indirect transclusions. + // GetMaxTransclusions return the maximum number of indirect transclusions. GetMaxTransclusions() int // GetYAMLHeader returns the current value of the "yaml-header" key. GetYAMLHeader() bool @@ -65,42 +61,5 @@ GetExpertMode() bool // GetVisibility returns the visibility value of the metadata. GetVisibility(m *meta.Meta) meta.Visibility } - -// HTMLInsecurity states what kind of insecure HTML is allowed. -// The lowest value is the most secure one (disallowing any HTML) -type HTMLInsecurity uint8 - -// Constant values for HTMLInsecurity: -const ( - NoHTML HTMLInsecurity = iota - SyntaxHTML - MarkdownHTML - ZettelmarkupHTML -) - -func (hi HTMLInsecurity) String() string { - switch hi { - case SyntaxHTML: - return "html" - case MarkdownHTML: - return "markdown" - case ZettelmarkupHTML: - return "zettelmarkup" - } - return "secure" -} - -// AllowHTML returns true, if the given HTML insecurity level matches the given syntax value. -func (hi HTMLInsecurity) AllowHTML(syntax string) bool { - switch hi { - case SyntaxHTML: - return syntax == api.ValueSyntaxHTML - case MarkdownHTML: - return syntax == api.ValueSyntaxHTML || syntax == "markdown" || syntax == "md" - case ZettelmarkupHTML: - return syntax == api.ValueSyntaxZmk || syntax == api.ValueSyntaxHTML || syntax == "markdown" || syntax == "md" - } - return false -} DELETED docs/manual/00000000025001 Index: docs/manual/00000000025001 ================================================================== --- docs/manual/00000000025001 +++ docs/manual/00000000025001 @@ -1,7 +0,0 @@ -id: 00000000025001 -title: Zettelstore User CSS -role: configuration -syntax: css -created: 20210622110143 -modified: 20220926183101 -visibility: public DELETED docs/manual/00000000025001.css Index: docs/manual/00000000025001.css ================================================================== --- docs/manual/00000000025001.css +++ docs/manual/00000000025001.css @@ -1,2 +0,0 @@ -/* User-defined CSS */ -.example { border-style: dotted !important } Index: docs/manual/00001002000000.zettel ================================================================== --- docs/manual/00001002000000.zettel +++ docs/manual/00001002000000.zettel @@ -1,22 +1,21 @@ id: 00001002000000 title: Design goals for the Zettelstore role: manual tags: #design #goal #manual #zettelstore syntax: zmk -created: 20210126175322 -modified: 20221018105415 +modified: 20211124131628 Zettelstore supports the following design goals: ; Longevity of stored notes / zettel : Every zettel you create should be readable without the help of any tool, even without Zettelstore. : It should be not hard to write other software that works with your zettel. ; Single user : All zettel belong to you, only to you. Zettelstore provides its services only to one person: you. - If the computer running Zettelstore is securely configured, there should be no risk that others are able to read or update your zettel. + If your device is securely configured, there should be no risk that others are able to read or update your zettel. : If you want, you can customize Zettelstore in a way that some specific or all persons are able to read some of your zettel. ; Ease of installation : If you want to use the Zettelstore software, all you need is to copy the executable to an appropriate file directory and start working. : Upgrading the software is done just by replacing the executable with a newer one. ; Ease of operation @@ -29,9 +28,5 @@ : Zettelstore provides a default [[web-based user interface|00001014000000]]. Anybody can provide alternative user interfaces, e.g. for special purposes. ; Simple service : The purpose of Zettelstore is to safely store your zettel and to provide some initial relations between them. : External software can be written to deeply analyze your zettel and the structures they form. -; Security by default -: Without any customization, Zettelstore provides its services in a safe and secure manner and does not expose you (or other users) to security risks. -: If you know what use are doing, Zettelstore allows you to relax some security-related preferences. - However, even in this case, the more secure way is chosen. Index: docs/manual/00001004010000.zettel ================================================================== --- docs/manual/00001004010000.zettel +++ docs/manual/00001004010000.zettel @@ -2,11 +2,11 @@ title: Zettelstore startup configuration role: manual tags: #configuration #manual #zettelstore syntax: zmk created: 20210126175322 -modified: 20221018184208 +modified: 20220914183434 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. @@ -66,20 +66,10 @@ ; [!insecure-cookie|''insecure-cookie''] : Must be set to [[true|00001006030500]], if authentication is enabled and Zettelstore is not accessible not via HTTPS (but via HTTP). Otherwise web browser are free to ignore the authentication cookie. Default: ""false"" -; [!insecure-html|''insecure-html''] -: Allows to use HTML, e.g. within supported markup languages, even if this might introduce security-related problems. - However, HTML containing the ``