summaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/tools/blog/blog.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/tools/blog/blog.go')
-rw-r--r--vendor/golang.org/x/tools/blog/blog.go437
1 files changed, 0 insertions, 437 deletions
diff --git a/vendor/golang.org/x/tools/blog/blog.go b/vendor/golang.org/x/tools/blog/blog.go
deleted file mode 100644
index 4055b1b..0000000
--- a/vendor/golang.org/x/tools/blog/blog.go
+++ /dev/null
@@ -1,437 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package blog implements a web server for articles written in present format.
-package blog // import "golang.org/x/tools/blog"
-
-import (
- "bytes"
- "encoding/json"
- "encoding/xml"
- "fmt"
- "html/template"
- "log"
- "net/http"
- "os"
- "path/filepath"
- "regexp"
- "sort"
- "strings"
- "time"
-
- "golang.org/x/tools/blog/atom"
- "golang.org/x/tools/present"
-)
-
-var validJSONPFunc = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_.]*$`)
-
-// Config specifies Server configuration values.
-type Config struct {
- ContentPath string // Relative or absolute location of article files and related content.
- TemplatePath string // Relative or absolute location of template files.
-
- BaseURL string // Absolute base URL (for permalinks; no trailing slash).
- BasePath string // Base URL path relative to server root (no trailing slash).
- GodocURL string // The base URL of godoc (for menu bar; no trailing slash).
- Hostname string // Server host name, used for rendering ATOM feeds.
-
- HomeArticles int // Articles to display on the home page.
- FeedArticles int // Articles to include in Atom and JSON feeds.
- FeedTitle string // The title of the Atom XML feed
-
- PlayEnabled bool
-}
-
-// Doc represents an article adorned with presentation data.
-type Doc struct {
- *present.Doc
- Permalink string // Canonical URL for this document.
- Path string // Path relative to server root (including base).
- HTML template.HTML // rendered article
-
- Related []*Doc
- Newer, Older *Doc
-}
-
-// 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 {
- home, index, article, doc *template.Template
- }
- atomFeed []byte // pre-rendered Atom feed
- jsonFeed []byte // pre-rendered JSON feed
- content http.Handler
-}
-
-// NewServer constructs a new Server using the specified config.
-func NewServer(cfg Config) (*Server, error) {
- present.PlayEnabled = cfg.PlayEnabled
-
- if notExist(cfg.TemplatePath) {
- return nil, fmt.Errorf("template directory not found: %s", cfg.TemplatePath)
- }
- root := filepath.Join(cfg.TemplatePath, "root.tmpl")
- parse := func(name string) (*template.Template, error) {
- path := filepath.Join(cfg.TemplatePath, name)
- if notExist(path) {
- return nil, fmt.Errorf("template %s was not found in %s", name, cfg.TemplatePath)
- }
- t := template.New("").Funcs(funcMap)
- return t.ParseFiles(root, path)
- }
-
- s := &Server{cfg: cfg}
-
- // Parse templates.
- var err error
- s.template.home, err = parse("home.tmpl")
- if err != nil {
- return nil, err
- }
- s.template.index, err = parse("index.tmpl")
- if err != nil {
- return nil, err
- }
- s.template.article, err = parse("article.tmpl")
- if err != nil {
- return nil, err
- }
- p := present.Template().Funcs(funcMap)
- s.template.doc, err = p.ParseFiles(filepath.Join(cfg.TemplatePath, "doc.tmpl"))
- if err != nil {
- return nil, err
- }
-
- // Load content.
- err = s.loadDocs(filepath.Clean(cfg.ContentPath))
- if err != nil {
- return nil, err
- }
-
- err = s.renderAtomFeed()
- if err != nil {
- return nil, err
- }
-
- err = s.renderJSONFeed()
- if err != nil {
- return nil, err
- }
-
- // Set up content file server.
- s.content = http.StripPrefix(s.cfg.BasePath, http.FileServer(http.Dir(cfg.ContentPath)))
-
- return s, nil
-}
-
-var funcMap = template.FuncMap{
- "sectioned": sectioned,
- "authors": authors,
-}
-
-// sectioned returns true if the provided Doc contains more than one section.
-// This is used to control whether to display the table of contents and headings.
-func sectioned(d *present.Doc) bool {
- return len(d.Sections) > 1
-}
-
-// authors returns a comma-separated list of author names.
-func authors(authors []present.Author) string {
- var b bytes.Buffer
- last := len(authors) - 1
- for i, a := range authors {
- if i > 0 {
- if i == last {
- b.WriteString(" and ")
- } else {
- b.WriteString(", ")
- }
- }
- b.WriteString(authorName(a))
- }
- return b.String()
-}
-
-// authorName returns the first line of the Author text: the author's name.
-func authorName(a present.Author) string {
- el := a.TextElem()
- if len(el) == 0 {
- return ""
- }
- text, ok := el[0].(present.Text)
- if !ok || len(text.Lines) == 0 {
- return ""
- }
- return text.Lines[0]
-}
-
-// loadDocs reads all content from the provided file system root, renders all
-// the articles it finds, adds them to the Server's docs field, computes the
-// denormalized docPaths, docTags, and tags fields, and populates the various
-// helper fields (Next, Previous, Related) for each Doc.
-func (s *Server) loadDocs(root string) error {
- // Read content into docs field.
- const ext = ".article"
- fn := func(p string, info os.FileInfo, err error) error {
- if filepath.Ext(p) != ext {
- return nil
- }
- f, err := os.Open(p)
- if err != nil {
- return err
- }
- defer f.Close()
- d, err := present.Parse(f, p, 0)
- if err != nil {
- return err
- }
- html := new(bytes.Buffer)
- err = d.Render(html, s.template.doc)
- if err != nil {
- return err
- }
- p = p[len(root) : len(p)-len(ext)] // trim root and extension
- p = filepath.ToSlash(p)
- s.docs = append(s.docs, &Doc{
- Doc: d,
- Path: s.cfg.BasePath + p,
- Permalink: s.cfg.BaseURL + p,
- HTML: template.HTML(html.String()),
- })
- return nil
- }
- err := filepath.Walk(root, fn)
- if err != nil {
- return err
- }
- sort.Sort(docsByTime(s.docs))
-
- // Pull out doc paths and tags and put in reverse-associating maps.
- s.docPaths = make(map[string]*Doc)
- s.docTags = make(map[string][]*Doc)
- 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)
- }
- }
-
- // Pull out unique sorted list of tags.
- for t := range s.docTags {
- s.tags = append(s.tags, t)
- }
- sort.Strings(s.tags)
-
- // Set up presentation-related fields, Newer, Older, and Related.
- for _, doc := range s.docs {
- // Newer, Older: docs adjacent to doc
- for i := range s.docs {
- if s.docs[i] != doc {
- continue
- }
- if i > 0 {
- doc.Newer = s.docs[i-1]
- }
- if i+1 < len(s.docs) {
- doc.Older = s.docs[i+1]
- }
- break
- }
-
- // Related: all docs that share tags with doc.
- related := make(map[*Doc]bool)
- for _, t := range doc.Tags {
- for _, d := range s.docTags[t] {
- if d != doc {
- related[d] = true
- }
- }
- }
- for d := range related {
- doc.Related = append(doc.Related, d)
- }
- sort.Sort(docsByTime(doc.Related))
- }
-
- return nil
-}
-
-// renderAtomFeed generates an XML Atom feed and stores it in the Server's
-// atomFeed field.
-func (s *Server) renderAtomFeed() error {
- var updated time.Time
- if len(s.docs) > 0 {
- updated = s.docs[0].Time
- }
- feed := atom.Feed{
- Title: s.cfg.FeedTitle,
- ID: "tag:" + s.cfg.Hostname + ",2013:" + s.cfg.Hostname,
- Updated: atom.Time(updated),
- Link: []atom.Link{{
- Rel: "self",
- Href: s.cfg.BaseURL + "/feed.atom",
- }},
- }
- for i, doc := range s.docs {
- if i >= s.cfg.FeedArticles {
- break
- }
- e := &atom.Entry{
- Title: doc.Title,
- ID: feed.ID + doc.Path,
- Link: []atom.Link{{
- Rel: "alternate",
- Href: doc.Permalink,
- }},
- Published: atom.Time(doc.Time),
- Updated: atom.Time(doc.Time),
- Summary: &atom.Text{
- Type: "html",
- Body: summary(doc),
- },
- Content: &atom.Text{
- Type: "html",
- Body: string(doc.HTML),
- },
- Author: &atom.Person{
- Name: authors(doc.Authors),
- },
- }
- feed.Entry = append(feed.Entry, e)
- }
- data, err := xml.Marshal(&feed)
- if err != nil {
- return err
- }
- s.atomFeed = data
- return nil
-}
-
-type jsonItem struct {
- Title string
- Link string
- Time time.Time
- Summary string
- Content string
- Author string
-}
-
-// renderJSONFeed generates a JSON feed and stores it in the Server's jsonFeed
-// field.
-func (s *Server) renderJSONFeed() error {
- var feed []jsonItem
- for i, doc := range s.docs {
- if i >= s.cfg.FeedArticles {
- break
- }
- item := jsonItem{
- Title: doc.Title,
- Link: doc.Permalink,
- Time: doc.Time,
- Summary: summary(doc),
- Content: string(doc.HTML),
- Author: authors(doc.Authors),
- }
- feed = append(feed, item)
- }
- data, err := json.Marshal(feed)
- if err != nil {
- return err
- }
- s.jsonFeed = data
- return nil
-}
-
-// summary returns the first paragraph of text from the provided Doc.
-func summary(d *Doc) string {
- if len(d.Sections) == 0 {
- return ""
- }
- for _, elem := range d.Sections[0].Elem {
- text, ok := elem.(present.Text)
- if !ok || text.Pre {
- // skip everything but non-text elements
- continue
- }
- var buf bytes.Buffer
- for _, s := range text.Lines {
- buf.WriteString(string(present.Style(s)))
- buf.WriteByte('\n')
- }
- return buf.String()
- }
- return ""
-}
-
-// rootData encapsulates data destined for the root template.
-type rootData struct {
- Doc *Doc
- BasePath string
- GodocURL string
- Data interface{}
-}
-
-// ServeHTTP serves the front, index, and article pages
-// as well as the ATOM and JSON feeds.
-func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- var (
- d = rootData{BasePath: s.cfg.BasePath, GodocURL: s.cfg.GodocURL}
- t *template.Template
- )
- switch p := strings.TrimPrefix(r.URL.Path, s.cfg.BasePath); p {
- case "/":
- d.Data = s.docs
- if len(s.docs) > s.cfg.HomeArticles {
- d.Data = s.docs[:s.cfg.HomeArticles]
- }
- t = s.template.home
- case "/index":
- d.Data = s.docs
- t = s.template.index
- case "/feed.atom", "/feeds/posts/default":
- w.Header().Set("Content-type", "application/atom+xml; charset=utf-8")
- w.Write(s.atomFeed)
- return
- case "/.json":
- if p := r.FormValue("jsonp"); validJSONPFunc.MatchString(p) {
- w.Header().Set("Content-type", "application/javascript; charset=utf-8")
- fmt.Fprintf(w, "%v(%s)", p, s.jsonFeed)
- return
- }
- w.Header().Set("Content-type", "application/json; charset=utf-8")
- w.Write(s.jsonFeed)
- return
- default:
- doc, ok := s.docPaths[p]
- if !ok {
- // Not a doc; try to just serve static content.
- s.content.ServeHTTP(w, r)
- return
- }
- d.Doc = doc
- t = s.template.article
- }
- err := t.ExecuteTemplate(w, "root", d)
- if err != nil {
- log.Println(err)
- }
-}
-
-// docsByTime implements sort.Interface, sorting Docs by their Time field.
-type docsByTime []*Doc
-
-func (s docsByTime) Len() int { return len(s) }
-func (s docsByTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-func (s docsByTime) Less(i, j int) bool { return s[i].Time.After(s[j].Time) }
-
-// notExist reports whether the path exists or not.
-func notExist(path string) bool {
- _, err := os.Stat(path)
- return os.IsNotExist(err)
-}