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 }