github.com/april1989/origin-go-tools@v0.0.32/cmd/present/dir.go (about)

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"html/template"
     9  	"io"
    10  	"log"
    11  	"net"
    12  	"net/http"
    13  	"os"
    14  	"path/filepath"
    15  	"sort"
    16  	"strings"
    17  
    18  	"github.com/april1989/origin-go-tools/present"
    19  )
    20  
    21  func init() {
    22  	http.HandleFunc("/", dirHandler)
    23  }
    24  
    25  // dirHandler serves a directory listing for the requested path, rooted at *contentPath.
    26  func dirHandler(w http.ResponseWriter, r *http.Request) {
    27  	if r.URL.Path == "/favicon.ico" {
    28  		http.NotFound(w, r)
    29  		return
    30  	}
    31  	name := filepath.Join(*contentPath, r.URL.Path)
    32  	if isDoc(name) {
    33  		err := renderDoc(w, name)
    34  		if err != nil {
    35  			log.Println(err)
    36  			http.Error(w, err.Error(), http.StatusInternalServerError)
    37  		}
    38  		return
    39  	}
    40  	if isDir, err := dirList(w, name); err != nil {
    41  		addr, _, e := net.SplitHostPort(r.RemoteAddr)
    42  		if e != nil {
    43  			addr = r.RemoteAddr
    44  		}
    45  		log.Printf("request from %s: %s", addr, err)
    46  		http.Error(w, err.Error(), http.StatusInternalServerError)
    47  		return
    48  	} else if isDir {
    49  		return
    50  	}
    51  	http.FileServer(http.Dir(*contentPath)).ServeHTTP(w, r)
    52  }
    53  
    54  func isDoc(path string) bool {
    55  	_, ok := contentTemplate[filepath.Ext(path)]
    56  	return ok
    57  }
    58  
    59  var (
    60  	// dirListTemplate holds the front page template.
    61  	dirListTemplate *template.Template
    62  
    63  	// contentTemplate maps the presentable file extensions to the
    64  	// template to be executed.
    65  	contentTemplate map[string]*template.Template
    66  )
    67  
    68  func initTemplates(base string) error {
    69  	// Locate the template file.
    70  	actionTmpl := filepath.Join(base, "templates/action.tmpl")
    71  
    72  	contentTemplate = make(map[string]*template.Template)
    73  
    74  	for ext, contentTmpl := range map[string]string{
    75  		".slide":   "slides.tmpl",
    76  		".article": "article.tmpl",
    77  	} {
    78  		contentTmpl = filepath.Join(base, "templates", contentTmpl)
    79  
    80  		// Read and parse the input.
    81  		tmpl := present.Template()
    82  		tmpl = tmpl.Funcs(template.FuncMap{"playable": playable})
    83  		if _, err := tmpl.ParseFiles(actionTmpl, contentTmpl); err != nil {
    84  			return err
    85  		}
    86  		contentTemplate[ext] = tmpl
    87  	}
    88  
    89  	var err error
    90  	dirListTemplate, err = template.ParseFiles(filepath.Join(base, "templates/dir.tmpl"))
    91  	return err
    92  }
    93  
    94  // renderDoc reads the present file, gets its template representation,
    95  // and executes the template, sending output to w.
    96  func renderDoc(w io.Writer, docFile string) error {
    97  	// Read the input and build the doc structure.
    98  	doc, err := parse(docFile, 0)
    99  	if err != nil {
   100  		return err
   101  	}
   102  
   103  	// Find which template should be executed.
   104  	tmpl := contentTemplate[filepath.Ext(docFile)]
   105  
   106  	// Execute the template.
   107  	return doc.Render(w, tmpl)
   108  }
   109  
   110  func parse(name string, mode present.ParseMode) (*present.Doc, error) {
   111  	f, err := os.Open(name)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	defer f.Close()
   116  	return present.Parse(f, name, mode)
   117  }
   118  
   119  // dirList scans the given path and writes a directory listing to w.
   120  // It parses the first part of each .slide file it encounters to display the
   121  // presentation title in the listing.
   122  // If the given path is not a directory, it returns (isDir == false, err == nil)
   123  // and writes nothing to w.
   124  func dirList(w io.Writer, name string) (isDir bool, err error) {
   125  	f, err := os.Open(name)
   126  	if err != nil {
   127  		return false, err
   128  	}
   129  	defer f.Close()
   130  	fi, err := f.Stat()
   131  	if err != nil {
   132  		return false, err
   133  	}
   134  	if isDir = fi.IsDir(); !isDir {
   135  		return false, nil
   136  	}
   137  	fis, err := f.Readdir(0)
   138  	if err != nil {
   139  		return false, err
   140  	}
   141  	strippedPath := strings.TrimPrefix(name, filepath.Clean(*contentPath))
   142  	strippedPath = strings.TrimPrefix(strippedPath, "/")
   143  	d := &dirListData{Path: strippedPath}
   144  	for _, fi := range fis {
   145  		// skip the golang.org directory
   146  		if name == "." && fi.Name() == "golang.org" {
   147  			continue
   148  		}
   149  		e := dirEntry{
   150  			Name: fi.Name(),
   151  			Path: filepath.ToSlash(filepath.Join(strippedPath, fi.Name())),
   152  		}
   153  		if fi.IsDir() && showDir(e.Name) {
   154  			d.Dirs = append(d.Dirs, e)
   155  			continue
   156  		}
   157  		if isDoc(e.Name) {
   158  			fn := filepath.ToSlash(filepath.Join(name, fi.Name()))
   159  			if p, err := parse(fn, present.TitlesOnly); err != nil {
   160  				log.Printf("parse(%q, present.TitlesOnly): %v", fn, err)
   161  			} else {
   162  				e.Title = p.Title
   163  			}
   164  			switch filepath.Ext(e.Path) {
   165  			case ".article":
   166  				d.Articles = append(d.Articles, e)
   167  			case ".slide":
   168  				d.Slides = append(d.Slides, e)
   169  			}
   170  		} else if showFile(e.Name) {
   171  			d.Other = append(d.Other, e)
   172  		}
   173  	}
   174  	if d.Path == "." {
   175  		d.Path = ""
   176  	}
   177  	sort.Sort(d.Dirs)
   178  	sort.Sort(d.Slides)
   179  	sort.Sort(d.Articles)
   180  	sort.Sort(d.Other)
   181  	return true, dirListTemplate.Execute(w, d)
   182  }
   183  
   184  // showFile reports whether the given file should be displayed in the list.
   185  func showFile(n string) bool {
   186  	switch filepath.Ext(n) {
   187  	case ".pdf":
   188  	case ".html":
   189  	case ".go":
   190  	default:
   191  		return isDoc(n)
   192  	}
   193  	return true
   194  }
   195  
   196  // showDir reports whether the given directory should be displayed in the list.
   197  func showDir(n string) bool {
   198  	if len(n) > 0 && (n[0] == '.' || n[0] == '_') || n == "present" {
   199  		return false
   200  	}
   201  	return true
   202  }
   203  
   204  type dirListData struct {
   205  	Path                          string
   206  	Dirs, Slides, Articles, Other dirEntrySlice
   207  }
   208  
   209  type dirEntry struct {
   210  	Name, Path, Title string
   211  }
   212  
   213  type dirEntrySlice []dirEntry
   214  
   215  func (s dirEntrySlice) Len() int           { return len(s) }
   216  func (s dirEntrySlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   217  func (s dirEntrySlice) Less(i, j int) bool { return s[i].Name < s[j].Name }