ref: 4f66f790b168005efb835b2499c4a502e492b747
parent: a89035bdaaa8bb1525a74d82e068ef80bfa28aed
author: Bjørn Erik Pedersen <[email protected]>
date: Mon Mar 21 20:28:42 EDT 2016
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
--- a/commands/gendoc.go
+++ b/commands/gendoc.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.
@@ -51,9 +51,9 @@
if !strings.HasSuffix(gendocdir, helpers.FilePathSeparator) {
gendocdir += helpers.FilePathSeparator
}
- if found, _ := helpers.Exists(gendocdir, hugofs.OsFs); !found {
+ if found, _ := helpers.Exists(gendocdir, hugofs.Os()); !found {
jww.FEEDBACK.Println("Directory", gendocdir, "does not exist, creating...")
- hugofs.OsFs.MkdirAll(gendocdir, 0777)
+ hugofs.Os().MkdirAll(gendocdir, 0777)
}
now := time.Now().Format(time.RFC3339)
prepender := func(filename string) string {
--- a/commands/genman.go
+++ b/commands/genman.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.
@@ -41,9 +41,9 @@
if !strings.HasSuffix(genmandir, helpers.FilePathSeparator) {
genmandir += helpers.FilePathSeparator
}
- if found, _ := helpers.Exists(genmandir, hugofs.OsFs); !found {
+ if found, _ := helpers.Exists(genmandir, hugofs.Os()); !found {
jww.FEEDBACK.Println("Directory", genmandir, "does not exist, creating...")
- hugofs.OsFs.MkdirAll(genmandir, 0777)
+ hugofs.Os().MkdirAll(genmandir, 0777)
}
cmd.Root().DisableAutoGenTag = true
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -423,7 +423,7 @@
if helpers.FilePathSeparator != cacheDir[len(cacheDir)-1:] {
cacheDir = cacheDir + helpers.FilePathSeparator
}
- isDir, err := helpers.DirExists(cacheDir, hugofs.SourceFs)
+ isDir, err := helpers.DirExists(cacheDir, hugofs.Source())
utils.CheckErr(err)
if isDir == false {
mkdir(cacheDir)
@@ -430,7 +430,7 @@
}
viper.Set("CacheDir", cacheDir)
} else {
- viper.Set("CacheDir", helpers.GetTempDir("hugo_cache", hugofs.SourceFs))
+ viper.Set("CacheDir", helpers.GetTempDir("hugo_cache", hugofs.Source()))
}
if verboseLog || logging || (viper.IsSet("LogFile") && viper.GetString("LogFile") != "") {
@@ -453,6 +453,9 @@
jww.INFO.Println("Using config file:", viper.ConfigFileUsed())
+ // Init file systems. This may be changed at a later point.
+ hugofs.InitDefaultFs()
+
themeDir := helpers.GetThemeDir()
if themeDir != "" {
if _, err := os.Stat(themeDir); os.IsNotExist(err) {
@@ -498,7 +501,7 @@
// This is only used for benchmark testing. Cause the content is only visible
// in memory
if renderToMemory {
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.SetDestination(new(afero.MemMapFs))
// Rendering to memoryFS, publish to Root regardless of publishDir.
viper.Set("PublishDir", "/")
}
@@ -524,7 +527,7 @@
}
func getStaticSourceFs() afero.Fs {
- source := hugofs.SourceFs
+ source := hugofs.Source()
themeDir, err := helpers.GetThemeStaticDirPath()
staticDir := helpers.GetStaticDirPath() + helpers.FilePathSeparator
@@ -563,8 +566,8 @@
jww.INFO.Println("using a UnionFS for static directory comprised of:")
jww.INFO.Println("Base:", themeDir)
jww.INFO.Println("Overlay:", staticDir)
- base := afero.NewReadOnlyFs(afero.NewBasePathFs(hugofs.SourceFs, themeDir))
- overlay := afero.NewReadOnlyFs(afero.NewBasePathFs(hugofs.SourceFs, staticDir))
+ base := afero.NewReadOnlyFs(afero.NewBasePathFs(hugofs.Source(), themeDir))
+ overlay := afero.NewReadOnlyFs(afero.NewBasePathFs(hugofs.Source(), staticDir))
return afero.NewCopyOnWriteFs(base, overlay)
}
@@ -587,7 +590,7 @@
syncer := fsync.NewSyncer()
syncer.NoTimes = viper.GetBool("notimes")
syncer.SrcFs = staticSourceFs
- syncer.DestFs = hugofs.DestinationFS
+ syncer.DestFs = hugofs.Destination()
// Now that we are using a unionFs for the static directories
// We can effectively clean the publishDir on initial sync
syncer.Delete = viper.GetBool("cleanDestinationDir")
@@ -653,12 +656,12 @@
return nil
}
- helpers.SymbolicWalk(hugofs.SourceFs, dataDir, walker)
- helpers.SymbolicWalk(hugofs.SourceFs, helpers.AbsPathify(viper.GetString("ContentDir")), walker)
- helpers.SymbolicWalk(hugofs.SourceFs, helpers.AbsPathify(viper.GetString("LayoutDir")), walker)
- helpers.SymbolicWalk(hugofs.SourceFs, helpers.AbsPathify(viper.GetString("StaticDir")), walker)
+ helpers.SymbolicWalk(hugofs.Source(), dataDir, walker)
+ helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("ContentDir")), walker)
+ helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("LayoutDir")), walker)
+ helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("StaticDir")), walker)
if helpers.ThemeSet() {
- helpers.SymbolicWalk(hugofs.SourceFs, helpers.AbsPathify(viper.GetString("themesDir")+"/"+viper.GetString("theme")), walker)
+ helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("themesDir")+"/"+viper.GetString("theme")), walker)
}
return a
@@ -770,8 +773,8 @@
// recursively add new directories to watch list
// When mkdir -p is used, only the top directory triggers an event (at least on OSX)
if ev.Op&fsnotify.Create == fsnotify.Create {
- if s, err := hugofs.SourceFs.Stat(ev.Name); err == nil && s.Mode().IsDir() {
- helpers.SymbolicWalk(hugofs.SourceFs, ev.Name, walkAdder)
+ if s, err := hugofs.Source().Stat(ev.Name); err == nil && s.Mode().IsDir() {
+ helpers.SymbolicWalk(hugofs.Source(), ev.Name, walkAdder)
}
}
@@ -813,7 +816,7 @@
syncer := fsync.NewSyncer()
syncer.NoTimes = viper.GetBool("notimes")
syncer.SrcFs = staticSourceFs
- syncer.DestFs = hugofs.DestinationFS
+ syncer.DestFs = hugofs.Destination()
// prevent spamming the log on changes
logger := helpers.NewDistinctFeedbackLogger()
@@ -858,7 +861,7 @@
// If file doesn't exist in any static dir, remove it
toRemove := filepath.Join(publishDir, relPath)
logger.Println("File no longer exists in static dir, removing", toRemove)
- hugofs.DestinationFS.RemoveAll(toRemove)
+ hugofs.Destination().RemoveAll(toRemove)
} else if err == nil {
// If file still exists, sync it
logger.Println("Syncing", relPath, "to", publishDir)
@@ -939,7 +942,7 @@
themeDir := helpers.GetThemeDir()
- fs := hugofs.SourceFs
+ fs := hugofs.Source()
path := filepath.Join(themeDir, "theme.toml")
exists, err := helpers.Exists(path, fs)
--- a/commands/import_jekyll.go
+++ b/commands/import_jekyll.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.
@@ -124,7 +124,7 @@
return convertJekyllPost(path, relPath, targetDir, draft)
}
- err = helpers.SymbolicWalk(hugofs.OsFs, jekyllRoot, callback)
+ err = helpers.SymbolicWalk(hugofs.Os(), jekyllRoot, callback)
if err != nil {
return err
@@ -139,7 +139,7 @@
// TODO: Consider calling doNewSite() instead?
func createSiteFromJekyll(jekyllRoot, targetDir string, force bool) error {
- fs := hugofs.SourceFs
+ fs := hugofs.Source()
if exists, _ := helpers.Exists(targetDir, fs); exists {
if isDir, _ := helpers.IsDir(targetDir, fs); !isDir {
return errors.New("Target path \"" + targetDir + "\" already exists but not a directory")
@@ -187,7 +187,7 @@
}
func loadJekyllConfig(jekyllRoot string) map[string]interface{} {
- fs := hugofs.SourceFs
+ fs := hugofs.Source()
path := filepath.Join(jekyllRoot, "_config.yml")
exists, err := helpers.Exists(path, fs)
@@ -252,7 +252,7 @@
return err
}
- err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), hugofs.SourceFs)
+ err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), hugofs.Source())
if err != nil {
return
}
--- a/commands/new.go
+++ b/commands/new.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.
@@ -110,7 +110,7 @@
kind = contentType
}
- return create.NewContent(hugofs.SourceFs, kind, createpath)
+ return create.NewContent(hugofs.Source(), kind, createpath)
}
func doNewSite(basepath string, force bool) error {
@@ -123,12 +123,12 @@
filepath.Join(basepath, "themes"),
}
- if exists, _ := helpers.Exists(basepath, hugofs.SourceFs); exists {
- if isDir, _ := helpers.IsDir(basepath, hugofs.SourceFs); !isDir {
+ if exists, _ := helpers.Exists(basepath, hugofs.Source()); exists {
+ if isDir, _ := helpers.IsDir(basepath, hugofs.Source()); !isDir {
return errors.New(basepath + " already exists but not a directory")
}
- isEmpty, _ := helpers.IsEmpty(basepath, hugofs.SourceFs)
+ isEmpty, _ := helpers.IsEmpty(basepath, hugofs.Source())
switch {
case !isEmpty && !force:
@@ -137,7 +137,7 @@
case !isEmpty && force:
all := append(dirs, filepath.Join(basepath, "config."+configFormat))
for _, path := range all {
- if exists, _ := helpers.Exists(path, hugofs.SourceFs); exists {
+ if exists, _ := helpers.Exists(path, hugofs.Source()); exists {
return errors.New(path + " already exists")
}
}
@@ -145,7 +145,7 @@
}
for _, dir := range dirs {
- hugofs.SourceFs.MkdirAll(dir, 0777)
+ hugofs.Source().MkdirAll(dir, 0777)
}
createConfig(basepath, configFormat)
@@ -185,7 +185,7 @@
createpath := helpers.AbsPathify(filepath.Join(viper.GetString("themesDir"), args[0]))
jww.INFO.Println("creating theme at", createpath)
- if x, _ := helpers.Exists(createpath, hugofs.SourceFs); x {
+ if x, _ := helpers.Exists(createpath, hugofs.Source()); x {
return newUserError(createpath, "already exists")
}
@@ -204,7 +204,7 @@
archDefault := []byte("+++\n+++\n")
- err := helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), hugofs.SourceFs)
+ err := helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), hugofs.Source())
if err != nil {
return err
}
@@ -234,7 +234,7 @@
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
`)
- err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE.md"), bytes.NewReader(by), hugofs.SourceFs)
+ err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE.md"), bytes.NewReader(by), hugofs.Source())
if err != nil {
return err
}
@@ -256,7 +256,7 @@
func touchFile(x ...string) {
inpath := filepath.Join(x...)
mkdir(filepath.Dir(inpath))
- err := helpers.WriteToDisk(inpath, bytes.NewReader([]byte{}), hugofs.SourceFs)
+ err := helpers.WriteToDisk(inpath, bytes.NewReader([]byte{}), hugofs.Source())
if err != nil {
jww.FATAL.Fatalln(err)
}
@@ -287,7 +287,7 @@
repo = ""
`)
- err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), hugofs.SourceFs)
+ err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), hugofs.Source())
if err != nil {
return
}
@@ -321,7 +321,7 @@
return err
}
- err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), hugofs.SourceFs)
+ err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), hugofs.Source())
if err != nil {
return
}
--- a/commands/new_test.go
+++ b/commands/new_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,7 +18,6 @@
"path/filepath"
"testing"
- "github.com/spf13/afero"
"github.com/spf13/hugo/hugofs"
"github.com/stretchr/testify/assert"
)
@@ -41,7 +40,7 @@
}
for _, path := range paths {
- _, err := hugofs.SourceFs.Stat(path)
+ _, err := hugofs.Source().Stat(path)
assert.Nil(t, err)
}
}
@@ -48,7 +47,7 @@
func TestDoNewSite(t *testing.T) {
basepath := filepath.Join(os.TempDir(), "blog")
- hugofs.SourceFs = new(afero.MemMapFs)
+ hugofs.InitMemFs()
err := doNewSite(basepath, false)
assert.Nil(t, err)
@@ -57,8 +56,8 @@
func TestDoNewSite_noerror_base_exists_but_empty(t *testing.T) {
basepath := filepath.Join(os.TempDir(), "blog")
- hugofs.SourceFs = new(afero.MemMapFs)
- hugofs.SourceFs.MkdirAll(basepath, 777)
+ hugofs.InitMemFs()
+ hugofs.Source().MkdirAll(basepath, 777)
err := doNewSite(basepath, false)
assert.Nil(t, err)
}
@@ -65,9 +64,9 @@
func TestDoNewSite_error_base_exists(t *testing.T) {
basepath := filepath.Join(os.TempDir(), "blog")
- hugofs.SourceFs = new(afero.MemMapFs)
- hugofs.SourceFs.MkdirAll(basepath, 777)
- hugofs.SourceFs.Create(filepath.Join(basepath, "foo"))
+ hugofs.InitMemFs()
+ hugofs.Source().MkdirAll(basepath, 777)
+ hugofs.Source().Create(filepath.Join(basepath, "foo"))
// Since the directory already exists and isn't empty, expect an error
err := doNewSite(basepath, false)
assert.NotNil(t, err)
@@ -75,8 +74,8 @@
func TestDoNewSite_force_empty_dir(t *testing.T) {
basepath := filepath.Join(os.TempDir(), "blog")
- hugofs.SourceFs = new(afero.MemMapFs)
- hugofs.SourceFs.MkdirAll(basepath, 777)
+ hugofs.InitMemFs()
+ hugofs.Source().MkdirAll(basepath, 777)
err := doNewSite(basepath, true)
assert.Nil(t, err)
@@ -86,8 +85,8 @@
func TestDoNewSite_error_force_dir_inside_exists(t *testing.T) {
basepath := filepath.Join(os.TempDir(), "blog")
contentPath := filepath.Join(basepath, "content")
- hugofs.SourceFs = new(afero.MemMapFs)
- hugofs.SourceFs.MkdirAll(contentPath, 777)
+ hugofs.InitMemFs()
+ hugofs.Source().MkdirAll(contentPath, 777)
err := doNewSite(basepath, true)
assert.NotNil(t, err)
}
@@ -95,9 +94,9 @@
func TestDoNewSite_error_force_config_inside_exists(t *testing.T) {
basepath := filepath.Join(os.TempDir(), "blog")
configPath := filepath.Join(basepath, "config.toml")
- hugofs.SourceFs = new(afero.MemMapFs)
- hugofs.SourceFs.MkdirAll(basepath, 777)
- hugofs.SourceFs.Create(configPath)
+ hugofs.InitMemFs()
+ hugofs.Source().MkdirAll(basepath, 777)
+ hugofs.Source().Create(configPath)
err := doNewSite(basepath, true)
assert.NotNil(t, err)
}
--- a/commands/server.go
+++ b/commands/server.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.
@@ -152,7 +152,7 @@
// Hugo writes the output to memory instead of the disk
if !renderToDisk {
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.SetDestination(new(afero.MemMapFs))
// Rendering to memoryFS, publish to Root regardless of publishDir.
viper.Set("PublishDir", "/")
}
@@ -191,7 +191,7 @@
jww.FEEDBACK.Println("Serving pages from memory")
}
- httpFs := afero.NewHttpFs(hugofs.DestinationFS)
+ httpFs := afero.NewHttpFs(hugofs.Destination())
fs := filesOnlyFs{httpFs.Dir(helpers.AbsPathify(viper.GetString("PublishDir")))}
fileserver := http.FileServer(fs)
--- a/create/content_test.go
+++ b/create/content_test.go
@@ -44,19 +44,19 @@
}
for i, c := range cases {
- err = create.NewContent(hugofs.SourceFs, c.kind, c.path)
+ err = create.NewContent(hugofs.Source(), c.kind, c.path)
if err != nil {
t.Errorf("[%d] NewContent: %s", i, err)
}
fname := filepath.Join(os.TempDir(), "content", filepath.FromSlash(c.path))
- _, err = hugofs.SourceFs.Stat(fname)
+ _, err = hugofs.Source().Stat(fname)
if err != nil {
t.Errorf("[%d] Stat: %s", i, err)
}
for _, v := range c.resultStrings {
- found, err := afero.FileContainsBytes(hugofs.SourceFs, fname, []byte(v))
+ found, err := afero.FileContainsBytes(hugofs.Source(), fname, []byte(v))
if err != nil {
t.Errorf("[%d] FileContainsBytes: %s", i, err)
}
@@ -77,7 +77,7 @@
}
func initFs() error {
- hugofs.SourceFs = new(afero.MemMapFs)
+ hugofs.SetSource(new(afero.MemMapFs))
perm := os.FileMode(0755)
var err error
@@ -89,7 +89,7 @@
}
for _, dir := range dirs {
dir = filepath.Join(os.TempDir(), dir)
- err = hugofs.SourceFs.Mkdir(dir, perm)
+ err = hugofs.Source().Mkdir(dir, perm)
if err != nil {
return err
}
@@ -109,7 +109,7 @@
content: "+++\n+++\n",
},
} {
- f, err := hugofs.SourceFs.Create(v.path)
+ f, err := hugofs.Source().Create(v.path)
if err != nil {
return err
}
--- a/docs/content/templates/functions.md
+++ b/docs/content/templates/functions.md
@@ -83,9 +83,9 @@
or create a map on the fly to pass into
{{partial "foo" (dict "important" "Smiles" "content" "You should do more")}}
-
+
### slice
`slice` allows you to create an array (`[]interface{}`) of all arguments that you pass to this function.
@@ -336,6 +336,12 @@
{{ .Content }}
{{ end }}
+## Files
+### readFile
+Reads a file from disk and converts it into a string. Note that the filename must be relative to the current project working dir.
+ So, if you have a file with the name `README.txt` in the root of your project with the content `Hugo Rocks!`:
+
+ `{{readFile "README.txt"}}` → `"Hugo Rocks!"`
## Math
--- a/helpers/pygments.go
+++ b/helpers/pygments.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.
@@ -60,7 +60,7 @@
io.WriteString(hash, lang)
io.WriteString(hash, options)
- fs := hugofs.OsFs
+ fs := hugofs.Os()
cacheDir := viper.GetString("CacheDir")
var cachefile string
--- a/hugofs/fs.go
+++ b/hugofs/fs.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.
@@ -13,8 +13,81 @@
package hugofs
-import "github.com/spf13/afero"
+import (
+ "github.com/spf13/afero"
+ "github.com/spf13/viper"
+)
-var SourceFs afero.Fs = new(afero.OsFs)
-var DestinationFS afero.Fs = new(afero.OsFs)
-var OsFs afero.Fs = new(afero.OsFs)
+var (
+ sourceFs afero.Fs
+ destinationFs afero.Fs
+ osFs afero.Fs = &afero.OsFs{}
+ workingDirFs *afero.BasePathFs
+)
+
+// Source returns Hugo's source file system.
+func Source() afero.Fs {
+ return sourceFs
+}
+
+// SetSource sets Hugo's source file system
+// and re-initializes dependent file systems.
+func SetSource(fs afero.Fs) {
+ sourceFs = fs
+ initSourceDependencies()
+}
+
+// Destination returns Hugo's destionation file system.
+func Destination() afero.Fs {
+ return destinationFs
+}
+
+// SetDestination sets Hugo's destionation file system
+func SetDestination(fs afero.Fs) {
+ destinationFs = fs
+}
+
+// Os returns an OS file system.
+func Os() afero.Fs {
+ return osFs
+}
+
+// WorkingDir returns a read-only file system
+// restricted to the project working dir.
+func WorkingDir() *afero.BasePathFs {
+ return workingDirFs
+}
+
+// InitFs initializes with the OS file system
+// as source and destination file systems.
+func InitDefaultFs() {
+ InitFs(&afero.OsFs{})
+}
+
+// InitMemFs initializes with a MemMapFs as source and destination file systems.
+// Useful for testing.
+func InitMemFs() {
+ InitFs(&afero.MemMapFs{})
+}
+
+// InitFs initializes with the given file system
+// as source and destination file systems.
+func InitFs(fs afero.Fs) {
+ sourceFs = fs
+ destinationFs = fs
+
+ initSourceDependencies()
+}
+
+func initSourceDependencies() {
+ workingDir := viper.GetString("WorkingDir")
+
+ if workingDir != "" {
+ workingDirFs = afero.NewBasePathFs(afero.NewReadOnlyFs(sourceFs), workingDir).(*afero.BasePathFs)
+ }
+
+}
+
+func init() {
+ InitDefaultFs()
+}
--- /dev/null
+++ b/hugofs/fs_test.go
@@ -1,0 +1,72 @@
+// 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.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugofs
+
+import (
+ "github.com/spf13/afero"
+ "github.com/spf13/viper"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestInitDefault(t *testing.T) {
+ viper.Reset()
+ defer viper.Reset()
+
+ InitDefaultFs()
+
+ assert.IsType(t, new(afero.OsFs), Source())
+ assert.IsType(t, new(afero.OsFs), Destination())
+ assert.IsType(t, new(afero.OsFs), Os())
+ assert.Nil(t, WorkingDir())
+}
+
+func TestInitMemFs(t *testing.T) {
+ viper.Reset()
+ defer viper.Reset()
+
+ InitMemFs()
+
+ assert.IsType(t, new(afero.MemMapFs), Source())
+ assert.IsType(t, new(afero.MemMapFs), Destination())
+ assert.IsType(t, new(afero.OsFs), Os())
+ assert.Nil(t, WorkingDir())
+}
+
+func TestSetSource(t *testing.T) {
+
+ InitMemFs()
+
+ SetSource(new(afero.OsFs))
+ assert.IsType(t, new(afero.OsFs), Source())
+}
+
+func TestSetDestination(t *testing.T) {
+
+ InitMemFs()
+
+ SetDestination(new(afero.OsFs))
+ assert.IsType(t, new(afero.OsFs), Destination())
+}
+
+func TestWorkingDir(t *testing.T) {
+ viper.Reset()
+ defer viper.Reset()
+
+ viper.Set("WorkingDir", "/a/b/")
+
+ InitMemFs()
+
+ assert.IsType(t, new(afero.BasePathFs), WorkingDir())
+}
--- a/hugolib/handler_test.go
+++ b/hugolib/handler_test.go
@@ -17,7 +17,6 @@
"path/filepath"
"testing"
- "github.com/spf13/afero"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
@@ -29,7 +28,7 @@
viper.Reset()
defer viper.Reset()
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
sources := []source.ByteSource{
{filepath.FromSlash("sect/doc1.html"), []byte("---\nmarkup: markdown\n---\n# title\nsome *content*")},
{filepath.FromSlash("sect/doc2.html"), []byte("<!doctype html><html><body>more content</body></html>")},
@@ -75,7 +74,7 @@
}
for _, test := range tests {
- file, err := hugofs.DestinationFS.Open(test.doc)
+ file, err := hugofs.Destination().Open(test.doc)
if err != nil {
t.Fatalf("Did not find %s in target.", test.doc)
}
--- a/hugolib/menu_test.go
+++ b/hugolib/menu_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.
@@ -22,7 +22,6 @@
"github.com/BurntSushi/toml"
"github.com/kr/pretty"
- "github.com/spf13/afero"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
"github.com/spf13/viper"
@@ -684,7 +683,7 @@
}
func createTestSite(pageSources []source.ByteSource) *Site {
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
s := &Site{
Source: &source.InMemorySource{ByteSource: pageSources},
--- a/hugolib/page.go
+++ b/hugolib/page.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.
@@ -890,9 +890,9 @@
jww.INFO.Println("creating", inpath)
if safe {
- err = helpers.SafeWriteToDisk(inpath, bytes.NewReader(by), hugofs.SourceFs)
+ err = helpers.SafeWriteToDisk(inpath, bytes.NewReader(by), hugofs.Source())
} else {
- err = helpers.WriteToDisk(inpath, bytes.NewReader(by), hugofs.SourceFs)
+ err = helpers.WriteToDisk(inpath, bytes.NewReader(by), hugofs.Source())
}
if err != nil {
return
--- a/hugolib/robotstxt_test.go
+++ b/hugolib/robotstxt_test.go
@@ -1,3 +1,16 @@
+// 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.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
package hugolib
import (
@@ -4,7 +17,6 @@
"bytes"
"testing"
- "github.com/spf13/afero"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
@@ -21,7 +33,7 @@
viper.Reset()
defer viper.Reset()
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
viper.Set("baseurl", "http://auth/bub/")
@@ -53,7 +65,7 @@
t.Fatalf("Unable to RenderRobotsTXT :%s", err)
}
- robotsFile, err := hugofs.DestinationFS.Open("robots.txt")
+ robotsFile, err := hugofs.Destination().Open("robots.txt")
if err != nil {
t.Fatalf("Unable to locate: robots.txt")
--- a/hugolib/rss_test.go
+++ b/hugolib/rss_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.
@@ -17,7 +17,6 @@
"bytes"
"testing"
- "github.com/spf13/afero"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
@@ -53,7 +52,7 @@
viper.Set("baseurl", "http://auth/bub/")
viper.Set("RSSUri", rssURI)
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
s := &Site{
Source: &source.InMemorySource{ByteSource: WEIGHTED_SOURCES},
}
@@ -72,7 +71,7 @@
t.Fatalf("Unable to RenderHomePage: %s", err)
}
- file, err := hugofs.DestinationFS.Open(rssURI)
+ file, err := hugofs.Destination().Open(rssURI)
if err != nil {
t.Fatalf("Unable to locate: %s", rssURI)
--- a/hugolib/shortcode_test.go
+++ b/hugolib/shortcode_test.go
@@ -487,7 +487,7 @@
createAndRenderPages(t, s)
for _, test := range tests {
- file, err := hugofs.DestinationFS.Open(test.outFile)
+ file, err := hugofs.Destination().Open(test.outFile)
if err != nil {
t.Fatalf("Did not find %s in target: %s", test.outFile, err)
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -529,7 +529,7 @@
// it's been updated
if ev.Op&fsnotify.Rename == fsnotify.Rename {
// If the file is still on disk, it's only been updated, if it's not, it's been moved
- if ex, err := afero.Exists(hugofs.SourceFs, ev.Name); !ex || err != nil {
+ if ex, err := afero.Exists(hugofs.Source(), ev.Name); !ex || err != nil {
path, _ := helpers.GetRelativePath(ev.Name, s.absContentDir())
s.RemovePageByPath(path)
continue
@@ -852,7 +852,7 @@
}
func (s *Site) checkDirectories() (err error) {
- if b, _ := helpers.DirExists(s.absContentDir(), hugofs.SourceFs); !b {
+ if b, _ := helpers.DirExists(s.absContentDir(), hugofs.Source()); !b {
return fmt.Errorf("No source directory found, expecting to find it at " + s.absContentDir())
}
return
--- a/hugolib/site_test.go
+++ b/hugolib/site_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.
@@ -25,7 +25,6 @@
"bitbucket.org/pkg/inflect"
- "github.com/spf13/afero"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
@@ -200,7 +199,7 @@
{false, TEMPLATE_FUNC, HTML("simple-template")},
}
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
for i, test := range tests {
@@ -226,7 +225,7 @@
t.Errorf("Unable to render html: %s", err)
}
- file, err := hugofs.DestinationFS.Open(filepath.FromSlash("out/index.html"))
+ file, err := hugofs.Destination().Open(filepath.FromSlash("out/index.html"))
if err != nil {
t.Errorf("Unable to open html: %s", err)
}
@@ -240,7 +239,7 @@
viper.Reset()
defer viper.Reset()
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
sources := []source.ByteSource{
{filepath.FromSlash("sect/doc1.md"), []byte("---\ntitle: doc1\ndraft: true\npublishdate: \"2414-05-29\"\n---\n# doc1\n*some content*")},
{filepath.FromSlash("sect/doc2.md"), []byte("---\ntitle: doc2\ndraft: true\npublishdate: \"2012-05-29\"\n---\n# doc2\n*some content*")},
@@ -299,7 +298,7 @@
// Issue #957
func TestCrossrefs(t *testing.T) {
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
for _, uglyURLs := range []bool{true, false} {
for _, relative := range []bool{true, false} {
doTestCrossrefs(t, relative, uglyURLs)
@@ -374,7 +373,7 @@
}
for _, test := range tests {
- file, err := hugofs.DestinationFS.Open(test.doc)
+ file, err := hugofs.Destination().Open(test.doc)
if err != nil {
t.Fatalf("Did not find %s in target: %s", test.doc, err)
@@ -392,7 +391,7 @@
// Issue #939
// Issue #1923
func TestShouldAlwaysHaveUglyURLs(t *testing.T) {
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
for _, uglyURLs := range []bool{true, false} {
doTestShouldAlwaysHaveUglyURLs(t, uglyURLs)
}
@@ -462,7 +461,7 @@
}
for _, test := range tests {
- file, err := hugofs.DestinationFS.Open(test.doc)
+ file, err := hugofs.Destination().Open(test.doc)
if err != nil {
t.Fatalf("Did not find %s in target: %s", test.doc, err)
}
@@ -489,7 +488,7 @@
}
func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) {
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
viper.Reset()
defer viper.Reset()
viper.Set("baseurl", "http://auth/sub/")
@@ -539,7 +538,7 @@
}
for _, test := range tests {
- file, err := hugofs.DestinationFS.Open(test.doc)
+ file, err := hugofs.Destination().Open(test.doc)
if err != nil {
t.Fatalf("Did not find %s in target: %s", test.doc, err)
}
@@ -560,7 +559,7 @@
viper.Reset()
defer viper.Reset()
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
sources := []source.ByteSource{
{filepath.FromSlash("sect/doc1.html"), []byte("---\nmarkup: markdown\n---\n# title\nsome *content*")},
{filepath.FromSlash("sect/doc2.html"), []byte("<!doctype html><html><body>more content</body></html>")},
@@ -605,7 +604,7 @@
}
for _, test := range tests {
- file, err := hugofs.DestinationFS.Open(test.doc)
+ file, err := hugofs.Destination().Open(test.doc)
if err != nil {
t.Fatalf("Did not find %s in target.", test.doc)
}
@@ -624,7 +623,7 @@
viper.Set("DefaultExtension", "html")
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
sources := []source.ByteSource{
{filepath.FromSlash("sect/doc1.html"), []byte("<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>")},
{filepath.FromSlash("content/blue/doc2.html"), []byte("---\nf: t\n---\n<!doctype html><html><body>more content</body></html>")},
@@ -662,7 +661,7 @@
for _, test := range tests {
- file, err := hugofs.DestinationFS.Open(filepath.FromSlash(test.file))
+ file, err := hugofs.Destination().Open(filepath.FromSlash(test.file))
if err != nil {
t.Fatalf("Unable to locate rendered content: %s", test.file)
}
@@ -730,7 +729,7 @@
viper.Reset()
defer viper.Reset()
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
viper.Set("baseurl", "http://auth/bub")
s := &Site{
@@ -804,7 +803,7 @@
}
}()
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
viper.Set("baseurl", "http://auth/bub")
s := &Site{
@@ -984,7 +983,7 @@
viper.Reset()
defer viper.Reset()
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
sources := []source.ByteSource{
{filepath.FromSlash("sect/doc1.md"), PAGE_WITH_WEIGHTED_TAXONOMIES_1},
{filepath.FromSlash("sect/doc2.md"), PAGE_WITH_WEIGHTED_TAXONOMIES_2},
@@ -1039,7 +1038,7 @@
}
func setupLinkingMockSite(t *testing.T) *Site {
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
sources := []source.ByteSource{
{filepath.FromSlash("index.md"), []byte("")},
{filepath.FromSlash("rootfile.md"), []byte("")},
--- a/hugolib/site_url_test.go
+++ b/hugolib/site_url_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.
@@ -19,7 +19,6 @@
"html/template"
- "github.com/spf13/afero"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/target"
@@ -88,7 +87,7 @@
viper.Reset()
defer viper.Reset()
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
viper.Set("uglyurls", false)
viper.Set("paginate", 10)
@@ -113,7 +112,7 @@
t.Errorf("Unable to render site lists: %s", err)
}
- _, err := hugofs.DestinationFS.Open("blue")
+ _, err := hugofs.Destination().Open("blue")
if err != nil {
t.Errorf("No indexed rendered.")
}
@@ -129,7 +128,7 @@
"sd3/index.html",
"sd4.html",
} {
- if _, err := hugofs.DestinationFS.Open(filepath.FromSlash(s)); err != nil {
+ if _, err := hugofs.Destination().Open(filepath.FromSlash(s)); err != nil {
t.Errorf("No alias rendered: %s", s)
}
}
--- a/hugolib/sitemap_test.go
+++ b/hugolib/sitemap_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.
@@ -17,7 +17,6 @@
"bytes"
"testing"
- "github.com/spf13/afero"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
@@ -40,7 +39,7 @@
viper.Reset()
defer viper.Reset()
- hugofs.DestinationFS = new(afero.MemMapFs)
+ hugofs.InitMemFs()
viper.Set("baseurl", "http://auth/bub/")
@@ -72,7 +71,7 @@
t.Fatalf("Unable to RenderRobotsTXT :%s", err)
}
- sitemapFile, err := hugofs.DestinationFS.Open("sitemap.xml")
+ sitemapFile, err := hugofs.Destination().Open("sitemap.xml")
if err != nil {
t.Fatalf("Unable to locate: sitemap.xml")
--- a/source/filesystem.go
+++ b/source/filesystem.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.
@@ -93,7 +93,7 @@
return err
}
- err := helpers.SymbolicWalk(hugofs.SourceFs, f.Base, walker)
+ err := helpers.SymbolicWalk(hugofs.Source(), f.Base, walker)
if err != nil {
jww.ERROR.Println(err)
--- a/target/file.go
+++ b/target/file.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.
@@ -49,7 +49,7 @@
return
}
- return helpers.WriteToDisk(translated, r, hugofs.DestinationFS)
+ return helpers.WriteToDisk(translated, r, hugofs.Destination())
}
func (fs *Filesystem) Translate(src string) (dest string, err error) {
--- a/target/htmlredirect.go
+++ b/target/htmlredirect.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.
@@ -144,5 +144,5 @@
return
}
- return helpers.WriteToDisk(path, buffer, hugofs.DestinationFS)
+ return helpers.WriteToDisk(path, buffer, hugofs.Destination())
}
--- a/target/page.go
+++ b/target/page.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.
@@ -41,7 +41,7 @@
return
}
- return helpers.WriteToDisk(translated, r, hugofs.DestinationFS)
+ return helpers.WriteToDisk(translated, r, hugofs.Destination())
}
func (pp *PagePub) Translate(src string) (dest string, err error) {
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -235,7 +235,7 @@
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 @@
}
}
- b, err := afero.ReadFile(hugofs.SourceFs, overlayFilename)
+ b, err := afero.ReadFile(hugofs.Source(), overlayFilename)
if err != nil {
return err
}
@@ -309,7 +309,7 @@
}
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
@@ -316,7 +316,7 @@
}
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 @@
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 @@
// 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 @@
}
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
}
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -24,6 +24,8 @@
"encoding/json"
"errors"
"fmt"
+ "github.com/spf13/afero"
+ "github.com/spf13/hugo/hugofs"
"html"
"html/template"
"math/rand"
@@ -1467,6 +1469,38 @@
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 @@
"plainify": plainify,
"pluralize": pluralize,
"readDir": readDir,
+ "readFile": readFileFromWorkingDir,
"ref": ref,
"relURL": func(a string) template.HTML { return template.HTML(helpers.RelURL(a)) },
"relref": relRef,
--- 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 @@
"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 @@
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 @@
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 @@
safeURL: http://gohugo.io
safeJS: (1*2)
plainify: Hello world, gophers!
+readFile: Hugo Rocks!
`
var b bytes.Buffer
@@ -2179,6 +2192,47 @@
_, err = sha1(t)
if err == nil {
t.Error("Expected error from sha1")
+ }
+ }
+}
+
+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)
+ }
}
}
}
--- 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 @@
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 @@
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 @@
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++ {
--- 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 @@
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() {
--- 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,7 +123,7 @@
{`tpl`, `{{.0.E}}`, 0, false},
} {
- hugofs.SourceFs = afero.NewMemMapFs()
+ hugofs.InitMemFs()
templ := New()
overlayTplName := "ot"
masterTplName := "mt"
@@ -130,10 +130,10 @@
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)