1
0
mirror of https://github.com/golang/go synced 2024-11-18 13:24:39 -07:00

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>
This commit is contained in:
Russ Cox 2020-03-15 16:22:23 -04:00
parent cf1dd6fc34
commit b304dfc3e0
3 changed files with 38 additions and 8 deletions

View File

@ -65,12 +65,13 @@ type Doc struct {
// Server implements an http.Handler that serves blog articles. // Server implements an http.Handler that serves blog articles.
type Server struct { type Server struct {
cfg Config cfg Config
docs []*Doc docs []*Doc
tags []string redirects map[string]string
docPaths map[string]*Doc // key is path without BasePath. tags []string
docTags map[string][]*Doc docPaths map[string]*Doc // key is path without BasePath.
template struct { docTags map[string][]*Doc
template struct {
home, index, article, doc *template.Template home, index, article, doc *template.Template
} }
atomFeed []byte // pre-rendered Atom feed atomFeed []byte // pre-rendered Atom feed
@ -118,7 +119,8 @@ func NewServer(cfg Config) (*Server, error) {
} }
// Load content. // Load content.
err = s.loadDocs(filepath.Clean(cfg.ContentPath)) content := filepath.Clean(cfg.ContentPath)
err = s.loadDocs(content)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -191,6 +193,7 @@ func (s *Server) loadDocs(root string) error {
if err != nil { if err != nil {
return err return err
} }
if filepath.Ext(p) != ext { if filepath.Ext(p) != ext {
return nil return nil
} }
@ -227,12 +230,27 @@ func (s *Server) loadDocs(root string) error {
// Pull out doc paths and tags and put in reverse-associating maps. // Pull out doc paths and tags and put in reverse-associating maps.
s.docPaths = make(map[string]*Doc) s.docPaths = make(map[string]*Doc)
s.docTags = make(map[string][]*Doc) s.docTags = make(map[string][]*Doc)
s.redirects = make(map[string]string)
for _, d := range s.docs { for _, d := range s.docs {
s.docPaths[strings.TrimPrefix(d.Path, s.cfg.BasePath)] = d s.docPaths[strings.TrimPrefix(d.Path, s.cfg.BasePath)] = d
for _, t := range d.Tags { for _, t := range d.Tags {
s.docTags[t] = append(s.docTags[t], d) 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. // Pull out unique sorted list of tags.
for t := range s.docTags { for t := range s.docTags {
@ -425,6 +443,10 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write(s.jsonFeed) w.Write(s.jsonFeed)
return return
default: default:
if redir, ok := s.redirects[p]; ok {
http.Redirect(w, r, redir, http.StatusMovedPermanently)
return
}
doc, ok := s.docPaths[p] doc, ok := s.docPaths[p]
if !ok { if !ok {
// Not a doc; try to just serve static content. // Not a doc; try to just serve static content.

View File

@ -17,6 +17,7 @@ and other metadata, which looks like:
15:04 2 Jan 2006 15:04 2 Jan 2006
Tags: foo, bar, baz Tags: foo, bar, baz
Summary: This is a great document you want to read. 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 The "# " prefix before the title indicates that this is
a Markdown-enabled present file: it uses a Markdown-enabled present file: it uses
@ -33,8 +34,12 @@ the document.
The summary line gives a short summary used in blog feeds. 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; 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 Markdown-enabled present, the summary defaults to being empty.
In legacy present, the summary defaults to the first paragraph of text. In legacy present, the summary defaults to the first paragraph of text.

View File

@ -79,6 +79,7 @@ type Doc struct {
TitleNotes []string TitleNotes []string
Sections []Section Sections []Section
Tags []string Tags []string
OldURL []string
} }
// Author represents the person who wrote and/or is presenting the document. // Author represents the person who wrote and/or is presenting the document.
@ -546,6 +547,8 @@ func parseHeader(doc *Doc, isMarkdown bool, lines *Lines) error {
doc.Tags = append(doc.Tags, tags...) doc.Tags = append(doc.Tags, tags...)
} else if strings.HasPrefix(text, "Summary:") { } else if strings.HasPrefix(text, "Summary:") {
doc.Summary = strings.TrimSpace(text[len("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 { } else if t, ok := parseTime(text); ok {
doc.Time = t doc.Time = t
} else if doc.Subtitle == "" { } else if doc.Subtitle == "" {