Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Firt pass at supporting presentations.

  • Loading branch information...
commit d2364fcc0b1fa74a3786074232e377133f483989 1 parent 5b70929
@garyburd garyburd authored
View
5 README.markdown
@@ -23,8 +23,9 @@ Development Environment Setup
- Install [Go](http://golang.org/doc/install) and configure [GOPATH](http://golang.org/doc/code.html).
- Install and run the server:
- go get github.com/garyburd/gopkgdoc/gddo-server
- gddo-server
+ go get github.com/garyburd/gopkgdoc/gddo-server
+ cd `go list -f '{{.Dir}}' github.com/garyburd/gopkgdoc/gddo-server`
+ gddo-server
License
-------
View
52 doc/get.go
@@ -53,18 +53,19 @@ var (
// service represents a source code control service.
type service struct {
- pattern *regexp.Regexp
- getDoc func(*http.Client, map[string]string, string) (*Package, error)
- prefix string
+ pattern *regexp.Regexp
+ prefix string
+ get func(*http.Client, map[string]string, string) (*Package, error)
+ getPresentation func(*http.Client, map[string]string) (*Presentation, error)
}
// services is the list of source code control services handled by gopkgdoc.
var services = []*service{
- {githubPattern, getGithubDoc, "github.com/"},
- {googlePattern, getGoogleDoc, "code.google.com/"},
- {bitbucketPattern, getBitbucketDoc, "bitbucket.org/"},
- {launchpadPattern, getLaunchpadDoc, "launchpad.net/"},
- {generalPattern, getGeneralDoc, ""},
+ {githubPattern, "github.com/", getGithubDoc, getGithubPresentation},
+ {googlePattern, "code.google.com/", getGoogleDoc, getGooglePresentation},
+ {bitbucketPattern, "bitbucket.org/", getBitbucketDoc, nil},
+ {launchpadPattern, "launchpad.net/", getLaunchpadDoc, nil},
+ {generalPattern, "", getGeneralDoc, nil},
}
func attrValue(attrs []xml.Attr, name string) string {
@@ -199,7 +200,7 @@ func getGeneralDoc(client *http.Client, match map[string]string, etag string) (*
// returns errNoMatch if the import path is not recognized.
func getStatic(client *http.Client, importPath string, etag string) (*Package, error) {
for _, s := range services {
- if !strings.HasPrefix(importPath, s.prefix) {
+ if s.get == nil || !strings.HasPrefix(importPath, s.prefix) {
continue
}
m := s.pattern.FindStringSubmatch(importPath)
@@ -215,7 +216,7 @@ func getStatic(client *http.Client, importPath string, etag string) (*Package, e
match[n] = m[i]
}
}
- return s.getDoc(client, match, etag)
+ return s.get(client, match, etag)
}
return nil, errNoMatch
}
@@ -252,3 +253,34 @@ func Get(client *http.Client, importPath string, etag string) (pdoc *Package, er
return pdoc, err
}
+
+// GetPresentation gets a presentation from the the given path.
+func GetPresentation(client *http.Client, importPath string) (*Presentation, error) {
+ ext := path.Ext(importPath)
+ if ext != ".slide" && ext != ".article" {
+ return nil, NotFoundError{"unknown file extension."}
+ }
+
+ importPath, file := path.Split(importPath)
+ importPath = strings.TrimSuffix(importPath, "/")
+ for _, s := range services {
+ if s.getPresentation == nil || !strings.HasPrefix(importPath, s.prefix) {
+ continue
+ }
+ m := s.pattern.FindStringSubmatch(importPath)
+ if m == nil {
+ if s.prefix != "" {
+ return nil, NotFoundError{"path prefix matches known service, but regexp does not."}
+ }
+ continue
+ }
+ match := map[string]string{"importPath": importPath, "file": file}
+ for i, n := range s.pattern.SubexpNames() {
+ if n != "" {
+ match[n] = m[i]
+ }
+ }
+ return s.getPresentation(client, match)
+ }
+ return nil, errNoMatch
+}
View
44 doc/github.go
@@ -15,7 +15,9 @@
package doc
import (
+ "io"
"net/http"
+ "net/url"
"path"
"regexp"
"strings"
@@ -136,3 +138,45 @@ func getGithubDoc(client *http.Client, match map[string]string, savedEtag string
return b.build(files)
}
+
+func getGithubPresentation(client *http.Client, match map[string]string) (*Presentation, error) {
+
+ match["cred"] = githubCred
+
+ p, err := httpGet(client, expand("https://api.github.com/repos/{owner}/{repo}/contents{dir}/{file}?{cred}", match), githubRawHeader)
+ if err != nil {
+ return nil, err
+ }
+ defer p.Close()
+
+ apiBase, err := url.Parse(expand("https://api.github.com/repos/{owner}/{repo}/contents{dir}/?{cred}", match))
+ if err != nil {
+ return nil, err
+ }
+ rawBase, err := url.Parse(expand("https://raw.github.com/{owner}/{repo}/master{dir}/", match))
+ if err != nil {
+ return nil, err
+ }
+
+ b := &presBuilder{
+ pres: &Presentation{},
+ content: p,
+ openFile: func(fname string) (io.ReadCloser, error) {
+ u, err := apiBase.Parse(fname)
+ if err != nil {
+ return nil, err
+ }
+ u.RawQuery = match["cred"]
+ return httpGet(client, u.String(), githubRawHeader)
+ },
+ resolveURL: func(fname string) string {
+ u, err := rawBase.Parse(fname)
+ if err != nil {
+ return "/-/notfound"
+ }
+ return u.String()
+ },
+ }
+
+ return b.build()
+}
View
88 doc/google.go
@@ -16,7 +16,9 @@ package doc
import (
"errors"
+ "io"
"net/http"
+ "net/url"
"regexp"
"strings"
)
@@ -30,28 +32,11 @@ var (
)
func getGoogleDoc(client *http.Client, match map[string]string, savedEtag string) (*Package, error) {
-
- if s := match["subrepo"]; s != "" {
- match["dot"] = "."
- match["query"] = "?repo=" + s
- } else {
- match["dot"] = ""
- match["query"] = ""
- }
-
+ setupGoogleMatch(match)
if m := googleEtagRe.FindStringSubmatch(savedEtag); m != nil {
match["vcs"] = m[1]
- } else {
- // Scrape the HTML project page to find the VCS.
- p, err := httpGetBytes(client, expand("http://code.google.com/p/{repo}/source/checkout", match))
- if err != nil {
- return nil, err
- }
- if m := googleRepoRe.FindSubmatch(p); m != nil {
- match["vcs"] = string(m[1])
- } else {
- return nil, NotFoundError{"Could not VCS on Google Code project page."}
- }
+ } else if err := getGoogleVCS(client, match); err != nil {
+ return nil, err
}
// Scrape the repo browser to find the project revision and individual Go files.
@@ -102,6 +87,30 @@ func getGoogleDoc(client *http.Client, match map[string]string, savedEtag string
return b.build(files)
}
+func setupGoogleMatch(match map[string]string) {
+ if s := match["subrepo"]; s != "" {
+ match["dot"] = "."
+ match["query"] = "?repo=" + s
+ } else {
+ match["dot"] = ""
+ match["query"] = ""
+ }
+}
+
+func getGoogleVCS(client *http.Client, match map[string]string) error {
+ // Scrape the HTML project page to find the VCS.
+ p, err := httpGetBytes(client, expand("http://code.google.com/p/{repo}/source/checkout", match))
+ if err != nil {
+ return err
+ }
+ m := googleRepoRe.FindSubmatch(p)
+ if m == nil {
+ return NotFoundError{"Could not VCS on Google Code project page."}
+ }
+ match["vcs"] = string(m[1])
+ return nil
+}
+
func getStandardDoc(client *http.Client, importPath string, savedEtag string) (*Package, error) {
p, err := httpGetBytes(client, "http://go.googlecode.com/hg-history/release/src/pkg/"+importPath+"/")
@@ -150,3 +159,42 @@ func getStandardDoc(client *http.Client, importPath string, savedEtag string) (*
return b.build(files)
}
+
+func getGooglePresentation(client *http.Client, match map[string]string) (*Presentation, error) {
+ setupGoogleMatch(match)
+ if err := getGoogleVCS(client, match); err != nil {
+ return nil, err
+ }
+
+ rawBase, err := url.Parse(expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/", match))
+ if err != nil {
+ return nil, err
+ }
+
+ p, err := httpGet(client, expand("http://{subrepo}{dot}{repo}.googlecode.com/{vcs}{dir}/{file}", match), nil)
+ if err != nil {
+ return nil, err
+ }
+ defer p.Close()
+
+ b := &presBuilder{
+ pres: &Presentation{},
+ content: p,
+ openFile: func(fname string) (io.ReadCloser, error) {
+ u, err := rawBase.Parse(fname)
+ if err != nil {
+ return nil, err
+ }
+ return httpGet(client, u.String(), nil)
+ },
+ resolveURL: func(fname string) string {
+ u, err := rawBase.Parse(fname)
+ if err != nil {
+ return "/-/notfound"
+ }
+ return u.String()
+ },
+ }
+
+ return b.build()
+}
View
2  doc/launchpad.go
@@ -31,7 +31,7 @@ var launchpadPattern = regexp.MustCompile(`^launchpad\.net/(?P<repo>(?P<project>
func getLaunchpadDoc(client *http.Client, match map[string]string, savedEtag string) (*Package, error) {
if match["project"] != "" && match["series"] != "" {
- rc, err := httpGet(client, expand("https://code.launchpad.net/{project}{series}/.bzr/branch-format", match))
+ rc, err := httpGet(client, expand("https://code.launchpad.net/{project}{series}/.bzr/branch-format", match), nil)
switch {
case err == nil:
rc.Close()
View
72 doc/present.go
@@ -0,0 +1,72 @@
+// Copyright 2013 Gary Burd
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package doc
+
+import (
+ "io"
+ "time"
+
+ "code.google.com/p/go.talks/pkg/present"
+)
+
+type Presentation struct {
+ Doc *present.Doc
+ Updated time.Time
+}
+
+type presBuilder struct {
+ pres *Presentation
+ filename string
+ content io.Reader
+ openFile func(fname string) (io.ReadCloser, error)
+ resolveURL func(fname string) string
+}
+
+func (b *presBuilder) resolveElem(e present.Elem) present.Elem {
+ switch e := e.(type) {
+ case present.Section:
+ for i := range e.Elem {
+ e.Elem[i] = b.resolveElem(e.Elem[i])
+ }
+ return e
+ case present.Image:
+ e.URL = b.resolveURL(e.URL)
+ return e
+ case present.Iframe:
+ e.URL = b.resolveURL(e.URL)
+ return e
+ }
+ return e
+}
+
+func (b *presBuilder) build() (*Presentation, error) {
+ ctxt := &present.Context{
+ OpenFile: b.openFile,
+ }
+
+ var err error
+ b.pres.Doc, err = present.Parse(ctxt, b.content, b.filename, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ for i := range b.pres.Doc.Sections {
+ b.pres.Doc.Sections[i] = b.resolveElem(b.pres.Doc.Sections[i]).(present.Section)
+ }
+
+ b.pres.Updated = time.Now().UTC()
+
+ return b.pres, nil
+}
View
28 doc/print.go
@@ -34,8 +34,9 @@ func indent(s string, n int) string {
}
var (
- etag = flag.String("etag", "", "Etag")
- local = flag.Bool("local", false, "Get package from local directory.")
+ etag = flag.String("etag", "", "Etag")
+ local = flag.Bool("local", false, "Get package from local directory.")
+ present = flag.Bool("present", false, "Get presentation.")
)
func main() {
@@ -43,14 +44,22 @@ func main() {
if len(flag.Args()) != 1 {
log.Fatal("Usage: go run print.go importPath")
}
+ if *present {
+ printPresentation(flag.Args()[0])
+ } else {
+ printPackage(flag.Args()[0])
+ }
+}
+
+func printPackage(path string) {
var (
pdoc *doc.Package
err error
)
if *local {
- pdoc, err = doc.GetDir(flag.Args()[0])
+ pdoc, err = doc.GetDir(path)
} else {
- pdoc, err = doc.Get(http.DefaultClient, flag.Args()[0], *etag)
+ pdoc, err = doc.Get(http.DefaultClient, path, *etag)
}
if err != nil {
log.Fatal(err)
@@ -132,3 +141,14 @@ func main() {
}
}
}
+
+func printPresentation(path string) {
+ pres, err := doc.GetPresentation(http.DefaultClient, path)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println("Doc: ", pres.Doc)
+ fmt.Println("Kind: ", pres.Kind)
+ fmt.Println("Name: ", pres.Name)
+ fmt.Println("URL: ", pres.URL)
+}
View
13 doc/util.go
@@ -115,11 +115,14 @@ func fetchFiles(client *http.Client, files []*source, header http.Header) error
// httpGet gets the specified resource. ErrNotFound is returned if the
// server responds with status 404.
-func httpGet(client *http.Client, url string) (io.ReadCloser, error) {
+func httpGet(client *http.Client, url string, header http.Header) (io.ReadCloser, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
+ for k, vs := range header {
+ req.Header[k] = vs
+ }
resp, err := client.Do(req)
if err != nil {
return nil, &RemoteError{req.URL.Host, err}
@@ -137,7 +140,7 @@ func httpGet(client *http.Client, url string) (io.ReadCloser, error) {
}
func httpGetJSON(client *http.Client, url string, v interface{}) error {
- rc, err := httpGet(client, url)
+ rc, err := httpGet(client, url, nil)
if err != nil {
return err
}
@@ -149,10 +152,10 @@ func httpGetJSON(client *http.Client, url string, v interface{}) error {
return err
}
-// httpGet gets the specified resource. ErrNotFound is returned if the
-// server responds with status 404.
+// httpGet gets the specified resource. ErrNotFound is returned if the server
+// responds with status 404.
func httpGetBytes(client *http.Client, url string) ([]byte, error) {
- rc, err := httpGet(client, url)
+ rc, err := httpGet(client, url, nil)
if err != nil {
return nil, err
}
View
73 gddo-server/main.go
@@ -21,6 +21,7 @@ import (
"errors"
"flag"
"fmt"
+ "go/build"
"io"
"io/ioutil"
"log"
@@ -31,6 +32,7 @@ import (
"regexp"
"runtime/debug"
"strings"
+ "sync"
"time"
"github.com/garyburd/gopkgdoc/database"
@@ -119,8 +121,7 @@ func isRobot(req *web.Request) bool {
}
func servePackage(resp web.Response, req *web.Request) error {
- p := path.Clean(req.URL.Path)
- if p != req.URL.Path {
+ if p := path.Clean(req.URL.Path); p != req.URL.Path {
return web.Redirect(resp, req, p, 301, nil)
}
@@ -270,7 +271,7 @@ func serveAbout(resp web.Response, req *web.Request) error {
return executeTemplate(resp, "about.html", 200, map[string]interface{}{"Host": req.URL.Host})
}
-func handleError(resp web.Response, req *web.Request, status int, err error, r interface{}) {
+func logError(req *web.Request, err error, r interface{}) {
if err != nil {
var buf bytes.Buffer
fmt.Fprintf(&buf, "Error serving %s: %v\n", req.URL, err)
@@ -280,6 +281,10 @@ func handleError(resp web.Response, req *web.Request, status int, err error, r i
}
log.Print(buf.String())
}
+}
+
+func handleError(resp web.Response, req *web.Request, status int, err error, r interface{}) {
+ logError(req, err, r)
switch status {
case 0:
// nothing to do
@@ -298,10 +303,47 @@ func handleError(resp web.Response, req *web.Request, status int, err error, r i
}
var (
+ presMu sync.Mutex
+ presentations = map[string]*doc.Presentation{}
+)
+
+func servePresentation(resp web.Response, req *web.Request) error {
+ if p := path.Clean(req.URL.Path); p != req.URL.Path {
+ return web.Redirect(resp, req, p, 301, nil)
+ }
+ p := req.RouteVars["path"]
+ presMu.Lock()
+ pres := presentations[p]
+ presMu.Unlock()
+
+ if pres == nil || time.Since(pres.Updated) > 30*time.Minute {
+ var err error
+ log.Println("Fetch presentation ", p)
+ pres, err = doc.GetPresentation(httpClient, p)
+ if err != nil {
+ return err
+ }
+ presMu.Lock()
+ presentations[p] = pres
+ presMu.Unlock()
+ }
+
+ return executeTemplate(resp, path.Ext(p)[1:]+".html", 200, pres)
+}
+
+func defaultBase(path string) string {
+ p, err := build.Default.Import(path, "", build.FindOnly)
+ if err != nil {
+ return "."
+ }
+ return p.Dir
+}
+
+var (
db *database.Database
robot = flag.Bool("robot", false, "Robot mode")
- templateDir = flag.String("template", "template", "Template directory.")
- staticDir = flag.String("static", "static", "Static file directory.")
+ baseDir = flag.String("base", defaultBase("github.com/garyburd/gopkgdoc/gddo-server"), "Base directory for templates and static files.")
+ presentBaseDir = flag.String("presentBase", defaultBase("code.google.com/p/go.talks/present"), "Base directory for templates and static files.")
getTimeout = flag.Duration("get_timeout", 8*time.Second, "Time to wait for package update from the VCS.")
firstGetTimeout = flag.Duration("first_get_timeout", 5*time.Second, "Time to wait for first fetch of package from the VCS.")
maxAge = flag.Duration("max_age", 24*time.Hour, "Crawl package documents older than this age.")
@@ -376,6 +418,13 @@ func main() {
log.Fatal(err)
}
+ if err := parsePresentTemplates([][]string{
+ {"article.html", "presentCommon.html"},
+ {"slide.html", "presentCommon.html"},
+ }); err != nil {
+ log.Fatal(err)
+ }
+
var err error
db, err = database.New()
if err != nil {
@@ -402,16 +451,18 @@ func main() {
r.Add("/-/go").GetFunc(serveGoIndex)
r.Add("/-/index").GetFunc(serveIndex)
r.Add("/-/refresh").PostFunc(serveRefresh)
- r.Add("/-/static/<path:.*>").Get(web.DirectoryHandler(*staticDir, sfo))
- r.Add("/robots.txt").Get(web.FileHandler(filepath.Join(*staticDir, "robots.txt"), nil))
- r.Add("/humans.txt").Get(web.FileHandler(filepath.Join(*staticDir, "humans.txt"), nil))
- r.Add("/favicon.ico").Get(web.FileHandler(filepath.Join(*staticDir, "favicon.ico"), nil))
- r.Add("/google3d2f3cd4cc2bb44b.html").Get(web.FileHandler(filepath.Join(*staticDir, "google3d2f3cd4cc2bb44b.html"), nil))
+ r.Add("/-/static/<path:.*>").Get(web.DirectoryHandler(filepath.Join(*baseDir, "static"), sfo))
+ r.Add("/-/talk/<path:.+>").GetFunc(servePresentation)
+ r.Add("/robots.txt").Get(web.FileHandler(filepath.Join(*baseDir, "static", "robots.txt"), nil))
+ r.Add("/humans.txt").Get(web.FileHandler(filepath.Join(*baseDir, "static", "humans.txt"), nil))
+ r.Add("/favicon.ico").Get(web.FileHandler(filepath.Join(*baseDir, "static", "favicon.ico"), nil))
+ r.Add("/google3d2f3cd4cc2bb44b.html").Get(web.FileHandler(filepath.Join(*baseDir, "static", "google3d2f3cd4cc2bb44b.html"), nil))
r.Add("/about").Get(web.RedirectHandler("/-/about", 301))
r.Add("/a/index").Get(web.RedirectHandler("/-/index", 301))
r.Add("/C").Get(web.RedirectHandler("http://golang.org/doc/articles/c_go_cgo.html", 301))
- r.Add("/<path:.*>").GetFunc(servePackage)
+ r.Add("/<path:.+>").GetFunc(servePackage)
h := web.ErrorHandler(handleError, web.FormAndCookieHandler(1000, false, r))
+
listener, err := net.Listen("tcp", *httpAddr)
if err != nil {
log.Fatal("Listen", err)
View
65 gddo-server/template.go
@@ -38,6 +38,8 @@ import (
ttemp "text/template"
"time"
+ "code.google.com/p/go.talks/pkg/present"
+
"github.com/garyburd/gopkgdoc/doc"
"github.com/garyburd/indigo/web"
)
@@ -58,7 +60,7 @@ func staticFileFn(p string) htemp.URL {
staticMutex.RUnlock()
if !ok {
- fp := filepath.Join(*staticDir, filepath.FromSlash(p))
+ fp := filepath.Join(*baseDir, "static", filepath.FromSlash(p))
b, err := ioutil.ReadFile(fp)
if err != nil {
log.Printf("WARNING could not read static file %s", fp)
@@ -362,6 +364,18 @@ func gaAccountFn() string {
return secrets.GAAccount
}
+func elemFn(t *htemp.Template, e present.Elem) (htemp.HTML, error) {
+ var buf bytes.Buffer
+ if err := t.ExecuteTemplate(&buf, e.TemplateName(), e); err != nil {
+ return "", err
+ }
+ return htemp.HTML(buf.Bytes()), nil
+}
+
+func headerLevelFn(e present.Section) int {
+ return len(e.Number) + 1
+}
+
var contentTypes = map[string]string{
".html": "text/html; charset=utf-8",
".txt": "text/plain; charset=utf-8",
@@ -384,6 +398,14 @@ var templates = map[string]interface {
Execute(io.Writer, interface{}) error
}{}
+func joinTemplateDir(files []string) []string {
+ result := make([]string, len(files))
+ for i := range files {
+ result[i] = filepath.Join(*baseDir, "template", files[i])
+ }
+ return result
+}
+
func parseHTMLTemplates(sets [][]string) error {
for _, set := range sets {
templateName := set[0]
@@ -409,41 +431,54 @@ func parseHTMLTemplates(sets [][]string) error {
"staticFile": staticFileFn,
"templateName": func() string { return templateName },
})
- var files []string
- for _, n := range set {
- files = append(files, filepath.Join(*templateDir, n))
- }
- if _, err := t.ParseFiles(files...); err != nil {
+ if _, err := t.ParseFiles(joinTemplateDir(set)...); err != nil {
return err
}
t = t.Lookup("ROOT")
if t == nil {
- return fmt.Errorf("ROOT template not found in %v", files)
+ return fmt.Errorf("ROOT template not found in %v", set)
}
- templates[templateName] = t
+ templates[set[0]] = t
}
return nil
}
func parseTextTemplates(sets [][]string) error {
for _, set := range sets {
- templateName := set[0]
t := ttemp.New("")
t.Funcs(ttemp.FuncMap{
"comment": commentTextFn,
})
- var files []string
- for _, n := range set {
- files = append(files, filepath.Join(*templateDir, n))
+ if _, err := t.ParseFiles(joinTemplateDir(set)...); err != nil {
+ return err
+ }
+ t = t.Lookup("ROOT")
+ if t == nil {
+ return fmt.Errorf("ROOT template not found in %v", set)
}
- if _, err := t.ParseFiles(files...); err != nil {
+ templates[set[0]] = t
+ }
+ return nil
+}
+
+func parsePresentTemplates(sets [][]string) error {
+ for _, set := range sets {
+ t := present.Template()
+ t.Funcs(htemp.FuncMap{
+ "gaAccount": gaAccountFn,
+ "staticFile": staticFileFn,
+ "elem": func(e present.Elem) (htemp.HTML, error) { return elemFn(t, e) },
+ "headerLevel": headerLevelFn,
+ "relativeTime": relativeTime,
+ })
+ if _, err := t.ParseFiles(joinTemplateDir(set)...); err != nil {
return err
}
t = t.Lookup("ROOT")
if t == nil {
- return fmt.Errorf("ROOT template not found in %v", files)
+ return fmt.Errorf("ROOT template not found in %v", set)
}
- templates[templateName] = t
+ templates[set[0]] = t
}
return nil
}
View
2  gddo-server/template/about.html
@@ -51,7 +51,7 @@ <h4 id="howto">Add a package to GoDoc</h4>
<h4>Plain Text</h4>
-GoDoc provides plain text output for integration with command line tools. Use the
+GoDoc provides plain text output for integration with shell scripts. Use the
HTTP Accept header to request a plain text response.
<p>Search for packages with the term 'sql':
View
21 gddo-server/template/article.html
@@ -0,0 +1,21 @@
+{{define "Head"}}<title>{{.Doc.Title}}</title>{{end}}
+
+{{define "Body"}}
+<h1>{{.Doc.Title}}</h1>{{with .Doc.Subtitle}}<p class="lead">{{.}}{{end}}
+
+{{range .Doc.Sections}}
+ {{elem .}}
+{{end}}
+
+<h2>Authors</h2>
+{{range .Doc.Authors}}
+ <div class="author">
+ {{range .Elem}}{{elem .}}{{end}}
+ </div>
+{{end}}
+
+{{end}}
+
+{{define "newline"}}
+{{/* No automatic line break. Paragraphs are free-form. */}}
+{{end}}
View
4 gddo-server/template/common.html
@@ -52,11 +52,11 @@
{{if .pdoc.ProjectRoot}}<a href="{{.pdoc.ProjectURL}}"><strong>{{.pdoc.ProjectName|html}}:</strong></a>{{else}}<a href="/-/go">Go:</a>{{end}}
{{breadcrumbs .pdoc (templateName)}}
{{if and .pdoc.Name (equal templateName "pkg.html")}}
- <div class="pull-right">
+ <span class="pull-right">
<a href="#_index">Index</a>
<span class="muted">|</span> <a href="#_files">Files</a>
{{if .pkgs}}<span class="muted">|</span> <a href="#_subdirs">Directories</a>{{end}}
- </div>
+ </span>
{{end}}
</div>{{end}}
View
64 gddo-server/template/presentCommon.html
@@ -0,0 +1,64 @@
+{{define "ROOT"}}<!DOCTYPE html>
+<meta charset="utf-8">
+<link href="{{staticFile "css/bootstrap.css"}}" rel="stylesheet">
+<link rel="shortcut icon" href="{{staticFile "favicon.ico"}}">
+{{template "Head" $}}
+<body>
+ <div class="container">
+ {{template "Body" $}}
+ </div>
+</body>
+{{with gaAccount}}<script type="text/javascript">
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', '{{.}}']);
+ _gaq.push(['_trackPageview']);
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+</script>{{end}}
+</html>
+{{end}}
+
+{{define "section"}}
+ <h{{headerLevel .}} id="TOC_{{.FormattedNumber}}">{{.Title}}</h{{headerLevel .}}>
+ {{range .Elem}}{{elem .}}{{end}}
+{{end}}
+
+{{define "list"}}
+ <ul>
+ {{range .Bullet}}
+ <li>{{style .}}</li>
+ {{end}}
+ </ul>
+{{end}}
+
+{{define "text"}}
+ {{if .Pre}}
+ <div class="code"><pre>{{range .Lines}}{{.}}{{end}}</pre></div>
+ {{else}}
+ <p>
+ {{range $i, $l := .Lines}}{{if $i}}{{template "newline"}}
+ {{end}}{{style $l}}{{end}}
+ </p>
+ {{end}}
+{{end}}
+
+{{define "code"}}
+ <div class="code{{if .Play}} playground{{end}}" contenteditable="true" spellcheck="false">{{.Text}}</div>
+{{end}}
+
+{{define "image"}}
+<div class="image">
+ <img src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}}>
+</div>
+{{end}}
+
+{{define "iframe"}}
+<iframe src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}}></iframe>
+{{end}}
+
+{{define "link"}}<p class="link"><a href="{{.URL}}" target="_blank">{{style .Label}}</a></p>{{end}}
+
+{{define "html"}}HTML not supported{{end}}
View
46 gddo-server/template/slide.html
@@ -0,0 +1,46 @@
+{{define "Head"}}<title>{{.Doc.Title}}</title>{{end}}
+
+{{define "Body"}}{{with .Doc}}
+
+ <a class="carousel-control left" href="#myCarousel" data-slide="prev">&lsaquo;</a>
+ <a class="carousel-control right" href="#myCarousel" data-slide="next">&rsaquo;</a>
+
+<div id="myCarousel" class="carousel slide" data-interval="">
+ <div class="carousel-inner">
+
+ <div class="active item">
+ <h1>{{.Title}}</h1>
+ {{with .Subtitle}}<h3>{{.}}</h3>{{end}}
+ {{if not .Time.IsZero}}<h3>{{.Time.Format "2 January 2006"}}</h3>{{end}}
+ {{range .Authors}}
+ <div class="presenter">
+ {{range .TextElem}}{{elem .}}{{end}}
+ </div>{{end}}
+ </div>
+
+ {{range $i, $s := .Sections}}
+ <div class="item">
+ {{if $s.Elem}}
+ <h3>{{$s.Title}}</h3>
+ {{range $s.Elem}}{{elem .}}{{end}}
+ {{else}}
+ <h2>{{$s.Title}}</h2>
+ {{end}}
+ </div>
+ {{end}}
+
+ <div class="item">
+ <h3>Thank you</h1>
+ {{range .Authors}}<div class="presenter">{{range .Elem}}{{elem .}}{{end}}</div>{{end}}
+ </div>
+
+ </div>
+</div>
+{{end}}
+<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
+<script src="{{staticFile "bootstrap.min.js"}}"></script>
+{{end}}
+
+{{define "newline"}}
+<br>
+{{end}}

3 comments on commit d2364fc

@AlekSi

It's awesome!

@AlekSi

But it doesn't complies. :)

# github.com/garyburd/gopkgdoc/doc
../doc/get.go:267: undefined: strings.TrimSuffix
../doc/present.go:55: undefined: present.Context
../doc/present.go:60: cannot use b.content (type io.Reader) as type string in function argument
../doc/present.go:60: cannot use b.filename (type string) as type present.ParseMode in function argument
../doc/present.go:60: too many arguments in call to present.Parse
@garyburd

Oops, I used a new feature in tip (strings.TrimSuffix). The error on present.go:60 will be taken care of when I push my changes to go.talks upstream.

Please sign in to comment.
Something went wrong with that request. Please try again.