github.com/gohugoio/hugo@v0.88.1/create/content_template_handler.go (about) 1 // Copyright 2017 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 create 15 16 import ( 17 "bytes" 18 "fmt" 19 "path/filepath" 20 "strings" 21 "time" 22 23 "github.com/gohugoio/hugo/common/paths" 24 25 "github.com/pkg/errors" 26 27 "github.com/gohugoio/hugo/helpers" 28 "github.com/gohugoio/hugo/source" 29 30 "github.com/gohugoio/hugo/hugolib" 31 "github.com/gohugoio/hugo/tpl" 32 "github.com/spf13/afero" 33 ) 34 35 // ArchetypeFileData represents the data available to an archetype template. 36 type ArchetypeFileData struct { 37 // The archetype content type, either given as --kind option or extracted 38 // from the target path's section, i.e. "blog/mypost.md" will resolve to 39 // "blog". 40 Type string 41 42 // The current date and time as a RFC3339 formatted string, suitable for use in front matter. 43 Date string 44 45 // The Site, fully equipped with all the pages etc. Note: This will only be set if it is actually 46 // used in the archetype template. Also, if this is a multilingual setup, 47 // this site is the site that best matches the target content file, based 48 // on the presence of language code in the filename. 49 Site *hugolib.SiteInfo 50 51 // Name will in most cases be the same as TranslationBaseName, e.g. "my-post". 52 // But if that value is "index" (bundles), the Name is instead the owning folder. 53 // This is the value you in most cases would want to use to construct the title in your 54 // archetype template. 55 Name string 56 57 // The target content file. Note that the .Content will be empty, as that 58 // has not been created yet. 59 source.File 60 } 61 62 const ( 63 // ArchetypeTemplateTemplate is used as initial template when adding an archetype template. 64 ArchetypeTemplateTemplate = `--- 65 title: "{{ replace .Name "-" " " | title }}" 66 date: {{ .Date }} 67 draft: true 68 --- 69 70 ` 71 ) 72 73 var ( 74 archetypeShortcodeReplacementsPre = strings.NewReplacer( 75 "{{<", "{x{<", 76 "{{%", "{x{%", 77 ">}}", ">}x}", 78 "%}}", "%}x}") 79 80 archetypeShortcodeReplacementsPost = strings.NewReplacer( 81 "{x{<", "{{<", 82 "{x{%", "{{%", 83 ">}x}", ">}}", 84 "%}x}", "%}}") 85 ) 86 87 func executeArcheTypeAsTemplate(s *hugolib.Site, name, kind, targetPath, archetypeFilename string) ([]byte, error) { 88 var ( 89 archetypeContent []byte 90 archetypeTemplate []byte 91 err error 92 ) 93 94 f, err := s.SourceSpec.NewFileInfoFrom(targetPath, targetPath) 95 if err != nil { 96 return nil, err 97 } 98 99 if name == "" { 100 name = f.TranslationBaseName() 101 102 if name == "index" || name == "_index" { 103 // Page bundles; the directory name will hopefully have a better name. 104 dir := strings.TrimSuffix(f.Dir(), helpers.FilePathSeparator) 105 _, name = filepath.Split(dir) 106 } 107 } 108 109 data := ArchetypeFileData{ 110 Type: kind, 111 Date: time.Now().Format(time.RFC3339), 112 Name: name, 113 File: f, 114 Site: s.Info, 115 } 116 117 if archetypeFilename == "" { 118 // TODO(bep) archetype revive the issue about wrong tpl funcs arg order 119 archetypeTemplate = []byte(ArchetypeTemplateTemplate) 120 } else { 121 archetypeTemplate, err = afero.ReadFile(s.BaseFs.Archetypes.Fs, archetypeFilename) 122 if err != nil { 123 return nil, fmt.Errorf("failed to read archetype file %s", err) 124 } 125 126 } 127 128 // The archetype template may contain shortcodes, and these does not play well 129 // with the Go templates. Need to set some temporary delimiters. 130 archetypeTemplate = []byte(archetypeShortcodeReplacementsPre.Replace(string(archetypeTemplate))) 131 132 // Reuse the Hugo template setup to get the template funcs properly set up. 133 templateHandler := s.Deps.Tmpl().(tpl.TemplateManager) 134 templateName := paths.Filename(archetypeFilename) 135 if err := templateHandler.AddTemplate("_text/"+templateName, string(archetypeTemplate)); err != nil { 136 return nil, errors.Wrapf(err, "Failed to parse archetype file %q:", archetypeFilename) 137 } 138 139 templ, _ := templateHandler.Lookup(templateName) 140 141 var buff bytes.Buffer 142 if err := templateHandler.Execute(templ, &buff, data); err != nil { 143 return nil, errors.Wrapf(err, "Failed to process archetype file %q:", archetypeFilename) 144 } 145 146 archetypeContent = []byte(archetypeShortcodeReplacementsPost.Replace(buff.String())) 147 148 return archetypeContent, nil 149 }