commit f4991da6450c09fb3d7317c7b9ed6ace57999bfe equwal <13551856+equwal@users.noreply.github.com> 2026-09-25 03:14:12 -0700 Add the command 'sbm-webpublish feed URL FEED' It sets the feed of one link in a shell on the server, as the Save feed form of the admin page does. merge cannot change a feed that a link has already. The form and the command tell when no link has the URL.
README.md | 7 +++++-- main.go | 53 +++++++++++++++++++++++++++++++++++++++-------------- server_test.go | 17 +++++++++++++++++ 3 files changed, 61 insertions(+), 16 deletions(-)
diff --git a/README.md b/README.md index 98a3cd9..36122e9 100644 --- a/README.md +++ b/README.md @@ -80,8 +80,11 @@ import does: bm --list | ssh root@example.com 'LINKS_FILE=/var/lib/sbm-webpublish/links.sbm sbm-webpublish merge' -It does not lock the file against the service, so do not use the admin -page at the same time. +`sbm-webpublish feed URL FEED` sets the feed of one link, as the admin +page does. An empty FEED (`''`) removes the feed. + +The commands do not lock the file against the service, so do not use the +admin page at the same time. ## Run diff --git a/main.go b/main.go index 65565b2..53b1259 100644 --- a/main.go +++ b/main.go @@ -203,20 +203,37 @@ func (s *server) add(w http.ResponseWriter, r *http.Request) { } } -// editFeed sets the feed of one link. An empty feed removes the feed. -func (s *server) editFeed(w http.ResponseWriter, r *http.Request) { - feed := r.FormValue("feed") +// saveFeed sets the feed of the link with the URL u, and tells what it did. +// An empty feed removes the feed. +func saveFeed(links *store, u, feed string) (string, error) { if badFeed(feed) { - done(w, r, "Give an http or https address for the feed.") - return + return "Give an http or https address for the feed.", nil } - k := norm(r.FormValue("url")) - if err := s.links.change(func(text string) string { return setFeed(text, k, feed) }); err != nil { + k, found := norm(u), false + err := links.change(func(text string) string { + for _, s := range lines(text) { + found = found || k != "" && key(s) == k + } + return setFeed(text, k, feed) + }) + switch { + case err != nil: + return "", err + case !found: + return "No link has that URL.", nil + } + return "Saved the feed.", nil +} + +// editFeed sets the feed of one link. An empty feed removes the feed. +func (s *server) editFeed(w http.ResponseWriter, r *http.Request) { + message, err := saveFeed(s.links, r.FormValue("url"), r.FormValue("feed")) + if err != nil { log.Print(err) http.Error(w, "cannot write the links", http.StatusInternalServerError) return } - done(w, r, "Saved the feed.") + done(w, r, message) } func (s *server) delete(w http.ResponseWriter, r *http.Request) { @@ -273,14 +290,22 @@ func env(name, fallback string) string { func main() { links := &store{path: env("LINKS_FILE", "links.sbm")} if len(os.Args) > 1 { - // "merge" adds the links of stdin, as bm --merge does. It does not - // lock the file against the service: do not use the admin page at - // the same time. - if os.Args[1] != "merge" || len(os.Args) > 2 { - fmt.Fprintln(os.Stderr, "usage: sbm-webpublish [merge < bookmarks]") + // The commands change the file as the admin page does, in a shell + // on the server. They do not lock the file against the service: do + // not use the admin page at the same time. + var message string + var err error + switch args := os.Args[1:]; { + case len(args) == 1 && args[0] == "merge": + // merge adds the links of stdin, as bm --merge does. + message, err = mergeFrom(links, os.Stdin, time.Now()) + case len(args) == 3 && args[0] == "feed": + // feed URL FEED sets the feed of a link. + message, err = saveFeed(links, args[1], args[2]) + default: + fmt.Fprintln(os.Stderr, "usage: sbm-webpublish [merge < bookmarks | feed URL FEED]") os.Exit(2) } - message, err := mergeFrom(links, os.Stdin, time.Now()) if err != nil { log.Fatal(err) } diff --git a/server_test.go b/server_test.go index d3769a1..2f4774a 100644 --- a/server_test.go +++ b/server_test.go @@ -330,6 +330,23 @@ func TestImport(t *testing.T) { } } +func TestFeedCommand(t *testing.T) { + s, _ := newServer(t) + write(t, s, "https://a.com\tA\tx\thttps://github.com/a/commits.atom\n") + for _, c := range []struct{ u, feed, message, file string }{ + // A feed that the link has already changes. + {"a.com/", "https://a.com/Japanese.xml", "Saved the feed.", "https://a.com\tA\tx\thttps://a.com/Japanese.xml\n"}, + {"https://b.com", "https://b.com/rss", "No link has that URL.", "https://a.com\tA\tx\thttps://a.com/Japanese.xml\n"}, + {"https://a.com", "file:///x", "Give an http or https address for the feed.", "https://a.com\tA\tx\thttps://a.com/Japanese.xml\n"}, + } { + m, err := saveFeed(s.links, c.u, c.feed) + b, _ := os.ReadFile(s.links.path) + if err != nil || m != c.message || string(b) != c.file { + t.Errorf("feed %s %s: %q %v, file %q", c.u, c.feed, m, err, b) + } + } +} + func TestMergeCommand(t *testing.T) { s, _ := newServer(t) write(t, s, "https://a.com\tA\t\n")