mirror of
https://github.com/golang/go
synced 2024-11-05 22:46:12 -07:00
37fd46feae
Without changing the behavior of the present command for local usage (using the local socket for running examples, defaulting to the current directory for all content). Add flags and set them to the appropriate values if running on App Engine. Notably, since the Go files must be in the same directory as app.yaml, the content root must be ./content/ to avoid listing the present source files. It also defaults to running example snippets via the HTTPTransport (https://play.golang.org/compile) instead of locally when on App Engine. There are also some small cleanup code changes. Update golang/go#28080 Change-Id: I40bb7923107614f88d2bfdffd34a824d4bacb3a1 Reviewed-on: https://go-review.googlesource.com/c/140841 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
218 lines
5.3 KiB
Go
218 lines
5.3 KiB
Go
// Copyright 2012 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 (
|
|
"html/template"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"golang.org/x/tools/present"
|
|
)
|
|
|
|
func init() {
|
|
http.HandleFunc("/", dirHandler)
|
|
}
|
|
|
|
// dirHandler serves a directory listing for the requested path, rooted at *contentPath.
|
|
func dirHandler(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/favicon.ico" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
name := filepath.Join(*contentPath, r.URL.Path)
|
|
if isDoc(name) {
|
|
err := renderDoc(w, name)
|
|
if err != nil {
|
|
log.Println(err)
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
return
|
|
}
|
|
if isDir, err := dirList(w, name); err != nil {
|
|
addr, _, e := net.SplitHostPort(r.RemoteAddr)
|
|
if e != nil {
|
|
addr = r.RemoteAddr
|
|
}
|
|
log.Printf("request from %s: %s", addr, err)
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
} else if isDir {
|
|
return
|
|
}
|
|
http.FileServer(http.Dir(*contentPath)).ServeHTTP(w, r)
|
|
}
|
|
|
|
func isDoc(path string) bool {
|
|
_, ok := contentTemplate[filepath.Ext(path)]
|
|
return ok
|
|
}
|
|
|
|
var (
|
|
// dirListTemplate holds the front page template.
|
|
dirListTemplate *template.Template
|
|
|
|
// contentTemplate maps the presentable file extensions to the
|
|
// template to be executed.
|
|
contentTemplate map[string]*template.Template
|
|
)
|
|
|
|
func initTemplates(base string) error {
|
|
// Locate the template file.
|
|
actionTmpl := filepath.Join(base, "templates/action.tmpl")
|
|
|
|
contentTemplate = make(map[string]*template.Template)
|
|
|
|
for ext, contentTmpl := range map[string]string{
|
|
".slide": "slides.tmpl",
|
|
".article": "article.tmpl",
|
|
} {
|
|
contentTmpl = filepath.Join(base, "templates", contentTmpl)
|
|
|
|
// Read and parse the input.
|
|
tmpl := present.Template()
|
|
tmpl = tmpl.Funcs(template.FuncMap{"playable": playable})
|
|
if _, err := tmpl.ParseFiles(actionTmpl, contentTmpl); err != nil {
|
|
return err
|
|
}
|
|
contentTemplate[ext] = tmpl
|
|
}
|
|
|
|
var err error
|
|
dirListTemplate, err = template.ParseFiles(filepath.Join(base, "templates/dir.tmpl"))
|
|
return err
|
|
}
|
|
|
|
// renderDoc reads the present file, gets its template representation,
|
|
// and executes the template, sending output to w.
|
|
func renderDoc(w io.Writer, docFile string) error {
|
|
// Read the input and build the doc structure.
|
|
doc, err := parse(docFile, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Find which template should be executed.
|
|
tmpl := contentTemplate[filepath.Ext(docFile)]
|
|
|
|
// Execute the template.
|
|
return doc.Render(w, tmpl)
|
|
}
|
|
|
|
func parse(name string, mode present.ParseMode) (*present.Doc, error) {
|
|
f, err := os.Open(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
return present.Parse(f, name, mode)
|
|
}
|
|
|
|
// dirList scans the given path and writes a directory listing to w.
|
|
// It parses the first part of each .slide file it encounters to display the
|
|
// presentation title in the listing.
|
|
// If the given path is not a directory, it returns (isDir == false, err == nil)
|
|
// and writes nothing to w.
|
|
func dirList(w io.Writer, name string) (isDir bool, err error) {
|
|
f, err := os.Open(name)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer f.Close()
|
|
fi, err := f.Stat()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if isDir = fi.IsDir(); !isDir {
|
|
return false, nil
|
|
}
|
|
fis, err := f.Readdir(0)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
strippedPath := strings.TrimPrefix(name, filepath.Clean(*contentPath))
|
|
strippedPath = strings.TrimPrefix(strippedPath, "/")
|
|
d := &dirListData{Path: strippedPath}
|
|
for _, fi := range fis {
|
|
// skip the golang.org directory
|
|
if name == "." && fi.Name() == "golang.org" {
|
|
continue
|
|
}
|
|
e := dirEntry{
|
|
Name: fi.Name(),
|
|
Path: filepath.ToSlash(filepath.Join(strippedPath, fi.Name())),
|
|
}
|
|
if fi.IsDir() && showDir(e.Name) {
|
|
d.Dirs = append(d.Dirs, e)
|
|
continue
|
|
}
|
|
if isDoc(e.Name) {
|
|
fn := filepath.ToSlash(filepath.Join(name, fi.Name()))
|
|
if p, err := parse(fn, present.TitlesOnly); err != nil {
|
|
log.Printf("parse(%q, present.TitlesOnly): %v", fn, err)
|
|
} else {
|
|
e.Title = p.Title
|
|
}
|
|
switch filepath.Ext(e.Path) {
|
|
case ".article":
|
|
d.Articles = append(d.Articles, e)
|
|
case ".slide":
|
|
d.Slides = append(d.Slides, e)
|
|
}
|
|
} else if showFile(e.Name) {
|
|
d.Other = append(d.Other, e)
|
|
}
|
|
}
|
|
if d.Path == "." {
|
|
d.Path = ""
|
|
}
|
|
sort.Sort(d.Dirs)
|
|
sort.Sort(d.Slides)
|
|
sort.Sort(d.Articles)
|
|
sort.Sort(d.Other)
|
|
return true, dirListTemplate.Execute(w, d)
|
|
}
|
|
|
|
// showFile reports whether the given file should be displayed in the list.
|
|
func showFile(n string) bool {
|
|
switch filepath.Ext(n) {
|
|
case ".pdf":
|
|
case ".html":
|
|
case ".go":
|
|
default:
|
|
return isDoc(n)
|
|
}
|
|
return true
|
|
}
|
|
|
|
// showDir reports whether the given directory should be displayed in the list.
|
|
func showDir(n string) bool {
|
|
if len(n) > 0 && (n[0] == '.' || n[0] == '_') || n == "present" {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
type dirListData struct {
|
|
Path string
|
|
Dirs, Slides, Articles, Other dirEntrySlice
|
|
}
|
|
|
|
type dirEntry struct {
|
|
Name, Path, Title string
|
|
}
|
|
|
|
type dirEntrySlice []dirEntry
|
|
|
|
func (s dirEntrySlice) Len() int { return len(s) }
|
|
func (s dirEntrySlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
func (s dirEntrySlice) Less(i, j int) bool { return s[i].Name < s[j].Name }
|