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