diff --git a/docs/layouts/chrome/menu.html b/docs/layouts/chrome/menu.html index 6e9fd08a0..6ea0c37c0 100644 --- a/docs/layouts/chrome/menu.html +++ b/docs/layouts/chrome/menu.html @@ -1,28 +1,28 @@ +
  • Release Notes
  • +
  • Roadmap
  • +
  • Contributing
  • +
  • Contributors
  • +
  • License
  • + \ No newline at end of file diff --git a/hugolib/config.go b/hugolib/config.go index 983aa254f..9c2cc6892 100644 --- a/hugolib/config.go +++ b/hugolib/config.go @@ -14,11 +14,11 @@ package hugolib import ( - "launchpad.net/goyaml" - "github.com/BurntSushi/toml" "encoding/json" "fmt" + "github.com/BurntSushi/toml" "io/ioutil" + "launchpad.net/goyaml" "os" "path" "path/filepath" @@ -29,11 +29,11 @@ import ( type Config struct { SourceDir, PublishDir, BaseUrl, StaticDir string Path, CacheDir, LayoutDir, DefaultLayout string - ConfigFile string - Title string + ConfigFile string + Title string Indexes map[string]string // singular, plural ProcessFilters map[string][]string - BuildDrafts bool + BuildDrafts, UglyUrls, Verbose bool } var c Config @@ -42,8 +42,8 @@ var c Config func SetupConfig(cfgfile *string, path *string) *Config { c.setPath(*path) - cfg , err := c.findConfigFile(*cfgfile) - c.ConfigFile = cfg + cfg, err := c.findConfigFile(*cfgfile) + c.ConfigFile = cfg if err != nil { fmt.Printf("%v", err) @@ -57,8 +57,10 @@ func SetupConfig(cfgfile *string, path *string) *Config { c.StaticDir = "static" c.DefaultLayout = "post" c.BuildDrafts = false + c.UglyUrls = false + c.Verbose = false - c.readInConfig() + c.readInConfig() // set index defaults if none provided if len(c.Indexes) == 0 { @@ -67,36 +69,36 @@ func SetupConfig(cfgfile *string, path *string) *Config { c.Indexes["category"] = "categories" } - if !strings.HasSuffix(c.BaseUrl, "/") { - c.BaseUrl = c.BaseUrl + "/" - } + if !strings.HasSuffix(c.BaseUrl, "/") { + c.BaseUrl = c.BaseUrl + "/" + } return &c } func (c *Config) readInConfig() { - file, err := ioutil.ReadFile(c.ConfigFile) - if err == nil { - switch path.Ext(c.ConfigFile) { - case ".yaml": - if err := goyaml.Unmarshal(file, &c); err != nil { - fmt.Printf("Error parsing config: %s", err) - os.Exit(1) - } + file, err := ioutil.ReadFile(c.ConfigFile) + if err == nil { + switch path.Ext(c.ConfigFile) { + case ".yaml": + if err := goyaml.Unmarshal(file, &c); err != nil { + fmt.Printf("Error parsing config: %s", err) + os.Exit(1) + } - case ".json": - if err := json.Unmarshal(file, &c); err != nil { - fmt.Printf("Error parsing config: %s", err) - os.Exit(1) - } + case ".json": + if err := json.Unmarshal(file, &c); err != nil { + fmt.Printf("Error parsing config: %s", err) + os.Exit(1) + } - case ".toml": - if _, err := toml.Decode(string(file), &c); err != nil { - fmt.Printf("Error parsing config: %s", err) - os.Exit(1) - } - } - } + case ".toml": + if _, err := toml.Decode(string(file), &c); err != nil { + fmt.Printf("Error parsing config: %s", err) + os.Exit(1) + } + } + } } func (c *Config) setPath(p string) { @@ -156,33 +158,33 @@ func (c *Config) GetAbsPath(name string) string { func (c *Config) findConfigFile(configFileName string) (string, error) { - if configFileName == "" { // config not specified, let's search - if b, _ := exists(c.GetAbsPath("config.json")); b { - return c.GetAbsPath("config.json"), nil - } + if configFileName == "" { // config not specified, let's search + if b, _ := exists(c.GetAbsPath("config.json")); b { + return c.GetAbsPath("config.json"), nil + } - if b, _ := exists(c.GetAbsPath("config.toml")); b { - return c.GetAbsPath("config.toml"), nil - } + if b, _ := exists(c.GetAbsPath("config.toml")); b { + return c.GetAbsPath("config.toml"), nil + } - if b, _ := exists(c.GetAbsPath("config.yaml")); b { - return c.GetAbsPath("config.yaml"), nil - } + if b, _ := exists(c.GetAbsPath("config.yaml")); b { + return c.GetAbsPath("config.yaml"), nil + } - return "", fmt.Errorf("config file not found in: %s", c.GetPath()) + return "", fmt.Errorf("config file not found in: %s", c.GetPath()) - } else { - // If the full path is given, just use that - if path.IsAbs(configFileName) { - return configFileName, nil - } + } else { + // If the full path is given, just use that + if path.IsAbs(configFileName) { + return configFileName, nil + } - // Else check the local directory - t := c.GetAbsPath(configFileName) - if b, _ := exists(t); b { - return t, nil - } else { - return "", fmt.Errorf("config file not found at: %s", t) - } - } + // Else check the local directory + t := c.GetAbsPath(configFileName) + if b, _ := exists(t); b { + return t, nil + } else { + return "", fmt.Errorf("config file not found at: %s", t) + } + } } diff --git a/hugolib/page.go b/hugolib/page.go index 80aef1d30..7887ca488 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -150,11 +150,11 @@ func (page *Page) parseYamlMetaData(data []byte) ([]string, error) { var err error datum, lines := splitPageContent(data, "---", "---") - d, err := page.handleYamlMetaData([]byte(strings.Join(datum, "\n"))) + d, err := page.handleYamlMetaData([]byte(strings.Join(datum, "\n"))) - if err != nil { - return lines, err - } + if err != nil { + return lines, err + } err = page.handleMetaData(d) return lines, err @@ -164,11 +164,11 @@ func (page *Page) parseTomlMetaData(data []byte) ([]string, error) { var err error datum, lines := splitPageContent(data, "+++", "+++") - d, err := page.handleTomlMetaData([]byte(strings.Join(datum, "\n"))) + d, err := page.handleTomlMetaData([]byte(strings.Join(datum, "\n"))) - if err != nil { - return lines, err - } + if err != nil { + return lines, err + } err = page.handleMetaData(d) return lines, err @@ -178,11 +178,11 @@ func (page *Page) parseJsonMetaData(data []byte) ([]string, error) { var err error datum, lines := splitPageContent(data, "{", "}") - d, err := page.handleJsonMetaData([]byte(strings.Join(datum, "\n"))) + d, err := page.handleJsonMetaData([]byte(strings.Join(datum, "\n"))) - if err != nil { - return lines, err - } + if err != nil { + return lines, err + } err = page.handleMetaData(d) return lines, err @@ -254,7 +254,7 @@ func (page *Page) handleYamlMetaData(datum []byte) (interface{}, error) { return m, nil } -func (page *Page) handleJsonMetaData(datum []byte) ( interface{}, error ) { +func (page *Page) handleJsonMetaData(datum []byte) (interface{}, error) { var f interface{} if err := json.Unmarshal(datum, &f); err != nil { return f, fmt.Errorf("Invalid JSON in %v \nError parsing page meta data: %s", page.FileName, err) @@ -385,10 +385,6 @@ func (page *Page) buildPageFromFile() error { return err } - if err := page.setOutFile(); err != nil { - return err - } - switch page.Markup { case "md": page.convertMarkdown(content) @@ -398,22 +394,6 @@ func (page *Page) buildPageFromFile() error { return nil } -func (p *Page) setOutFile() error { - if len(strings.TrimSpace(p.Slug)) > 0 { - // Use Slug if provided - p.OutFile = strings.TrimSpace(p.Slug + "." + p.Extension) - } else if len(strings.TrimSpace(p.Url)) > 2 { - // Use Url if provided & Slug missing - p.OutFile = strings.TrimSpace(p.Url) - } else { - // Fall back to filename - _, t := filepath.Split(p.FileName) - p.OutFile = replaceExtension(strings.TrimSpace(t), p.Extension) - } - - return nil -} - func (page *Page) convertMarkdown(lines []string) { page.RawMarkdown = strings.Join(lines, "\n") @@ -437,7 +417,7 @@ func (page *Page) convertRestructuredText(lines []string) { rstLines := strings.Split(out.String(), "\n") for i, line := range rstLines { if strings.HasPrefix(line, "") { - rstLines = (rstLines[i+1:len(rstLines)-3]) + rstLines = (rstLines[i+1 : len(rstLines)-3]) } } content := strings.Join(rstLines, "\n") diff --git a/hugolib/site.go b/hugolib/site.go index b27639409..18b007e40 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -27,6 +27,8 @@ import ( //"sync" ) +const slash = string(os.PathSeparator) + type Site struct { c Config Pages Pages @@ -45,7 +47,7 @@ type SiteInfo struct { Indexes *OrderedIndexList Recent *Pages LastChange time.Time - Title string + Title string } func (s *Site) getFromIndex(kind string, name string) Pages { @@ -164,7 +166,7 @@ func (s *Site) initialize() { filepath.Walk(s.c.GetAbsPath(s.c.SourceDir), walker) - s.Info = SiteInfo{BaseUrl: template.URL(s.c.BaseUrl), Title: s.c.Title} + s.Info = SiteInfo{BaseUrl: template.URL(s.c.BaseUrl), Title: s.c.Title} s.Shortcodes = make(map[string]ShortcodeFunc) } @@ -201,6 +203,7 @@ func (s *Site) CreatePages() { page := NewPage(fileName) page.Site = s.Info page.Tmpl = s.Tmpl + s.setOutFile(page) if s.c.BuildDrafts || !page.Draft { s.Pages = append(s.Pages, page) } @@ -251,7 +254,30 @@ func (s *Site) RenderPages() { func (s *Site) WritePages() { for _, p := range s.Pages { - s.WritePublic(p.Section, p.OutFile, p.RenderedContent.Bytes()) + s.WritePublic(p.Section+slash+p.OutFile, p.RenderedContent.Bytes()) + } +} + +func (s *Site) setOutFile(p *Page) { + if len(strings.TrimSpace(p.Slug)) > 0 { + // Use Slug if provided + if s.c.UglyUrls { + p.OutFile = strings.TrimSpace(p.Slug + "." + p.Extension) + } else { + p.OutFile = strings.TrimSpace(p.Slug + "/index.html") + } + } else if len(strings.TrimSpace(p.Url)) > 2 { + // Use Url if provided & Slug missing + p.OutFile = strings.TrimSpace(p.Url) + } else { + // Fall back to filename + _, t := filepath.Split(p.FileName) + if s.c.UglyUrls { + p.OutFile = replaceExtension(strings.TrimSpace(t), p.Extension) + } else { + file, _ := fileExt(strings.TrimSpace(t)) + p.OutFile = file + "/index." + p.Extension + } } } @@ -261,24 +287,42 @@ func (s *Site) RenderIndexes() { n := s.NewNode() n.Title = strings.Title(k) url := Urlize(plural + "/" + k) - n.Url = url + ".html" - n.Permalink = template.HTML(MakePermalink(string(n.Site.BaseUrl), string(n.Url))) + plink := url + if s.c.UglyUrls { + n.Url = url + ".html" + plink = n.Url + } else { + n.Url = url + "/index.html" + } + n.Permalink = template.HTML(MakePermalink(string(n.Site.BaseUrl), string(plink))) n.RSSlink = template.HTML(MakePermalink(string(n.Site.BaseUrl), string(url+".xml"))) n.Date = o[0].Date n.Data[singular] = o n.Data["Pages"] = o - layout := "indexes/" + singular + ".html" + layout := "indexes" + slash + singular + ".html" x := s.RenderThing(n, layout) - s.WritePublic(plural, k+".html", x.Bytes()) + + var base string + if s.c.UglyUrls { + base = plural + slash + k + } else { + base = plural + slash + k + slash + "index" + } + + s.WritePublic(base+".html", x.Bytes()) if a := s.Tmpl.Lookup("rss.xml"); a != nil { // XML Feed y := s.NewXMLBuffer() - n.Url = Urlize(plural + "/" + k + ".xml") - n.Permalink = template.HTML(string(n.Site.BaseUrl) + plural + "/" + k + ".xml") + if s.c.UglyUrls { + n.Url = Urlize(plural + "/" + k + ".xml") + } else { + n.Url = Urlize(plural + "/" + k + "/index.xml") + } + n.Permalink = template.HTML(string(n.Site.BaseUrl) + n.Url) s.Tmpl.ExecuteTemplate(y, "rss.xml", n) - s.WritePublic(plural, k+".xml", y.Bytes()) + s.WritePublic(base+".xml", y.Bytes()) } } } @@ -290,21 +334,21 @@ func (s *Site) RenderLists() { n.Title = strings.Title(inflect.Pluralize(section)) n.Url = Urlize(section + "/index.html") n.Permalink = template.HTML(MakePermalink(string(n.Site.BaseUrl), string(n.Url))) - n.RSSlink = template.HTML(MakePermalink(string(n.Site.BaseUrl), string(section+"/index.xml"))) + n.RSSlink = template.HTML(MakePermalink(string(n.Site.BaseUrl), string(section+".xml"))) n.Date = data[0].Date n.Data["Pages"] = data layout := "indexes/" + section + ".html" x := s.RenderThing(n, layout) - s.WritePublic(section, "index.html", x.Bytes()) + s.WritePublic(section+slash+"index.html", x.Bytes()) if a := s.Tmpl.Lookup("rss.xml"); a != nil { // XML Feed - n.Url = Urlize(section + "/index.xml") - n.Permalink = template.HTML(string(n.Site.BaseUrl) + section + "/index.xml") + n.Url = Urlize(section + ".xml") + n.Permalink = template.HTML(string(n.Site.BaseUrl) + n.Url) y := s.NewXMLBuffer() s.Tmpl.ExecuteTemplate(y, "rss.xml", n) - s.WritePublic(section, "index.xml", y.Bytes()) + s.WritePublic(section+slash+"index.xml", y.Bytes()) } } } @@ -322,16 +366,16 @@ func (s *Site) RenderHomePage() { n.Data["Pages"] = s.Pages[:9] } x := s.RenderThing(n, "index.html") - s.WritePublic("", "index.html", x.Bytes()) + s.WritePublic("index.html", x.Bytes()) if a := s.Tmpl.Lookup("rss.xml"); a != nil { // XML Feed n.Url = Urlize("index.xml") - n.Title = "Recent Content" - n.Permalink = template.HTML(string(n.Site.BaseUrl) + "index.xml") + n.Title = "Recent Content" + n.Permalink = template.HTML(string(n.Site.BaseUrl) + "index.xml") y := s.NewXMLBuffer() s.Tmpl.ExecuteTemplate(y, "rss.xml", n) - s.WritePublic("", "index.xml", y.Bytes()) + s.WritePublic("index.xml", y.Bytes()) } } @@ -361,17 +405,18 @@ func (s *Site) NewXMLBuffer() *bytes.Buffer { return bytes.NewBufferString(header) } -func (s *Site) WritePublic(path string, filename string, content []byte) { - AbsPath := "" - if path != "" { - // TODO double check the following line.. calling GetAbsPath 2x seems wrong - mkdirIf(s.c.GetAbsPath(filepath.Join(s.c.GetAbsPath(s.c.PublishDir), path))) - AbsPath = filepath.Join(s.c.GetAbsPath(s.c.PublishDir), path, filename) - } else { - AbsPath = filepath.Join(s.c.GetAbsPath(s.c.PublishDir), filename) +func (s *Site) WritePublic(path string, content []byte) { + + if s.c.Verbose { + fmt.Println(path) } - file, _ := os.Create(AbsPath) + path, filename := filepath.Split(path) + + path = filepath.FromSlash(s.c.GetAbsPath(filepath.Join(s.c.PublishDir, path))) + mkdirIf(path) + + file, _ := os.Create(filepath.Join(path, filename)) defer file.Close() file.Write(content) diff --git a/main.go b/main.go index fe4c0e871..ddac8f02e 100644 --- a/main.go +++ b/main.go @@ -14,10 +14,10 @@ package main import ( - "github.com/spf13/hugo/hugolib" "flag" "fmt" "github.com/howeyc/fsnotify" + "github.com/spf13/hugo/hugolib" "net/http" "os" "path/filepath" @@ -39,6 +39,7 @@ var ( watchMode = flag.Bool("w", false, "watch filesystem for changes and recreate as needed") server = flag.Bool("s", false, "run a (very) simple web server") port = flag.String("port", "1313", "port to run web server on, default :1313") + uglyUrls = flag.Bool("uglyurls", false, "use /filename.html instead of /filename/ ") ) func usage() { @@ -58,12 +59,14 @@ func main() { config := hugolib.SetupConfig(cfgfile, path) config.BuildDrafts = *draft + config.UglyUrls = *uglyUrls + config.Verbose = *verbose if *baseUrl != "" { config.BaseUrl = *baseUrl } else if *server { - config.BaseUrl = "http://localhost:" + *port - } + config.BaseUrl = "http://localhost:" + *port + } if *version { fmt.Println("Hugo Static Site Generator v0.8") @@ -115,11 +118,11 @@ func serve(port string, config *hugolib.Config) { } func buildSite(config *hugolib.Config) *hugolib.Site { - startTime := time.Now() + startTime := time.Now() site := hugolib.NewSite(config) site.Build() site.Stats() - fmt.Printf("in %v ms\n", int(1000 * time.Since(startTime).Seconds())) + fmt.Printf("in %v ms\n", int(1000*time.Since(startTime).Seconds())) return site } @@ -145,6 +148,9 @@ func NewWatcher(c *hugolib.Config, port string, server bool) error { select { case ev := <-watcher.Event: var _ = ev + if c.Verbose { + fmt.Println(ev) + } watchChange(c) // TODO add newly created directories to the watch list case err := <-watcher.Error: