| // Copyright 2020 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 main |
| |
| import ( |
| "bytes" |
| "fmt" |
| "html/template" |
| "log" |
| "net/http" |
| "sort" |
| |
| "golang.org/x/tools/godoc" |
| "golang.org/x/tools/godoc/vfs" |
| "golang.org/x/website/internal/history" |
| ) |
| |
| // projectHandler serves The Go Project page. |
| type projectHandler struct { |
| ReleaseHistory []MajorRelease // Pre-computed release history to display. |
| } |
| |
| func (h projectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { |
| const relPath = "doc/contrib.html" |
| |
| src, err := vfs.ReadFile(fs, relPath) |
| if err != nil { |
| log.Printf("reading template %s: %v", relPath, err) |
| pres.ServeError(w, req, relPath, err) |
| return |
| } |
| |
| meta, src, err := extractMetadata(src) |
| if err != nil { |
| log.Printf("decoding metadata %s: %v", relPath, err) |
| pres.ServeError(w, req, relPath, err) |
| return |
| } |
| if !meta.Template { |
| err := fmt.Errorf("got non-template, want template") |
| log.Printf("unexpected metadata %s: %v", relPath, err) |
| pres.ServeError(w, req, relPath, err) |
| return |
| } |
| |
| page := godoc.Page{ |
| Title: meta.Title, |
| Subtitle: meta.Subtitle, |
| GoogleCN: googleCN(req), |
| } |
| data := projectTemplateData{ |
| Major: h.ReleaseHistory, |
| } |
| |
| // Evaluate as HTML template. |
| tmpl, err := template.New("").Parse(string(src)) |
| if err != nil { |
| log.Printf("parsing template %s: %v", relPath, err) |
| pres.ServeError(w, req, relPath, err) |
| return |
| } |
| var buf bytes.Buffer |
| if err := tmpl.Execute(&buf, data); err != nil { |
| log.Printf("executing template %s: %v", relPath, err) |
| pres.ServeError(w, req, relPath, err) |
| return |
| } |
| src = buf.Bytes() |
| |
| page.Body = src |
| pres.ServePage(w, page) |
| } |
| |
| // sortMajorReleases returns a sorted list of major Go releases, |
| // suitable to be displayed on the Go project page. |
| func sortMajorReleases(rs map[history.GoVer]history.Release) []MajorRelease { |
| var major []MajorRelease |
| for v, r := range rs { |
| if !v.IsMajor() { |
| continue |
| } |
| major = append(major, MajorRelease{ver: v, rel: r}) |
| } |
| sort.Slice(major, func(i, j int) bool { |
| if major[i].ver.X != major[j].ver.X { |
| return major[i].ver.X > major[j].ver.X |
| } |
| return major[i].ver.Y > major[j].ver.Y |
| }) |
| return major |
| } |
| |
| type projectTemplateData struct { |
| Major []MajorRelease |
| } |
| |
| // MajorRelease represents a major Go release entry as displayed on the Go project page. |
| type MajorRelease struct { |
| ver history.GoVer |
| rel history.Release |
| } |
| |
| // V returns the Go release version string, like "1.14", "1.14.1", "1.14.2", etc. |
| func (r MajorRelease) V() string { |
| return r.ver.String() |
| } |
| |
| // Date returns the date of the release, formatted for display on the Go project page. |
| func (r MajorRelease) Date() string { |
| d := r.rel.Date |
| return fmt.Sprintf("%s %d", d.Month, d.Year) |
| } |