hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

content_factory.go (4989B)

    1 // Copyright 2021 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 	"io"
   19 	"path/filepath"
   20 	"strings"
   21 	"time"
   22 
   23 	"github.com/gohugoio/hugo/common/htime"
   24 	"github.com/gohugoio/hugo/helpers"
   25 
   26 	"github.com/gohugoio/hugo/source"
   27 
   28 	"github.com/gohugoio/hugo/resources/page"
   29 
   30 	"github.com/spf13/afero"
   31 )
   32 
   33 // ContentFactory creates content files from archetype templates.
   34 type ContentFactory struct {
   35 	h *HugoSites
   36 
   37 	// We parse the archetype templates as Go templates, so we need
   38 	// to replace any shortcode with a temporary placeholder.
   39 	shortcodeReplacerPre  *strings.Replacer
   40 	shortcodeReplacerPost *strings.Replacer
   41 }
   42 
   43 // ApplyArchetypeFilename archetypeFilename to w as a template using the given Page p as the foundation for the data context.
   44 func (f ContentFactory) ApplyArchetypeFilename(w io.Writer, p page.Page, archetypeKind, archetypeFilename string) error {
   45 
   46 	fi, err := f.h.SourceFilesystems.Archetypes.Fs.Stat(archetypeFilename)
   47 	if err != nil {
   48 		return err
   49 	}
   50 
   51 	if fi.IsDir() {
   52 		return fmt.Errorf("archetype directory (%q) not supported", archetypeFilename)
   53 	}
   54 
   55 	templateSource, err := afero.ReadFile(f.h.SourceFilesystems.Archetypes.Fs, archetypeFilename)
   56 	if err != nil {
   57 		return fmt.Errorf("failed to read archetype file %q: %s: %w", archetypeFilename, err, err)
   58 
   59 	}
   60 
   61 	return f.ApplyArchetypeTemplate(w, p, archetypeKind, string(templateSource))
   62 
   63 }
   64 
   65 // ApplyArchetypeTemplate templateSource to w as a template using the given Page p as the foundation for the data context.
   66 func (f ContentFactory) ApplyArchetypeTemplate(w io.Writer, p page.Page, archetypeKind, templateSource string) error {
   67 	ps := p.(*pageState)
   68 	if archetypeKind == "" {
   69 		archetypeKind = p.Type()
   70 	}
   71 
   72 	d := &archetypeFileData{
   73 		Type: archetypeKind,
   74 		Date: htime.Now().Format(time.RFC3339),
   75 		Page: p,
   76 		File: p.File(),
   77 	}
   78 
   79 	templateSource = f.shortcodeReplacerPre.Replace(templateSource)
   80 
   81 	templ, err := ps.s.TextTmpl().Parse("archetype.md", string(templateSource))
   82 	if err != nil {
   83 		return fmt.Errorf("failed to parse archetype template: %s: %w", err, err)
   84 	}
   85 
   86 	result, err := executeToString(ps.s.Tmpl(), templ, d)
   87 	if err != nil {
   88 		return fmt.Errorf("failed to execute archetype template: %s: %w", err, err)
   89 	}
   90 
   91 	_, err = io.WriteString(w, f.shortcodeReplacerPost.Replace(result))
   92 
   93 	return err
   94 
   95 }
   96 
   97 func (f ContentFactory) SectionFromFilename(filename string) (string, error) {
   98 	filename = filepath.Clean(filename)
   99 	rel, _, err := f.h.AbsProjectContentDir(filename)
  100 	if err != nil {
  101 		return "", err
  102 	}
  103 
  104 	parts := strings.Split(helpers.ToSlashTrimLeading(rel), "/")
  105 	if len(parts) < 2 {
  106 		return "", nil
  107 	}
  108 	return parts[0], nil
  109 }
  110 
  111 // CreateContentPlaceHolder creates a content placeholder file inside the
  112 // best matching content directory.
  113 func (f ContentFactory) CreateContentPlaceHolder(filename string) (string, error) {
  114 	filename = filepath.Clean(filename)
  115 	_, abs, err := f.h.AbsProjectContentDir(filename)
  116 
  117 	if err != nil {
  118 		return "", err
  119 	}
  120 
  121 	// This will be overwritten later, just write a placeholder to get
  122 	// the paths correct.
  123 	placeholder := `---
  124 title: "Content Placeholder"
  125 _build:
  126   render: never
  127   list: never
  128   publishResources: false
  129 ---
  130 
  131 `
  132 
  133 	return abs, afero.SafeWriteReader(f.h.Fs.Source, abs, strings.NewReader(placeholder))
  134 }
  135 
  136 // NewContentFactory creates a new ContentFactory for h.
  137 func NewContentFactory(h *HugoSites) ContentFactory {
  138 	return ContentFactory{
  139 		h: h,
  140 		shortcodeReplacerPre: strings.NewReplacer(
  141 			"{{<", "{x{<",
  142 			"{{%", "{x{%",
  143 			">}}", ">}x}",
  144 			"%}}", "%}x}"),
  145 		shortcodeReplacerPost: strings.NewReplacer(
  146 			"{x{<", "{{<",
  147 			"{x{%", "{{%",
  148 			">}x}", ">}}",
  149 			"%}x}", "%}}"),
  150 	}
  151 }
  152 
  153 // archetypeFileData represents the data available to an archetype template.
  154 type archetypeFileData struct {
  155 	// The archetype content type, either given as --kind option or extracted
  156 	// from the target path's section, i.e. "blog/mypost.md" will resolve to
  157 	// "blog".
  158 	Type string
  159 
  160 	// The current date and time as a RFC3339 formatted string, suitable for use in front matter.
  161 	Date string
  162 
  163 	// The temporary page. Note that only the file path information is relevant at this stage.
  164 	Page page.Page
  165 
  166 	// File is the same as Page.File, embedded here for historic reasons.
  167 	// TODO(bep) make this a method.
  168 	source.File
  169 }
  170 
  171 func (f *archetypeFileData) Site() page.Site {
  172 	return f.Page.Site()
  173 }
  174 
  175 func (f *archetypeFileData) Name() string {
  176 	return f.Page.File().ContentBaseName()
  177 }