present: add OldURL metadata and use for redirects in blog
This will allow renaming blog pages to have shorter,
more easily typed URLs, while keeping the old links working.
Change-Id: I2cd6733eaaf02a4b8e73afc773173c655d317ee6
Reviewed-on: https://go-review.googlesource.com/c/tools/+/223603
Reviewed-by: Andrew Bonventre <andybons@golang.org>
diff --git a/blog/blog.go b/blog/blog.go
index 1c3bc54..3e8f873 100644
--- a/blog/blog.go
+++ b/blog/blog.go
@@ -65,12 +65,13 @@
// Server implements an http.Handler that serves blog articles.
type Server struct {
- cfg Config
- docs []*Doc
- tags []string
- docPaths map[string]*Doc // key is path without BasePath.
- docTags map[string][]*Doc
- template struct {
+ cfg Config
+ docs []*Doc
+ redirects map[string]string
+ tags []string
+ docPaths map[string]*Doc // key is path without BasePath.
+ docTags map[string][]*Doc
+ template struct {
home, index, article, doc *template.Template
}
atomFeed []byte // pre-rendered Atom feed
@@ -118,7 +119,8 @@
}
// Load content.
- err = s.loadDocs(filepath.Clean(cfg.ContentPath))
+ content := filepath.Clean(cfg.ContentPath)
+ err = s.loadDocs(content)
if err != nil {
return nil, err
}
@@ -191,6 +193,7 @@
if err != nil {
return err
}
+
if filepath.Ext(p) != ext {
return nil
}
@@ -227,12 +230,27 @@
// Pull out doc paths and tags and put in reverse-associating maps.
s.docPaths = make(map[string]*Doc)
s.docTags = make(map[string][]*Doc)
+ s.redirects = make(map[string]string)
for _, d := range s.docs {
s.docPaths[strings.TrimPrefix(d.Path, s.cfg.BasePath)] = d
for _, t := range d.Tags {
s.docTags[t] = append(s.docTags[t], d)
}
}
+ for _, d := range s.docs {
+ for _, old := range d.OldURL {
+ if !strings.HasPrefix(old, "/") {
+ old = "/" + old
+ }
+ if _, ok := s.docPaths[old]; ok {
+ return fmt.Errorf("redirect %s -> %s conflicts with document %s", old, d.Path, old)
+ }
+ if new, ok := s.redirects[old]; ok {
+ return fmt.Errorf("redirect %s -> %s conflicts with redirect %s -> %s", old, d.Path, old, new)
+ }
+ s.redirects[old] = d.Path
+ }
+ }
// Pull out unique sorted list of tags.
for t := range s.docTags {
@@ -425,6 +443,10 @@
w.Write(s.jsonFeed)
return
default:
+ if redir, ok := s.redirects[p]; ok {
+ http.Redirect(w, r, redir, http.StatusMovedPermanently)
+ return
+ }
doc, ok := s.docPaths[p]
if !ok {
// Not a doc; try to just serve static content.
diff --git a/present/doc.go b/present/doc.go
index fb84e12..cdee8ad 100644
--- a/present/doc.go
+++ b/present/doc.go
@@ -17,6 +17,7 @@
15:04 2 Jan 2006
Tags: foo, bar, baz
Summary: This is a great document you want to read.
+ OldURL: former-path-for-this-doc
The "# " prefix before the title indicates that this is
a Markdown-enabled present file: it uses
@@ -33,8 +34,12 @@
The summary line gives a short summary used in blog feeds.
+The old URL line, which may be repeated, gives an older (perhaps relative) URL
+for this document.
+A server might use these to generate appropriate redirects.
+
Only the title is required;
-the subtitle, date, tags, and summary lines are optional.
+the subtitle, date, tags, summary, and old URL lines are optional.
In Markdown-enabled present, the summary defaults to being empty.
In legacy present, the summary defaults to the first paragraph of text.
diff --git a/present/parse.go b/present/parse.go
index 7f38e6e..672a6ff 100644
--- a/present/parse.go
+++ b/present/parse.go
@@ -79,6 +79,7 @@
TitleNotes []string
Sections []Section
Tags []string
+ OldURL []string
}
// Author represents the person who wrote and/or is presenting the document.
@@ -546,6 +547,8 @@
doc.Tags = append(doc.Tags, tags...)
} else if strings.HasPrefix(text, "Summary:") {
doc.Summary = strings.TrimSpace(text[len("Summary:"):])
+ } else if strings.HasPrefix(text, "OldURL:") {
+ doc.OldURL = append(doc.OldURL, strings.TrimSpace(text[len("OldURL:"):]))
} else if t, ok := parseTime(text); ok {
doc.Time = t
} else if doc.Subtitle == "" {