github.com/shohhei1126/hugo@v0.42.2-0.20180623210752-3d5928889ad7/hugolib/page_collections.go (about)

     1  // Copyright 2016 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package hugolib
    15  
    16  import (
    17  	"path"
    18  	"path/filepath"
    19  	"strings"
    20  
    21  	"github.com/gohugoio/hugo/cache"
    22  	"github.com/gohugoio/hugo/helpers"
    23  )
    24  
    25  // PageCollections contains the page collections for a site.
    26  type PageCollections struct {
    27  	// Includes only pages of all types, and only pages in the current language.
    28  	Pages Pages
    29  
    30  	// Includes all pages in all languages, including the current one.
    31  	// Includes pages of all types.
    32  	AllPages Pages
    33  
    34  	// A convenience cache for the traditional index types, taxonomies, home page etc.
    35  	// This is for the current language only.
    36  	indexPages Pages
    37  
    38  	// A convenience cache for the regular pages.
    39  	// This is for the current language only.
    40  	RegularPages Pages
    41  
    42  	// A convenience cache for the all the regular pages.
    43  	AllRegularPages Pages
    44  
    45  	// Includes absolute all pages (of all types), including drafts etc.
    46  	rawAllPages Pages
    47  
    48  	// Includes headless bundles, i.e. bundles that produce no output for its content page.
    49  	headlessPages Pages
    50  
    51  	pageCache *cache.PartitionedLazyCache
    52  }
    53  
    54  func (c *PageCollections) refreshPageCaches() {
    55  	c.indexPages = c.findPagesByKindNotIn(KindPage, c.Pages)
    56  	c.RegularPages = c.findPagesByKindIn(KindPage, c.Pages)
    57  	c.AllRegularPages = c.findPagesByKindIn(KindPage, c.AllPages)
    58  
    59  	var s *Site
    60  
    61  	if len(c.Pages) > 0 {
    62  		s = c.Pages[0].s
    63  	}
    64  
    65  	cacheLoader := func(kind string) func() (map[string]interface{}, error) {
    66  		return func() (map[string]interface{}, error) {
    67  			cache := make(map[string]interface{})
    68  			switch kind {
    69  			case KindPage:
    70  				// Note that we deliberately use the pages from all sites
    71  				// in this cache, as we intend to use this in the ref and relref
    72  				// shortcodes. If the user says "sect/doc1.en.md", he/she knows
    73  				// what he/she is looking for.
    74  				for _, pageCollection := range []Pages{c.AllRegularPages, c.headlessPages} {
    75  					for _, p := range pageCollection {
    76  						cache[filepath.ToSlash(p.Source.Path())] = p
    77  
    78  						if s != nil && p.s == s {
    79  							// Ref/Relref supports this potentially ambiguous lookup.
    80  							cache[p.Source.LogicalName()] = p
    81  
    82  							translasionBaseName := p.Source.TranslationBaseName()
    83  							dir := filepath.ToSlash(strings.TrimSuffix(p.Dir(), helpers.FilePathSeparator))
    84  
    85  							if translasionBaseName == "index" {
    86  								_, name := path.Split(dir)
    87  								cache[name] = p
    88  								cache[dir] = p
    89  							} else {
    90  								// Again, ambigous
    91  								cache[translasionBaseName] = p
    92  							}
    93  
    94  							// We need a way to get to the current language version.
    95  							pathWithNoExtensions := path.Join(dir, translasionBaseName)
    96  							cache[pathWithNoExtensions] = p
    97  						}
    98  					}
    99  
   100  				}
   101  			default:
   102  				for _, p := range c.indexPages {
   103  					key := path.Join(p.sections...)
   104  					cache[key] = p
   105  				}
   106  			}
   107  
   108  			return cache, nil
   109  		}
   110  	}
   111  
   112  	partitions := make([]cache.Partition, len(allKindsInPages))
   113  
   114  	for i, kind := range allKindsInPages {
   115  		partitions[i] = cache.Partition{Key: kind, Load: cacheLoader(kind)}
   116  	}
   117  
   118  	c.pageCache = cache.NewPartitionedLazyCache(partitions...)
   119  }
   120  
   121  func newPageCollections() *PageCollections {
   122  	return &PageCollections{}
   123  }
   124  
   125  func newPageCollectionsFromPages(pages Pages) *PageCollections {
   126  	return &PageCollections{rawAllPages: pages}
   127  }
   128  
   129  func (c *PageCollections) getPage(typ string, sections ...string) *Page {
   130  	var key string
   131  	if len(sections) == 1 {
   132  		key = filepath.ToSlash(sections[0])
   133  	} else {
   134  		key = path.Join(sections...)
   135  	}
   136  
   137  	p, _ := c.pageCache.Get(typ, key)
   138  	if p == nil {
   139  		return nil
   140  	}
   141  	return p.(*Page)
   142  
   143  }
   144  
   145  func (*PageCollections) findPagesByKindIn(kind string, inPages Pages) Pages {
   146  	var pages Pages
   147  	for _, p := range inPages {
   148  		if p.Kind == kind {
   149  			pages = append(pages, p)
   150  		}
   151  	}
   152  	return pages
   153  }
   154  
   155  func (*PageCollections) findFirstPageByKindIn(kind string, inPages Pages) *Page {
   156  	for _, p := range inPages {
   157  		if p.Kind == kind {
   158  			return p
   159  		}
   160  	}
   161  	return nil
   162  }
   163  
   164  func (*PageCollections) findPagesByKindNotIn(kind string, inPages Pages) Pages {
   165  	var pages Pages
   166  	for _, p := range inPages {
   167  		if p.Kind != kind {
   168  			pages = append(pages, p)
   169  		}
   170  	}
   171  	return pages
   172  }
   173  
   174  func (c *PageCollections) findPagesByKind(kind string) Pages {
   175  	return c.findPagesByKindIn(kind, c.Pages)
   176  }
   177  
   178  func (c *PageCollections) addPage(page *Page) {
   179  	c.rawAllPages = append(c.rawAllPages, page)
   180  }
   181  
   182  func (c *PageCollections) removePageFilename(filename string) {
   183  	if i := c.rawAllPages.findPagePosByFilename(filename); i >= 0 {
   184  		c.clearResourceCacheForPage(c.rawAllPages[i])
   185  		c.rawAllPages = append(c.rawAllPages[:i], c.rawAllPages[i+1:]...)
   186  	}
   187  
   188  }
   189  
   190  func (c *PageCollections) removePage(page *Page) {
   191  	if i := c.rawAllPages.findPagePos(page); i >= 0 {
   192  		c.clearResourceCacheForPage(c.rawAllPages[i])
   193  		c.rawAllPages = append(c.rawAllPages[:i], c.rawAllPages[i+1:]...)
   194  	}
   195  
   196  }
   197  
   198  func (c *PageCollections) findPagesByShortcode(shortcode string) Pages {
   199  	var pages Pages
   200  
   201  	for _, p := range c.rawAllPages {
   202  		if p.shortcodeState != nil {
   203  			if _, ok := p.shortcodeState.nameSet[shortcode]; ok {
   204  				pages = append(pages, p)
   205  			}
   206  		}
   207  	}
   208  	return pages
   209  }
   210  
   211  func (c *PageCollections) replacePage(page *Page) {
   212  	// will find existing page that matches filepath and remove it
   213  	c.removePage(page)
   214  	c.addPage(page)
   215  }
   216  
   217  func (c *PageCollections) clearResourceCacheForPage(page *Page) {
   218  	if len(page.Resources) > 0 {
   219  		first := page.Resources[0]
   220  		dir := path.Dir(first.RelPermalink())
   221  		dir = strings.TrimPrefix(dir, page.LanguagePrefix())
   222  		// This is done to keep the memory usage in check when doing live reloads.
   223  		page.s.resourceSpec.DeleteCacheByPrefix(dir)
   224  	}
   225  }