diff --git a/commands/hugo.go b/commands/hugo.go index 3e8351658..95e1c5864 100644 --- a/commands/hugo.go +++ b/commands/hugo.go @@ -153,6 +153,7 @@ var ( themesDir string source string logI18nWarnings bool + disableKinds []string ) // Execute adds all child commands to the root command HugoCmd and sets flags appropriately. @@ -239,6 +240,8 @@ func initHugoBuildCommonFlags(cmd *cobra.Command) { cmd.Flags().BoolP("noChmod", "", false, "Don't sync permission mode of files") cmd.Flags().BoolVarP(&logI18nWarnings, "i18n-warnings", "", false, "Print missing translations") + cmd.Flags().StringSliceVar(&disableKinds, "disableKinds", []string{}, "Disable different kind of pages (home, RSS etc.)") + // Set bash-completion. // Each flag must first be defined before using the SetAnnotation() call. cmd.Flags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{}) @@ -290,6 +293,10 @@ func InitializeConfig(subCmdVs ...*cobra.Command) (*deps.DepsCfg, error) { c.initializeFlags(cmdV) } + if len(disableKinds) > 0 { + c.Set("disableKinds", disableKinds) + } + logger, err := createLogger(cfg.Cfg) if err != nil { return cfg, err @@ -450,6 +457,7 @@ func (c *commandeer) initializeFlags(cmd *cobra.Command) { for _, key := range flagKeys { c.setValueFromFlag(cmd.Flags(), key) } + } func (c *commandeer) setValueFromFlag(flags *flag.FlagSet, key string) { diff --git a/hugolib/disableKinds_test.go b/hugolib/disableKinds_test.go new file mode 100644 index 000000000..3b039478c --- /dev/null +++ b/hugolib/disableKinds_test.go @@ -0,0 +1,216 @@ +// 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 ( + "strings" + "testing" + + "fmt" + + "github.com/spf13/afero" + "github.com/spf13/hugo/deps" + + "github.com/spf13/hugo/helpers" + "github.com/spf13/hugo/hugofs" + "github.com/stretchr/testify/require" +) + +func TestDisableKindsNoneDisabled(t *testing.T) { + t.Parallel() + doTestDisableKinds(t) +} + +func TestDisableKindsSomeDisabled(t *testing.T) { + t.Parallel() + doTestDisableKinds(t, KindSection, kind404) +} + +func TestDisableKindsOneDisabled(t *testing.T) { + t.Parallel() + for _, kind := range allKinds { + doTestDisableKinds(t, kind) + } +} + +func TestDisableKindsAllDisabled(t *testing.T) { + t.Parallel() + doTestDisableKinds(t, allKinds...) +} + +func doTestDisableKinds(t *testing.T, disabled ...string) { + + siteConfigTemplate := ` +baseURL = "http://example.com/blog" +enableRobotsTXT = true +disableKinds = %s + +paginate = 1 +defaultContentLanguage = "en" + +[Taxonomies] +tag = "tags" +category = "categories" +` + + pageTemplate := `--- +title: "%s" +tags: +%s +categories: +- Hugo +--- +# Doc +` + + mf := afero.NewMemMapFs() + + disabledStr := "[]" + + if len(disabled) > 0 { + disabledStr = strings.Replace(fmt.Sprintf("%#v", disabled), "[]string{", "[", -1) + disabledStr = strings.Replace(disabledStr, "}", "]", -1) + } + + siteConfig := fmt.Sprintf(siteConfigTemplate, disabledStr) + writeToFs(t, mf, "config.toml", siteConfig) + + cfg, err := LoadConfig(mf, "", "config.toml") + require.NoError(t, err) + + fs := hugofs.NewFrom(mf, cfg) + th := testHelper{cfg, fs, t} + + writeSource(t, fs, "layouts/index.html", "Home|{{ .Title }}|{{ .Content }}") + writeSource(t, fs, "layouts/_default/single.html", "Single|{{ .Title }}|{{ .Content }}") + writeSource(t, fs, "layouts/_default/list.html", "List|{{ .Title }}|{{ .Content }}") + writeSource(t, fs, "layouts/_default/terms.html", "Terms List|{{ .Title }}|{{ .Content }}") + writeSource(t, fs, "layouts/404.html", "Page Not Found") + + writeSource(t, fs, "content/sect/p1.md", fmt.Sprintf(pageTemplate, "P1", "- tag1")) + + writeNewContentFile(t, fs, "Category Terms", "2017-01-01", "content/categories/_index.md", 10) + writeNewContentFile(t, fs, "Tag1 List", "2017-01-01", "content/tags/tag1/_index.md", 10) + + h, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg}) + + require.NoError(t, err) + require.Len(t, h.Sites, 1) + + err = h.Build(BuildCfg{}) + + require.NoError(t, err) + + assertDisabledKinds(th, h.Sites[0], disabled...) + +} + +func assertDisabledKinds(th testHelper, s *Site, disabled ...string) { + assertDisabledKind(th, + func(isDisabled bool) bool { + if isDisabled { + return len(s.RegularPages) == 0 + } + return len(s.RegularPages) > 0 + }, disabled, KindPage, "public/sect/p1/index.html", "Single|P1") + assertDisabledKind(th, + func(isDisabled bool) bool { + p := s.getPage(KindHome) + if isDisabled { + return p == nil + } + return p != nil + }, disabled, KindHome, "public/index.html", "Home") + assertDisabledKind(th, + func(isDisabled bool) bool { + p := s.getPage(KindSection, "sect") + if isDisabled { + return p == nil + } + return p != nil + }, disabled, KindSection, "public/sect/index.html", "Sects") + assertDisabledKind(th, + func(isDisabled bool) bool { + p := s.getPage(KindTaxonomy, "tags", "tag1") + + if isDisabled { + return p == nil + } + return p != nil + + }, disabled, KindTaxonomy, "public/tags/tag1/index.html", "Tag1") + assertDisabledKind(th, + func(isDisabled bool) bool { + p := s.getPage(KindTaxonomyTerm, "tags") + if isDisabled { + return p == nil + } + return p != nil + + }, disabled, KindTaxonomyTerm, "public/tags/index.html", "Tags") + assertDisabledKind(th, + func(isDisabled bool) bool { + p := s.getPage(KindTaxonomyTerm, "categories") + + if isDisabled { + return p == nil + } + return p != nil + + }, disabled, KindTaxonomyTerm, "public/categories/index.html", "Category Terms") + assertDisabledKind(th, + func(isDisabled bool) bool { + p := s.getPage(KindTaxonomy, "categories", "hugo") + if isDisabled { + return p == nil + } + return p != nil + + }, disabled, KindTaxonomy, "public/categories/hugo/index.html", "Hugo") + // The below have no page in any collection. + assertDisabledKind(th, func(isDisabled bool) bool { return true }, disabled, kindRSS, "public/index.xml", "") + assertDisabledKind(th, func(isDisabled bool) bool { return true }, disabled, kindSitemap, "public/sitemap.xml", "sitemap") + assertDisabledKind(th, func(isDisabled bool) bool { return true }, disabled, kindRobotsTXT, "public/robots.txt", "User-agent") + assertDisabledKind(th, func(isDisabled bool) bool { return true }, disabled, kind404, "public/404.html", "Page Not Found") +} + +func assertDisabledKind(th testHelper, kindAssert func(bool) bool, disabled []string, kind, path, matcher string) { + isDisabled := stringSliceContains(kind, disabled...) + require.True(th.T, kindAssert(isDisabled), fmt.Sprintf("%s: %t", kind, isDisabled)) + + if kind == kindRSS && !isDisabled { + // If the home page is also disabled, there is not RSS to look for. + if stringSliceContains(KindHome, disabled...) { + isDisabled = true + } + } + + if isDisabled { + // Path should not exist + fileExists, err := helpers.Exists(path, th.Fs.Destination) + require.False(th.T, fileExists) + require.NoError(th.T, err) + + } else { + th.assertFileContent(path, matcher) + } +} + +func stringSliceContains(k string, values ...string) bool { + for _, v := range values { + if k == v { + return true + } + } + return false +} diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index 74aa94ede..9babf4aca 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -303,15 +303,17 @@ func (h *HugoSites) createMissingPages() error { for _, s := range h.Sites { - // home pages - home := s.findPagesByKind(KindHome) - if len(home) > 1 { - panic("Too many homes") - } - if len(home) == 0 { - n := s.newHomePage() - s.Pages = append(s.Pages, n) - newPages = append(newPages, n) + if s.isEnabled(KindHome) { + // home pages + home := s.findPagesByKind(KindHome) + if len(home) > 1 { + panic("Too many homes") + } + if len(home) == 0 { + n := s.newHomePage() + s.Pages = append(s.Pages, n) + newPages = append(newPages, n) + } } // taxonomy list and terms pages @@ -339,43 +341,50 @@ func (h *HugoSites) createMissingPages() error { break } } - if !foundTaxonomyPage { - n := s.newTaxonomyPage(plural, key) - s.Pages = append(s.Pages, n) - newPages = append(newPages, n) + + if s.isEnabled(KindTaxonomy) { + if !foundTaxonomyPage { + n := s.newTaxonomyPage(plural, key) + s.Pages = append(s.Pages, n) + newPages = append(newPages, n) + } } - if !foundTaxonomyTermsPage { - foundTaxonomyTermsPage = true - n := s.newTaxonomyTermsPage(plural) - s.Pages = append(s.Pages, n) - newPages = append(newPages, n) + if s.isEnabled(KindTaxonomyTerm) { + if !foundTaxonomyTermsPage { + foundTaxonomyTermsPage = true + n := s.newTaxonomyTermsPage(plural) + s.Pages = append(s.Pages, n) + newPages = append(newPages, n) + } } } } } - sectionPages := s.findPagesByKind(KindSection) - if len(sectionPages) < len(s.Sections) { - for name, section := range s.Sections { - // A section may be created for the root content folder if a - // content file is placed there. - // We cannot create a section node for that, because - // that would overwrite the home page. - if name == "" { - continue - } - foundSection := false - for _, sectionPage := range sectionPages { - if sectionPage.sections[0] == name { - foundSection = true - break + if s.isEnabled(KindSection) { + sectionPages := s.findPagesByKind(KindSection) + if len(sectionPages) < len(s.Sections) { + for name, section := range s.Sections { + // A section may be created for the root content folder if a + // content file is placed there. + // We cannot create a section node for that, because + // that would overwrite the home page. + if name == "" { + continue + } + foundSection := false + for _, sectionPage := range sectionPages { + if sectionPage.sections[0] == name { + foundSection = true + break + } + } + if !foundSection { + n := s.newSectionPage(name, section) + s.Pages = append(s.Pages, n) + newPages = append(newPages, n) } - } - if !foundSection { - n := s.newSectionPage(name, section) - s.Pages = append(s.Pages, n) - newPages = append(newPages, n) } } } @@ -429,6 +438,14 @@ func (h *HugoSites) setupTranslations() { panic("Page language missing: " + p.Title) } + if p.Kind == kindUnknown { + p.Kind = p.s.kindFromSections(p.sections) + } + + if !p.s.isEnabled(p.Kind) { + continue + } + shouldBuild := p.shouldBuild() for i, site := range h.Sites { diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index a214e523c..059b0fe1e 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -136,6 +136,12 @@ func (h *HugoSites) process(config *BuildCfg, events ...fsnotify.Event) error { } func (h *HugoSites) assemble(config *BuildCfg) error { + if config.whatChanged.source { + for _, s := range h.Sites { + s.createTaxonomiesEntries() + } + } + // TODO(bep) we could probably wait and do this in one go later h.setupTranslations() diff --git a/hugolib/hugo_sites_build_test.go b/hugolib/hugo_sites_build_test.go index 33273c233..9f8b6084a 100644 --- a/hugolib/hugo_sites_build_test.go +++ b/hugolib/hugo_sites_build_test.go @@ -218,6 +218,7 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) { // Check site config for _, s := range sites.Sites { require.True(t, s.Info.defaultContentLanguageInSubdir, s.Info.Title) + require.NotNil(t, s.disabledKinds) } enSite := sites.Sites[0] diff --git a/hugolib/page.go b/hugolib/page.go index 2590cd7e5..e92767da0 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -42,7 +42,8 @@ import ( ) var ( - cjk = regexp.MustCompile(`\p{Han}|\p{Hangul}|\p{Hiragana}|\p{Katakana}`) + cjk = regexp.MustCompile(`\p{Han}|\p{Hangul}|\p{Hiragana}|\p{Katakana}`) + allKinds = []string{KindHome, KindSection, KindTaxonomy, KindTaxonomyTerm, kindRSS, kindSitemap, kindRobotsTXT, kind404} ) const ( diff --git a/hugolib/site.go b/hugolib/site.go index 6df98b86b..31a8606f9 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -105,13 +105,22 @@ type Site struct { Data map[string]interface{} Language *helpers.Language + disabledKinds map[string]bool + // Logger etc. *deps.Deps `json:"-"` } +func (s *Site) isEnabled(kind string) bool { + if kind == kindUnknown { + panic("Unknown kind") + } + return !s.disabledKinds[kind] +} + // reset returns a new Site prepared for rebuild. func (s *Site) reset() *Site { - return &Site{Deps: s.Deps, Language: s.Language, owner: s.owner, PageCollections: newPageCollections()} + return &Site{Deps: s.Deps, disabledKinds: s.disabledKinds, Language: s.Language, owner: s.owner, PageCollections: newPageCollections()} } // newSite creates a new site with the given configuration. @@ -122,7 +131,12 @@ func newSite(cfg deps.DepsCfg) (*Site, error) { cfg.Language = helpers.NewDefaultLanguage(cfg.Cfg) } - s := &Site{PageCollections: c, Language: cfg.Language} + disabledKinds := make(map[string]bool) + for _, disabled := range cast.ToStringSlice(cfg.Language.Get("disableKinds")) { + disabledKinds[disabled] = true + } + + s := &Site{PageCollections: c, Language: cfg.Language, disabledKinds: disabledKinds} s.Info = newSiteInfo(siteBuilderCfg{s: s, pageCollections: c, language: s.Language}) return s, nil @@ -820,6 +834,7 @@ func (s *Site) setupPrevNext() { } func (s *Site) render() (err error) { + if err = s.preparePages(); err != nil { return } @@ -1493,8 +1508,18 @@ func (s *Site) getTaxonomyKey(key string) string { } return s.PathSpec.MakePathSanitized(key) } -func (s *Site) assembleTaxonomies() { + +// We need to create the top level taxonomy early in the build process +// to be able to determine the page Kind correctly. +func (s *Site) createTaxonomiesEntries() { s.Taxonomies = make(TaxonomyList) + taxonomies := s.Language.GetStringMapString("taxonomies") + for _, plural := range taxonomies { + s.Taxonomies[plural] = make(Taxonomy) + } +} + +func (s *Site) assembleTaxonomies() { s.taxonomiesPluralSingular = make(map[string]string) s.taxonomiesOrigKey = make(map[string]string) @@ -1503,7 +1528,6 @@ func (s *Site) assembleTaxonomies() { s.Log.INFO.Printf("found taxonomies: %#v\n", taxonomies) for singular, plural := range taxonomies { - s.Taxonomies[plural] = make(Taxonomy) s.taxonomiesPluralSingular[plural] = singular for _, p := range s.Pages { diff --git a/hugolib/site_render.go b/hugolib/site_render.go index 23b5a11b4..c67743394 100644 --- a/hugolib/site_render.go +++ b/hugolib/site_render.go @@ -130,6 +130,10 @@ func (s *Site) renderPaginator(p *Page) error { func (s *Site) renderRSS(p *Page) error { + if !s.isEnabled(kindRSS) { + return nil + } + if s.Cfg.GetBool("disableRSS") { return nil } @@ -167,6 +171,10 @@ func (s *Site) renderRSS(p *Page) error { } func (s *Site) render404() error { + if !s.isEnabled(kind404) { + return nil + } + if s.Cfg.GetBool("disable404") { return nil } @@ -184,6 +192,10 @@ func (s *Site) render404() error { } func (s *Site) renderSitemap() error { + if !s.isEnabled(kindSitemap) { + return nil + } + if s.Cfg.GetBool("disableSitemap") { return nil } @@ -227,6 +239,10 @@ func (s *Site) renderSitemap() error { } func (s *Site) renderRobotsTXT() error { + if !s.isEnabled(kindRobotsTXT) { + return nil + } + if !s.Cfg.GetBool("enableRobotsTXT") { return nil }