aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/blog/blog.go93
-rw-r--r--pkg/atom/atom.go57
-rw-r--r--template/root.tmpl3
3 files changed, 149 insertions, 4 deletions
diff --git a/cmd/blog/blog.go b/cmd/blog/blog.go
index cddb5a2..ee3491d 100644
--- a/cmd/blog/blog.go
+++ b/cmd/blog/blog.go
@@ -8,6 +8,7 @@ package main
import (
"bytes"
+ "encoding/xml"
"html/template"
"log"
"net/http"
@@ -16,14 +17,18 @@ import (
"path/filepath"
"sort"
"strings"
+ "time"
+ "code.google.com/p/go.blog/pkg/atom"
_ "code.google.com/p/go.talks/pkg/playground"
"code.google.com/p/go.talks/pkg/present"
)
const (
- baseURL = "http://blog.golang.org"
- homeArticles = 5 // number of articles to display on the home page
+ hostname = "blog.golang.org"
+ baseURL = "http://" + hostname
+ homeArticles = 5 // number of articles to display on the home page
+ feedArticles = 10 // number of articles to include in Atom feed
)
// Doc represents an article, adorned with presentation data:
@@ -47,7 +52,8 @@ type Server struct {
template struct {
home, index, article, doc *template.Template
}
- content http.Handler
+ atomFeed []byte // pre-rendered Atom feed
+ content http.Handler
}
// NewServer constructs a new Server, serving articles from the specified
@@ -90,6 +96,11 @@ func NewServer(pathPrefix, contentPath, templatePath string) (*Server, error) {
return nil, err
}
+ err = s.renderAtomFeed()
+ if err != nil {
+ return nil, err
+ }
+
// Set up content file server.
s.content = http.StripPrefix(pathPrefix, http.FileServer(http.Dir(contentPath)))
@@ -226,6 +237,78 @@ func (s *Server) loadDocs(root string) error {
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) > 1 {
+ updated = s.docs[0].Time
+ }
+ feed := atom.Feed{
+ Title: "The Go Programming Language Blog",
+ ID: "tag:" + hostname + ",2013:" + hostname,
+ Updated: atom.Time(updated),
+ Link: []atom.Link{{
+ Rel: "self",
+ Href: baseURL + path.Join(s.pathPrefix, "/feed.atom"),
+ }},
+ }
+ for i, doc := range s.docs {
+ if i >= feedArticles {
+ break
+ }
+ e := &atom.Entry{
+ Title: doc.Title,
+ ID: feed.ID + doc.Path,
+ Link: []atom.Link{{
+ Rel: "alternate",
+ Href: baseURL + doc.Path,
+ }},
+ 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
+}
+
+// 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
@@ -253,6 +336,10 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case "index":
d.Data = s.docs
t = s.template.index
+ case "feed.atom":
+ w.Header().Set("Content-type", "application/atom+xml")
+ w.Write(s.atomFeed)
+ return
default:
doc, ok := s.docPaths[p]
if !ok {
diff --git a/pkg/atom/atom.go b/pkg/atom/atom.go
new file mode 100644
index 0000000..bc114dd
--- /dev/null
+++ b/pkg/atom/atom.go
@@ -0,0 +1,57 @@
+// Copyright 2009 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.
+
+// Adapted from encoding/xml/read_test.go.
+
+// Package atom defines XML data structures for an Atom feed.
+package atom
+
+import (
+ "encoding/xml"
+ "time"
+)
+
+type Feed struct {
+ XMLName xml.Name `xml:"http://www.w3.org/2005/Atom feed"`
+ Title string `xml:"title"`
+ ID string `xml:"id"`
+ Link []Link `xml:"link"`
+ Updated TimeStr `xml:"updated"`
+ Author *Person `xml:"author"`
+ Entry []*Entry `xml:"entry"`
+}
+
+type Entry struct {
+ Title string `xml:"title"`
+ ID string `xml:"id"`
+ Link []Link `xml:"link"`
+ Published TimeStr `xml:"published"`
+ Updated TimeStr `xml:"updated"`
+ Author *Person `xml:"author"`
+ Summary *Text `xml:"summary"`
+ Content *Text `xml:"content"`
+}
+
+type Link struct {
+ Rel string `xml:"rel,attr"`
+ Href string `xml:"href,attr"`
+}
+
+type Person struct {
+ Name string `xml:"name"`
+ URI string `xml:"uri,omitempty"`
+ Email string `xml:"email,omitempty"`
+ InnerXML string `xml:",innerxml"`
+}
+
+type Text struct {
+ Type string `xml:"type,attr"`
+ Body string `xml:",chardata"`
+}
+
+type TimeStr string
+
+func Time(t time.Time) TimeStr {
+ return TimeStr(t.Format("2006-01-02T15:04:05-07:00"))
+}
diff --git a/template/root.tmpl b/template/root.tmpl
index 4348690..5b2f80e 100644
--- a/template/root.tmpl
+++ b/template/root.tmpl
@@ -4,9 +4,10 @@
<!DOCTYPE html>
<html>
<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>{{template "title" .}}</title>
<link type="text/css" rel="stylesheet" href="/static/style.css">
+ <link rel="alternate" type="application/atom+xml" title="blog.golang.org - Atom Feed" href="http://blog.golang.org/feed.atom" />
</head>
<body>