hugo

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

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

postpub.go (4720B)

    1 // Copyright 2020 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 postpub
   15 
   16 import (
   17 	"fmt"
   18 	"reflect"
   19 	"strconv"
   20 	"strings"
   21 
   22 	"github.com/spf13/cast"
   23 
   24 	"github.com/gohugoio/hugo/common/hreflect"
   25 	"github.com/gohugoio/hugo/common/maps"
   26 	"github.com/gohugoio/hugo/media"
   27 	"github.com/gohugoio/hugo/resources/resource"
   28 )
   29 
   30 type PostPublishedResource interface {
   31 	resource.ResourceTypeProvider
   32 	resource.ResourceLinksProvider
   33 	resource.ResourceMetaProvider
   34 	resource.ResourceParamsProvider
   35 	resource.ResourceDataProvider
   36 	resource.OriginProvider
   37 
   38 	MediaType() map[string]any
   39 }
   40 
   41 const (
   42 	PostProcessPrefix = "__h_pp_l1"
   43 
   44 	// The suffix has an '=' in it to prevent the minifier to remove any enclosing
   45 	// quoutes around the attribute values.
   46 	// See issue #8884.
   47 	PostProcessSuffix = "__e="
   48 )
   49 
   50 func NewPostPublishResource(id int, r resource.Resource) PostPublishedResource {
   51 	return &PostPublishResource{
   52 		prefix:   PostProcessPrefix + "_" + strconv.Itoa(id) + "_",
   53 		delegate: r,
   54 	}
   55 }
   56 
   57 // postPublishResource holds a Resource to be transformed post publishing.
   58 type PostPublishResource struct {
   59 	prefix   string
   60 	delegate resource.Resource
   61 }
   62 
   63 func (r *PostPublishResource) field(name string) string {
   64 	return r.prefix + name + PostProcessSuffix
   65 }
   66 
   67 func (r *PostPublishResource) Permalink() string {
   68 	return r.field("Permalink")
   69 }
   70 
   71 func (r *PostPublishResource) RelPermalink() string {
   72 	return r.field("RelPermalink")
   73 }
   74 
   75 func (r *PostPublishResource) Origin() resource.Resource {
   76 	return r.delegate
   77 }
   78 
   79 func (r *PostPublishResource) GetFieldString(pattern string) (string, bool) {
   80 	if r == nil {
   81 		panic("resource is nil")
   82 	}
   83 	prefixIdx := strings.Index(pattern, r.prefix)
   84 	if prefixIdx == -1 {
   85 		// Not a method on this resource.
   86 		return "", false
   87 	}
   88 
   89 	fieldAccessor := pattern[prefixIdx+len(r.prefix) : strings.Index(pattern, PostProcessSuffix)]
   90 
   91 	d := r.delegate
   92 	switch {
   93 	case fieldAccessor == "RelPermalink":
   94 		return d.RelPermalink(), true
   95 	case fieldAccessor == "Permalink":
   96 		return d.Permalink(), true
   97 	case fieldAccessor == "Name":
   98 		return d.Name(), true
   99 	case fieldAccessor == "Title":
  100 		return d.Title(), true
  101 	case fieldAccessor == "ResourceType":
  102 		return d.ResourceType(), true
  103 	case fieldAccessor == "Content":
  104 		content, err := d.(resource.ContentProvider).Content()
  105 		if err != nil {
  106 			return "", true
  107 		}
  108 		return cast.ToString(content), true
  109 	case strings.HasPrefix(fieldAccessor, "MediaType"):
  110 		return r.fieldToString(d.MediaType(), fieldAccessor), true
  111 	case fieldAccessor == "Data.Integrity":
  112 		return cast.ToString((d.Data().(map[string]any)["Integrity"])), true
  113 	default:
  114 		panic(fmt.Sprintf("unknown field accessor %q", fieldAccessor))
  115 	}
  116 }
  117 
  118 func (r *PostPublishResource) fieldToString(receiver any, path string) string {
  119 	fieldname := strings.Split(path, ".")[1]
  120 
  121 	receiverv := reflect.ValueOf(receiver)
  122 	switch receiverv.Kind() {
  123 	case reflect.Map:
  124 		v := receiverv.MapIndex(reflect.ValueOf(fieldname))
  125 		return cast.ToString(v.Interface())
  126 	default:
  127 		v := receiverv.FieldByName(fieldname)
  128 		if !v.IsValid() {
  129 			method := hreflect.GetMethodByName(receiverv, fieldname)
  130 			if method.IsValid() {
  131 				vals := method.Call(nil)
  132 				if len(vals) > 0 {
  133 					v = vals[0]
  134 				}
  135 
  136 			}
  137 		}
  138 
  139 		if v.IsValid() {
  140 			return cast.ToString(v.Interface())
  141 		}
  142 		return ""
  143 	}
  144 }
  145 
  146 func (r *PostPublishResource) Data() any {
  147 	m := map[string]any{
  148 		"Integrity": "",
  149 	}
  150 	insertFieldPlaceholders("Data", m, r.field)
  151 	return m
  152 }
  153 
  154 func (r *PostPublishResource) MediaType() map[string]any {
  155 	m := structToMapWithPlaceholders("MediaType", media.Type{}, r.field)
  156 	return m
  157 }
  158 
  159 func (r *PostPublishResource) ResourceType() string {
  160 	return r.field("ResourceType")
  161 }
  162 
  163 func (r *PostPublishResource) Name() string {
  164 	return r.field("Name")
  165 }
  166 
  167 func (r *PostPublishResource) Title() string {
  168 	return r.field("Title")
  169 }
  170 
  171 func (r *PostPublishResource) Params() maps.Params {
  172 	panic(r.fieldNotSupported("Params"))
  173 }
  174 
  175 func (r *PostPublishResource) Content() (any, error) {
  176 	return r.field("Content"), nil
  177 }
  178 
  179 func (r *PostPublishResource) fieldNotSupported(name string) string {
  180 	return fmt.Sprintf("method .%s is currently not supported in post-publish transformations.", name)
  181 }