github.com/neohugo/neohugo@v0.123.8/hugolib/content_map.go (about)

     1  // Copyright 2019 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  	"fmt"
    18  	"path"
    19  	"path/filepath"
    20  	"strings"
    21  	"unicode"
    22  
    23  	"github.com/bep/logg"
    24  	"github.com/neohugo/neohugo/common/hugio"
    25  	"github.com/neohugo/neohugo/common/paths"
    26  	"github.com/neohugo/neohugo/identity"
    27  	"github.com/neohugo/neohugo/source"
    28  
    29  	"github.com/neohugo/neohugo/resources/page"
    30  	"github.com/neohugo/neohugo/resources/resource"
    31  
    32  	"github.com/neohugo/neohugo/hugofs"
    33  )
    34  
    35  // Used to mark ambiguous keys in reverse index lookups.
    36  var ambiguousContentNode = &pageState{}
    37  
    38  var trimCutsetDotSlashSpace = func(r rune) bool {
    39  	return r == '.' || r == '/' || unicode.IsSpace(r)
    40  }
    41  
    42  type contentMapConfig struct {
    43  	lang                 string
    44  	taxonomyConfig       taxonomiesConfigValues
    45  	taxonomyDisabled     bool
    46  	taxonomyTermDisabled bool
    47  	pageDisabled         bool
    48  	isRebuild            bool
    49  }
    50  
    51  var _ contentNodeI = (*resourceSource)(nil)
    52  
    53  type resourceSource struct {
    54  	path   *paths.Path
    55  	opener hugio.OpenReadSeekCloser
    56  	fi     hugofs.FileMetaInfo
    57  
    58  	r resource.Resource
    59  }
    60  
    61  func (r resourceSource) clone() *resourceSource {
    62  	r.r = nil
    63  	return &r
    64  }
    65  
    66  func (r *resourceSource) LangIndex() int {
    67  	if r.r != nil && r.isPage() {
    68  		return r.r.(*pageState).s.languagei
    69  	}
    70  
    71  	return r.fi.Meta().LangIndex
    72  }
    73  
    74  func (r *resourceSource) MarkStale() {
    75  	resource.MarkStale(r.r)
    76  }
    77  
    78  func (r *resourceSource) resetBuildState() {
    79  	if rr, ok := r.r.(buildStateReseter); ok {
    80  		rr.resetBuildState()
    81  	}
    82  }
    83  
    84  func (r *resourceSource) isPage() bool {
    85  	_, ok := r.r.(page.Page)
    86  	return ok
    87  }
    88  
    89  func (r *resourceSource) GetIdentity() identity.Identity {
    90  	if r.r != nil {
    91  		return r.r.(identity.IdentityProvider).GetIdentity()
    92  	}
    93  	return r.path
    94  }
    95  
    96  func (r *resourceSource) ForEeachIdentity(f func(identity.Identity) bool) bool {
    97  	return f(r.GetIdentity())
    98  }
    99  
   100  func (r *resourceSource) Path() string {
   101  	return r.path.Path()
   102  }
   103  
   104  func (r *resourceSource) isContentNodeBranch() bool {
   105  	return false
   106  }
   107  
   108  var _ contentNodeI = (*resourceSources)(nil)
   109  
   110  type resourceSources []*resourceSource
   111  
   112  func (n resourceSources) MarkStale() {
   113  	for _, r := range n {
   114  		if r != nil {
   115  			r.MarkStale()
   116  		}
   117  	}
   118  }
   119  
   120  func (n resourceSources) Path() string {
   121  	panic("not supported")
   122  }
   123  
   124  func (n resourceSources) isContentNodeBranch() bool {
   125  	return false
   126  }
   127  
   128  func (n resourceSources) resetBuildState() {
   129  	for _, r := range n {
   130  		if r != nil {
   131  			r.resetBuildState()
   132  		}
   133  	}
   134  }
   135  
   136  func (n resourceSources) GetIdentity() identity.Identity {
   137  	for _, r := range n {
   138  		if r != nil {
   139  			return r.GetIdentity()
   140  		}
   141  	}
   142  	return nil
   143  }
   144  
   145  func (n resourceSources) ForEeachIdentity(f func(identity.Identity) bool) bool {
   146  	for _, r := range n {
   147  		if r != nil {
   148  			if f(r.GetIdentity()) {
   149  				return true
   150  			}
   151  		}
   152  	}
   153  	return false
   154  }
   155  
   156  func (cfg contentMapConfig) getTaxonomyConfig(s string) (v viewName) {
   157  	for _, n := range cfg.taxonomyConfig.views {
   158  		if strings.HasPrefix(s, n.pluralTreeKey) {
   159  			return n
   160  		}
   161  	}
   162  	return
   163  }
   164  
   165  func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
   166  	if fi.IsDir() {
   167  		return nil
   168  	}
   169  
   170  	insertResource := func(fim hugofs.FileMetaInfo) error {
   171  		pi := fi.Meta().PathInfo
   172  		key := pi.Base()
   173  		tree := m.treeResources
   174  
   175  		commit := tree.Lock(true)
   176  		defer commit()
   177  
   178  		r := func() (hugio.ReadSeekCloser, error) {
   179  			return fim.Meta().Open()
   180  		}
   181  
   182  		var rs *resourceSource
   183  		if pi.IsContent() {
   184  			// Create the page now as we need it at assemembly time.
   185  			// The other resources are created if needed.
   186  			pageResource, pi, err := m.s.h.newPage(
   187  				&pageMeta{
   188  					f:        source.NewFileInfo(fim),
   189  					pathInfo: pi,
   190  					bundled:  true,
   191  				},
   192  			)
   193  			if err != nil {
   194  				return err
   195  			}
   196  			if pageResource == nil {
   197  				// Disabled page.
   198  				return nil
   199  			}
   200  			key = pi.Base()
   201  
   202  			rs = &resourceSource{r: pageResource}
   203  		} else {
   204  			rs = &resourceSource{path: pi, opener: r, fi: fim}
   205  		}
   206  
   207  		tree.InsertIntoValuesDimension(key, rs)
   208  
   209  		return nil
   210  	}
   211  
   212  	meta := fi.Meta()
   213  	pi := meta.PathInfo
   214  
   215  	switch pi.BundleType() {
   216  	case paths.PathTypeFile, paths.PathTypeContentResource:
   217  		m.s.Log.Trace(logg.StringFunc(
   218  			func() string {
   219  				return fmt.Sprintf("insert resource: %q", fi.Meta().Filename)
   220  			},
   221  		))
   222  		if err := insertResource(fi); err != nil {
   223  			return err
   224  		}
   225  	default:
   226  		m.s.Log.Trace(logg.StringFunc(
   227  			func() string {
   228  				return fmt.Sprintf("insert bundle: %q", fi.Meta().Filename)
   229  			},
   230  		))
   231  		// A content file.
   232  		p, pi, err := m.s.h.newPage(
   233  			&pageMeta{
   234  				f:        source.NewFileInfo(fi),
   235  				pathInfo: pi,
   236  				bundled:  false,
   237  			},
   238  		)
   239  		if err != nil {
   240  			return err
   241  		}
   242  		if p == nil {
   243  			// Disabled page.
   244  			return nil
   245  		}
   246  
   247  		m.treePages.InsertWithLock(pi.Base(), p)
   248  
   249  	}
   250  	return nil
   251  }
   252  
   253  // The home page is represented with the zero string.
   254  // All other keys starts with a leading slash. No trailing slash.
   255  // Slashes are Unix-style.
   256  func cleanTreeKey(elem ...string) string {
   257  	var s string
   258  	if len(elem) > 0 {
   259  		s = elem[0]
   260  		if len(elem) > 1 {
   261  			s = path.Join(elem...)
   262  		}
   263  	}
   264  	s = strings.TrimFunc(s, trimCutsetDotSlashSpace)
   265  	s = filepath.ToSlash(strings.ToLower(paths.Sanitize(s)))
   266  	if s == "" || s == "/" {
   267  		return ""
   268  	}
   269  	if s[0] != '/' {
   270  		s = "/" + s
   271  	}
   272  	return s
   273  }