Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gohugoio/hugo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/tpl
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2016-03-22 02:28:42 +0300
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2016-03-31 22:24:18 +0300
commit4f66f790b168005efb835b2499c4a502e492b747 (patch)
tree2e5f808bf10ac18ff1735dd743aa63e5513737d5 /tpl
parenta89035bdaaa8bb1525a74d82e068ef80bfa28aed (diff)
Add readFile template func
This also includes a refactor of the hugofs package and its usage. The motivation for that is: The Afero filesystems are brilliant. Hugo's way of adding a dozen of global variables for the different filesystems was a mistake. In readFile (and also in some other places in Hugo today) we need a way to restrict the access inside the working dir. We could use ioutil.ReadFile and implement the path checking, checking the base path and the dots ("..") etc. But it is obviously better to use an Afero BasePathFs combined witha ReadOnlyFs. We could create a use-once-filesystem and handle the initialization ourselves, but since this is also useful to others and the initialization depends on some other global state (which would mean to create a new file system on every invocation), we might as well do it properly and encapsulate the predefined set of filesystems. This change also leads the way, if needed, to encapsulate the file systems in a struct, making it possible to have several file system sets in action at once (parallel multilanguage site building? With Moore's law and all...) Fixes #1551
Diffstat (limited to 'tpl')
-rw-r--r--tpl/template.go14
-rw-r--r--tpl/template_funcs.go35
-rw-r--r--tpl/template_funcs_test.go64
-rw-r--r--tpl/template_resources.go10
-rw-r--r--tpl/template_resources_test.go4
-rw-r--r--tpl/template_test.go8
6 files changed, 112 insertions, 23 deletions
diff --git a/tpl/template.go b/tpl/template.go
index 9266fc8d4..7fad95912 100644
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -235,7 +235,7 @@ func (t *GoHTMLTemplate) AddTemplateFileWithMaster(name, overlayFilename, master
masterTpl := t.Lookup(masterFilename)
if masterTpl == nil {
- b, err := afero.ReadFile(hugofs.SourceFs, masterFilename)
+ b, err := afero.ReadFile(hugofs.Source(), masterFilename)
if err != nil {
return err
}
@@ -248,7 +248,7 @@ func (t *GoHTMLTemplate) AddTemplateFileWithMaster(name, overlayFilename, master
}
}
- b, err := afero.ReadFile(hugofs.SourceFs, overlayFilename)
+ b, err := afero.ReadFile(hugofs.Source(), overlayFilename)
if err != nil {
return err
}
@@ -309,14 +309,14 @@ func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) er
}
case ".ace":
var innerContent, baseContent []byte
- innerContent, err := afero.ReadFile(hugofs.SourceFs, path)
+ innerContent, err := afero.ReadFile(hugofs.Source(), path)
if err != nil {
return err
}
if baseTemplatePath != "" {
- baseContent, err = afero.ReadFile(hugofs.SourceFs, baseTemplatePath)
+ baseContent, err = afero.ReadFile(hugofs.Source(), baseTemplatePath)
if err != nil {
return err
}
@@ -329,7 +329,7 @@ func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) er
return t.AddTemplateFileWithMaster(name, path, baseTemplatePath)
}
- b, err := afero.ReadFile(hugofs.SourceFs, path)
+ b, err := afero.ReadFile(hugofs.Source(), path)
if err != nil {
return err
@@ -414,7 +414,7 @@ func (t *GoHTMLTemplate) loadTemplates(absPath string, prefix string) {
// This may be a view that shouldn't have base template
// Have to look inside it to make sure
- needsBase, err := helpers.FileContainsAny(path, innerMarkers, hugofs.OsFs)
+ needsBase, err := helpers.FileContainsAny(path, innerMarkers, hugofs.Os())
if err != nil {
return err
}
@@ -442,7 +442,7 @@ func (t *GoHTMLTemplate) loadTemplates(absPath string, prefix string) {
}
for _, pathToCheck := range pathsToCheck {
- if ok, err := helpers.Exists(pathToCheck, hugofs.OsFs); err == nil && ok {
+ if ok, err := helpers.Exists(pathToCheck, hugofs.Os()); err == nil && ok {
baseTemplatePath = pathToCheck
break
}
diff --git a/tpl/template_funcs.go b/tpl/template_funcs.go
index 5fb496c14..8abd70b8c 100644
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -24,6 +24,8 @@ import (
"encoding/json"
"errors"
"fmt"
+ "github.com/spf13/afero"
+ "github.com/spf13/hugo/hugofs"
"html"
"html/template"
"math/rand"
@@ -1467,6 +1469,38 @@ func index(item interface{}, indices ...interface{}) (interface{}, error) {
return v.Interface(), nil
}
+// readFile reads the file named by filename relative to the given basepath
+// and returns the contents as a string.
+// There is a upper size limit set at 1 megabytes.
+func readFile(fs *afero.BasePathFs, filename string) (string, error) {
+ if filename == "" {
+ return "", errors.New("readFile needs a filename")
+ }
+
+ if info, err := fs.Stat(filename); err == nil {
+ if info.Size() > 1000000 {
+ return "", fmt.Errorf("File %q is too big", filename)
+ }
+ } else {
+ return "", err
+ }
+ b, err := afero.ReadFile(fs, filename)
+
+ if err != nil {
+ return "", err
+ }
+
+ return string(b), nil
+}
+
+// readFileFromWorkingDir reads the file named by filename relative to the
+// configured WorkingDir.
+// It returns the contents as a string.
+// There is a upper size limit set at 1 megabytes.
+func readFileFromWorkingDir(i interface{}) (string, error) {
+ return readFile(hugofs.WorkingDir(), cast.ToString(i))
+}
+
// safeHTMLAttr returns a given string as html/template HTMLAttr content.
//
// safeHTMLAttr is currently disabled, pending further discussion
@@ -1689,6 +1723,7 @@ func init() {
"plainify": plainify,
"pluralize": pluralize,
"readDir": readDir,
+ "readFile": readFileFromWorkingDir,
"ref": ref,
"relURL": func(a string) template.HTML { return template.HTML(helpers.RelURL(a)) },
"relref": relRef,
diff --git a/tpl/template_funcs_test.go b/tpl/template_funcs_test.go
index 77cd52a33..efb583867 100644
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -1,4 +1,4 @@
-// Copyright 2015 The Hugo Authors. All rights reserved.
+// Copyright 2016 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -18,18 +18,20 @@ import (
"encoding/base64"
"errors"
"fmt"
+ "github.com/spf13/afero"
+ "github.com/spf13/cast"
+ "github.com/spf13/hugo/hugofs"
+ "github.com/spf13/viper"
+ "github.com/stretchr/testify/assert"
"html/template"
"math/rand"
"path"
+ "path/filepath"
"reflect"
"runtime"
"strings"
"testing"
"time"
-
- "github.com/spf13/cast"
- "github.com/spf13/viper"
- "github.com/stretchr/testify/assert"
)
type tstNoStringer struct {
@@ -63,6 +65,15 @@ func TestFuncsInTemplate(t *testing.T) {
viper.Reset()
defer viper.Reset()
+ workingDir := "/home/hugo"
+
+ viper.Set("WorkingDir", workingDir)
+
+ fs := &afero.MemMapFs{}
+ hugofs.InitFs(fs)
+
+ afero.WriteFile(fs, filepath.Join(workingDir, "README.txt"), []byte("Hugo Rocks!"), 0755)
+
// Add the examples from the docs: As a smoke test and to make sure the examples work.
// TODO(bep): docs: fix title example
in :=
@@ -109,6 +120,7 @@ safeCSS: {{ "Bat&Man" | safeCSS | safeCSS }}
safeURL: {{ "http://gohugo.io" | safeURL | safeURL }}
safeJS: {{ "(1*2)" | safeJS | safeJS }}
plainify: {{ plainify "Hello <strong>world</strong>, gophers!" }}
+readFile: {{ readFile "README.txt" }}
`
expected := `chomp: <p>Blockhead</p>
dateFormat: Wednesday, Jan 21, 2015
@@ -153,6 +165,7 @@ safeCSS: Bat&amp;Man
safeURL: http://gohugo.io
safeJS: (1*2)
plainify: Hello world, gophers!
+readFile: Hugo Rocks!
`
var b bytes.Buffer
@@ -2182,3 +2195,44 @@ func TestSHA1(t *testing.T) {
}
}
}
+
+func TestReadFile(t *testing.T) {
+ viper.Reset()
+ defer viper.Reset()
+
+ workingDir := "/home/hugo"
+
+ viper.Set("WorkingDir", workingDir)
+
+ fs := &afero.MemMapFs{}
+ hugofs.InitFs(fs)
+
+ afero.WriteFile(fs, filepath.Join(workingDir, "/f/f1.txt"), []byte("f1-content"), 0755)
+ afero.WriteFile(fs, filepath.Join("/home", "f2.txt"), []byte("f2-content"), 0755)
+
+ for i, this := range []struct {
+ filename string
+ expect interface{}
+ }{
+ {"", false},
+ {"b", false},
+ {filepath.FromSlash("/f/f1.txt"), "f1-content"},
+ {filepath.FromSlash("f/f1.txt"), "f1-content"},
+ {filepath.FromSlash("../f2.txt"), false},
+ } {
+ result, err := readFileFromWorkingDir(this.filename)
+ if b, ok := this.expect.(bool); ok && !b {
+ if err == nil {
+ t.Errorf("[%d] readFile didn't return an expected error", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] readFile failed: %s", i, err)
+ continue
+ }
+ if result != this.expect {
+ t.Errorf("[%d] readFile got %q but expected %q", i, result, this.expect)
+ }
+ }
+ }
+}
diff --git a/tpl/template_resources.go b/tpl/template_resources.go
index 1c0655d2c..0d0ea8332 100644
--- a/tpl/template_resources.go
+++ b/tpl/template_resources.go
@@ -1,4 +1,4 @@
-// Copyright 2015 The Hugo Authors. All rights reserved.
+// Copyright 2016 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -177,9 +177,9 @@ func resGetResource(url string) ([]byte, error) {
return nil, nil
}
if strings.Contains(url, "://") {
- return resGetRemote(url, hugofs.SourceFs, http.DefaultClient)
+ return resGetRemote(url, hugofs.Source(), http.DefaultClient)
}
- return resGetLocal(url, hugofs.SourceFs)
+ return resGetLocal(url, hugofs.Source())
}
// getJSON expects one or n-parts of a URL to a resource which can either be a local or a remote one.
@@ -201,7 +201,7 @@ func getJSON(urlParts ...string) interface{} {
jww.ERROR.Printf("Cannot read json from resource %s with error message %s", url, err)
jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)
time.Sleep(resSleep)
- resDeleteCache(url, hugofs.SourceFs)
+ resDeleteCache(url, hugofs.Source())
continue
}
break
@@ -234,7 +234,7 @@ func getCSV(sep string, urlParts ...string) [][]string {
var clearCacheSleep = func(i int, u string) {
jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)
time.Sleep(resSleep)
- resDeleteCache(url, hugofs.SourceFs)
+ resDeleteCache(url, hugofs.Source())
}
for i := 0; i <= resRetries; i++ {
diff --git a/tpl/template_resources_test.go b/tpl/template_resources_test.go
index 8e3a9d1d0..d091595b0 100644
--- a/tpl/template_resources_test.go
+++ b/tpl/template_resources_test.go
@@ -1,4 +1,4 @@
-// Copyright 2015 The Hugo Authors. All rights reserved.
+// Copyright 2016 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -214,7 +214,7 @@ type wd struct {
func testRetryWhenDone() wd {
cd := viper.GetString("CacheDir")
- viper.Set("CacheDir", helpers.GetTempDir("", hugofs.SourceFs))
+ viper.Set("CacheDir", helpers.GetTempDir("", hugofs.Source()))
var tmpSleep time.Duration
tmpSleep, resSleep = resSleep, time.Millisecond
return wd{func() {
diff --git a/tpl/template_test.go b/tpl/template_test.go
index 9649a8fa8..c96b8c87c 100644
--- a/tpl/template_test.go
+++ b/tpl/template_test.go
@@ -1,4 +1,4 @@
-// Copyright 2015 The Hugo Authors. All rights reserved.
+// Copyright 2016 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -123,17 +123,17 @@ func TestAddTemplateFileWithMaster(t *testing.T) {
{`tpl`, `{{.0.E}}`, 0, false},
} {
- hugofs.SourceFs = afero.NewMemMapFs()
+ hugofs.InitMemFs()
templ := New()
overlayTplName := "ot"
masterTplName := "mt"
finalTplName := "tp"
if this.writeSkipper != 1 {
- afero.WriteFile(hugofs.SourceFs, masterTplName, []byte(this.masterTplContent), 0644)
+ afero.WriteFile(hugofs.Source(), masterTplName, []byte(this.masterTplContent), 0644)
}
if this.writeSkipper != 2 {
- afero.WriteFile(hugofs.SourceFs, overlayTplName, []byte(this.overlayTplContent), 0644)
+ afero.WriteFile(hugofs.Source(), overlayTplName, []byte(this.overlayTplContent), 0644)
}
err := templ.AddTemplateFileWithMaster(finalTplName, overlayTplName, masterTplName)