2016-10-31 14:53:33 -04:00
|
|
|
// Copyright 2016 The Hugo Authors. All rights reserved.
|
|
|
|
//
|
|
|
|
// 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 hugolib
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
|
|
|
|
2016-11-08 17:34:52 -05:00
|
|
|
bp "github.com/spf13/hugo/bufferpool"
|
2016-10-31 14:53:33 -04:00
|
|
|
"github.com/spf13/hugo/helpers"
|
2016-11-08 17:34:52 -05:00
|
|
|
"github.com/spf13/viper"
|
2016-10-31 14:53:33 -04:00
|
|
|
|
|
|
|
jww "github.com/spf13/jwalterweatherman"
|
|
|
|
)
|
|
|
|
|
|
|
|
// renderPages renders pages each corresponding to a markdown file.
|
|
|
|
// TODO(bep np doc
|
|
|
|
func (s *Site) renderPages() error {
|
|
|
|
|
|
|
|
results := make(chan error)
|
|
|
|
pages := make(chan *Page)
|
|
|
|
errs := make(chan error)
|
|
|
|
|
|
|
|
go errorCollator(results, errs)
|
|
|
|
|
|
|
|
procs := getGoMaxProcs()
|
|
|
|
|
|
|
|
wg := &sync.WaitGroup{}
|
|
|
|
|
|
|
|
for i := 0; i < procs*4; i++ {
|
|
|
|
wg.Add(1)
|
|
|
|
go pageRenderer(s, pages, results, wg)
|
|
|
|
}
|
|
|
|
|
2016-11-03 19:34:25 -04:00
|
|
|
for _, page := range s.Nodes {
|
2016-10-31 14:53:33 -04:00
|
|
|
pages <- page
|
|
|
|
}
|
|
|
|
|
|
|
|
close(pages)
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
close(results)
|
|
|
|
|
|
|
|
err := <-errs
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error(s) rendering pages: %s", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func pageRenderer(s *Site, pages <-chan *Page, results chan<- error, wg *sync.WaitGroup) {
|
|
|
|
defer wg.Done()
|
|
|
|
for p := range pages {
|
|
|
|
targetPath := p.TargetPath()
|
|
|
|
layouts := p.layouts()
|
2016-11-01 17:39:24 -04:00
|
|
|
jww.DEBUG.Printf("Render %s to %q with layouts %q", p.NodeType, targetPath, layouts)
|
2016-11-07 14:24:37 -05:00
|
|
|
|
2016-10-31 14:53:33 -04:00
|
|
|
if err := s.renderAndWritePage("page "+p.FullFilePath(), targetPath, p, s.appendThemeTemplates(layouts)...); err != nil {
|
|
|
|
results <- err
|
|
|
|
}
|
|
|
|
|
2016-11-01 11:47:15 -04:00
|
|
|
// Taxonomy terms have no page set to paginate, so skip that for now.
|
|
|
|
if p.NodeType.IsNode() && p.NodeType != NodeTaxonomyTerms {
|
2016-10-31 14:53:33 -04:00
|
|
|
if err := s.renderPaginator(p); err != nil {
|
|
|
|
results <- err
|
|
|
|
}
|
|
|
|
}
|
2016-11-02 16:34:19 -04:00
|
|
|
|
|
|
|
if err := s.renderRSS(p); err != nil {
|
|
|
|
results <- err
|
|
|
|
}
|
2016-10-31 14:53:33 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// renderPaginator must be run after the owning Page has been rendered.
|
|
|
|
// TODO(bep) np
|
|
|
|
func (s *Site) renderPaginator(p *Page) error {
|
|
|
|
if p.paginator != nil {
|
|
|
|
jww.DEBUG.Printf("Render paginator for page %q", p.Path())
|
|
|
|
paginatePath := helpers.Config().GetString("paginatePath")
|
|
|
|
|
|
|
|
// write alias for page 1
|
|
|
|
// TODO(bep) ml all of these n.addLang ... fix.
|
2016-11-07 14:24:37 -05:00
|
|
|
// TODO(bep) np URL
|
|
|
|
|
|
|
|
aliasPath := p.addLangPathPrefix(helpers.PaginateAliasPath(path.Join(p.sections...), 1))
|
|
|
|
//TODO(bep) np node.permalink
|
|
|
|
s.writeDestAlias(aliasPath, p.Node.Permalink(), nil)
|
2016-10-31 14:53:33 -04:00
|
|
|
|
|
|
|
pagers := p.paginator.Pagers()
|
|
|
|
|
|
|
|
for i, pager := range pagers {
|
|
|
|
if i == 0 {
|
|
|
|
// already created
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
pagerNode := p.copy()
|
|
|
|
|
|
|
|
pagerNode.paginator = pager
|
|
|
|
if pager.TotalPages() > 0 {
|
|
|
|
first, _ := pager.page(0)
|
|
|
|
pagerNode.Date = first.Date
|
|
|
|
pagerNode.Lastmod = first.Lastmod
|
|
|
|
}
|
|
|
|
|
|
|
|
pageNumber := i + 1
|
|
|
|
htmlBase := path.Join(p.URLPath.URL, fmt.Sprintf("/%s/%d", paginatePath, pageNumber))
|
|
|
|
htmlBase = p.addLangPathPrefix(htmlBase)
|
2016-11-07 14:24:37 -05:00
|
|
|
|
2016-10-31 14:53:33 -04:00
|
|
|
if err := s.renderAndWritePage(pagerNode.Title,
|
|
|
|
filepath.FromSlash(htmlBase), pagerNode, p.layouts()...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2016-11-02 16:34:19 -04:00
|
|
|
|
|
|
|
func (s *Site) renderRSS(p *Page) error {
|
|
|
|
layouts := p.rssLayouts()
|
|
|
|
|
|
|
|
if layouts == nil {
|
|
|
|
// No RSS for this NodeType
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(bep) np check RSS titles
|
2016-11-03 19:34:25 -04:00
|
|
|
// TODO(bep) np check RSS page limit, 50?
|
2016-11-02 16:34:19 -04:00
|
|
|
rssNode := p.copy()
|
|
|
|
|
|
|
|
// TODO(bep) np todelido URL
|
|
|
|
rssURI := s.Language.GetString("rssURI")
|
|
|
|
rssNode.URLPath.URL = path.Join(rssNode.URLPath.URL, rssURI)
|
|
|
|
|
2016-11-07 14:24:37 -05:00
|
|
|
if err := s.renderAndWriteXML(rssNode.Title, rssNode.addLangFilepathPrefix(rssNode.URLPath.URL), rssNode, s.appendThemeTemplates(layouts)...); err != nil {
|
2016-11-02 16:34:19 -04:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2016-11-08 17:34:52 -05:00
|
|
|
|
|
|
|
func (s *Site) render404() error {
|
|
|
|
if viper.GetBool("disable404") {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
p := s.newNodePage(Node404)
|
|
|
|
p.Title = "404 Page not found"
|
|
|
|
p.Data["Pages"] = s.Pages
|
|
|
|
s.setPageURLs(p, "404.html")
|
|
|
|
|
|
|
|
nfLayouts := []string{"404.html"}
|
|
|
|
if nfErr := s.renderAndWritePage("404 page", "404.html", p, s.appendThemeTemplates(nfLayouts)...); nfErr != nil {
|
|
|
|
return nfErr
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Site) renderSitemap() error {
|
|
|
|
if viper.GetBool("disableSitemap") {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
sitemapDefault := parseSitemap(viper.GetStringMap("sitemap"))
|
|
|
|
|
|
|
|
n := s.newNodePage(NodeSitemap)
|
|
|
|
|
2016-11-09 13:59:28 -05:00
|
|
|
// Include all pages (regular, home page, taxonomies etc.)
|
|
|
|
pages := s.Nodes
|
2016-11-08 17:34:52 -05:00
|
|
|
|
|
|
|
page := s.newNodePage(NodeSitemap)
|
|
|
|
page.URLPath.URL = ""
|
|
|
|
page.Sitemap.ChangeFreq = sitemapDefault.ChangeFreq
|
|
|
|
page.Sitemap.Priority = sitemapDefault.Priority
|
2016-11-09 13:59:28 -05:00
|
|
|
page.Sitemap.Filename = sitemapDefault.Filename
|
2016-11-08 17:34:52 -05:00
|
|
|
|
|
|
|
n.Data["Pages"] = pages
|
|
|
|
|
2016-11-09 13:59:28 -05:00
|
|
|
// TODO(bep) this should be done somewhere else
|
2016-11-08 17:34:52 -05:00
|
|
|
for _, page := range pages {
|
|
|
|
if page.Sitemap.ChangeFreq == "" {
|
|
|
|
page.Sitemap.ChangeFreq = sitemapDefault.ChangeFreq
|
|
|
|
}
|
|
|
|
|
|
|
|
if page.Sitemap.Priority == -1 {
|
|
|
|
page.Sitemap.Priority = sitemapDefault.Priority
|
|
|
|
}
|
|
|
|
|
|
|
|
if page.Sitemap.Filename == "" {
|
|
|
|
page.Sitemap.Filename = sitemapDefault.Filename
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
smLayouts := []string{"sitemap.xml", "_default/sitemap.xml", "_internal/_default/sitemap.xml"}
|
|
|
|
addLanguagePrefix := n.Site.IsMultiLingual()
|
|
|
|
if err := s.renderAndWriteXML("sitemap", n.addLangPathPrefixIfFlagSet(page.Sitemap.Filename, addLanguagePrefix), n, s.appendThemeTemplates(smLayouts)...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Site) renderRobotsTXT() error {
|
|
|
|
if !viper.GetBool("enableRobotsTXT") {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
n := s.newNodePage(NodeRobotsTXT)
|
|
|
|
n.Data["Pages"] = s.Pages
|
|
|
|
|
|
|
|
rLayouts := []string{"robots.txt", "_default/robots.txt", "_internal/_default/robots.txt"}
|
|
|
|
outBuffer := bp.GetBuffer()
|
|
|
|
defer bp.PutBuffer(outBuffer)
|
|
|
|
err := s.renderForLayouts("robots", n, outBuffer, s.appendThemeTemplates(rLayouts)...)
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
err = s.writeDestFile("robots.txt", outBuffer)
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// renderAliases renders shell pages that simply have a redirect in the header.
|
|
|
|
func (s *Site) renderAliases() error {
|
2016-11-10 05:01:58 -05:00
|
|
|
for _, p := range s.Nodes {
|
2016-11-08 17:34:52 -05:00
|
|
|
if len(p.Aliases) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
plink, err := p.Permalink()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, a := range p.Aliases {
|
|
|
|
if err := s.writeDestAlias(a, plink, p); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if s.owner.multilingual.enabled() {
|
|
|
|
mainLang := s.owner.multilingual.DefaultLang.Lang
|
|
|
|
if s.Info.defaultContentLanguageInSubdir {
|
|
|
|
mainLangURL := s.Info.pathSpec.AbsURL(mainLang, false)
|
|
|
|
jww.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
|
|
|
|
if err := s.publishDestAlias(s.languageAliasTarget(), "/", mainLangURL, nil); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mainLangURL := s.Info.pathSpec.AbsURL("", false)
|
|
|
|
jww.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
|
|
|
|
if err := s.publishDestAlias(s.languageAliasTarget(), mainLang, mainLangURL, nil); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|