aboutsummaryrefslogtreecommitdiff
path: root/cmd/blog/blog.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/blog/blog.go')
-rw-r--r--cmd/blog/blog.go61
1 files changed, 58 insertions, 3 deletions
diff --git a/cmd/blog/blog.go b/cmd/blog/blog.go
index 1542bad..12a42b8 100644
--- a/cmd/blog/blog.go
+++ b/cmd/blog/blog.go
@@ -8,12 +8,15 @@ package main
import (
"bytes"
+ "encoding/json"
"encoding/xml"
+ "fmt"
"html/template"
"log"
"net/http"
"os"
"path/filepath"
+ "regexp"
"sort"
"time"
@@ -25,10 +28,12 @@ import (
const (
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
+ homeArticles = 5 // articles to display on the home page
+ feedArticles = 10 // articles to include in Atom and JSON feeds
)
+var validJSONPFunc = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_.]*$`)
+
// Doc represents an article, adorned with presentation data:
// its absolute path and related articles.
type Doc struct {
@@ -50,6 +55,7 @@ type Server struct {
home, index, article, doc *template.Template
}
atomFeed []byte // pre-rendered Atom feed
+ jsonFeed []byte // pre-rendered JSON feed
content http.Handler
}
@@ -97,6 +103,11 @@ func NewServer(contentPath, templatePath string) (*Server, error) {
return nil, err
}
+ err = s.renderJSONFeed()
+ if err != nil {
+ return nil, err
+ }
+
// Set up content file server.
s.content = http.FileServer(http.Dir(contentPath))
@@ -285,6 +296,41 @@ func (s *Server) renderAtomFeed() error {
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 >= feedArticles {
+ break
+ }
+ item := jsonItem{
+ Title: doc.Title,
+ Link: baseURL + doc.Path,
+ 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 {
@@ -329,9 +375,18 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
d.Data = s.docs
t = s.template.index
case "/feed.atom", "/feeds/posts/default":
- w.Header().Set("Content-type", "application/atom+xml")
+ 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")
+ w.Write(s.jsonFeed)
+ return
default:
doc, ok := s.docPaths[p]
if !ok {