collections via markdown files
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -130,7 +130,7 @@ func ImageProcessFilter(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *
|
||||
}
|
||||
} else {
|
||||
// local file
|
||||
imgSource = mark2web.ResolveInputPath(imgSource)
|
||||
imgSource = mark2web.CurrentTreeNode.ResolveInputPath(imgSource)
|
||||
if p.Filename == "" {
|
||||
p.Filename = fmt.Sprintf(
|
||||
"%s_%s",
|
||||
@@ -142,7 +142,7 @@ func ImageProcessFilter(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *
|
||||
|
||||
var imgTarget string
|
||||
if p.TargetDir != "" {
|
||||
imgTarget = mark2web.ResolveOutputPath(
|
||||
imgTarget = mark2web.CurrentTreeNode.ResolveOutputPath(
|
||||
path.Clean(p.TargetDir) + "/" +
|
||||
p.Filename,
|
||||
)
|
||||
@@ -158,10 +158,10 @@ func ImageProcessFilter(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *
|
||||
}
|
||||
}
|
||||
|
||||
p.Filename = mark2web.ResolveNavPath(p.TargetDir + "/" + p.Filename)
|
||||
p.Filename = mark2web.CurrentTreeNode.ResolveNavPath(p.TargetDir + "/" + p.Filename)
|
||||
|
||||
} else {
|
||||
imgTarget = mark2web.ResolveOutputPath(p.Filename)
|
||||
imgTarget = mark2web.CurrentTreeNode.ResolveOutputPath(p.Filename)
|
||||
}
|
||||
|
||||
if f, err := os.Stat(imgTarget); err == nil && !f.IsDir() {
|
||||
@@ -226,5 +226,5 @@ func ImageProcessFilter(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *
|
||||
helper.Log.Noticef("finished image: %s", imgTarget)
|
||||
})
|
||||
}
|
||||
return pongo2.AsValue(mark2web.ResolveNavPath(p.Filename)), nil
|
||||
return pongo2.AsValue(mark2web.CurrentTreeNode.ResolveNavPath(p.Filename)), nil
|
||||
}
|
||||
|
||||
@@ -36,9 +36,6 @@ func TestImageProcessFilter(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
mark2web.CurrentContext = &pongo2.Context{
|
||||
"CurrentPath": "",
|
||||
}
|
||||
|
||||
os.Remove("../../test/out/fit_300x300_q060_test.jpg")
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
// RelativePathFilter returns the relative path to navpoint based on current nav
|
||||
func RelativePathFilter(in, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
|
||||
return pongo2.AsValue(
|
||||
mark2web.ResolveNavPath(
|
||||
mark2web.CurrentTreeNode.ResolveNavPath(
|
||||
in.String(),
|
||||
),
|
||||
), nil
|
||||
|
||||
@@ -17,10 +17,23 @@ func TestRelativePathFilter(t *testing.T) {
|
||||
"testabs": "/abs",
|
||||
"testsub": "../sub/rel",
|
||||
}
|
||||
|
||||
mark2web.Config.Directories.Output = "../../test/out"
|
||||
|
||||
mark2web.CurrentTreeNode = &mark2web.TreeNode{
|
||||
InputPath: "../../test/in/content",
|
||||
OutputPath: "../../test/out/sub",
|
||||
Config: &mark2web.PathConfig{
|
||||
Imaging: &mark2web.ImagingConfig{
|
||||
Quality: 60,
|
||||
Height: 300,
|
||||
Width: 300,
|
||||
Process: "fit",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Convey("parse template", func() {
|
||||
mark2web.CurrentContext = &pongo2.Context{
|
||||
"CurrentPath": "sub",
|
||||
}
|
||||
|
||||
output, err := pongo2.RenderTemplateString("{{ testrel|relative_path }}", ctx)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
@@ -2,7 +2,6 @@ package helper
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CreateDirectory creates direcory with all missing parents and panic if error
|
||||
@@ -25,14 +24,3 @@ func CreateDirectory(dir string) {
|
||||
Log.Panicf("unknown error for output directory '%s': %s", dir, err)
|
||||
}
|
||||
}
|
||||
|
||||
// BackToRoot builds ../../ string
|
||||
func BackToRoot(curNavPath string) string {
|
||||
tmpPath := ""
|
||||
if curNavPath != "" {
|
||||
for i := strings.Count(curNavPath, "/") + 1; i > 0; i-- {
|
||||
tmpPath += "../"
|
||||
}
|
||||
}
|
||||
return tmpPath
|
||||
}
|
||||
|
||||
18
pkg/helper/regexp.go
Normal file
18
pkg/helper/regexp.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package helper
|
||||
|
||||
import "regexp"
|
||||
|
||||
// GetRegexpParams gets a map of named regexp group matches
|
||||
// use pe. (?P<Year>\d{4})-(?P<Month>\d{2})-(?P<Day>\d{2}) as regexp
|
||||
func GetRegexpParams(regEx *regexp.Regexp, str string) (paramsMap map[string]string) {
|
||||
|
||||
match := regEx.FindStringSubmatch(str)
|
||||
|
||||
paramsMap = make(map[string]string)
|
||||
for i, name := range regEx.SubexpNames() {
|
||||
if i > 0 && i <= len(match) {
|
||||
paramsMap[name] = match[i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -35,13 +35,13 @@ func ProcessAssets() {
|
||||
}
|
||||
|
||||
// fixAssetsPath replaces assets path based on current path
|
||||
func fixAssetsPath(str, curNavPath string) string {
|
||||
func (node *TreeNode) fixAssetsPath(str string) string {
|
||||
if find := Config.Assets.FixTemplate.Find; find != "" {
|
||||
helper.Log.Debugf("fixing assets paths for path '%s'", curNavPath)
|
||||
helper.Log.Debugf("fixing assets paths for path '%s'", node.CurrentNavPath())
|
||||
repl := Config.Assets.FixTemplate.Replace
|
||||
toPath := Config.Assets.ToPath
|
||||
|
||||
bToRoot := helper.BackToRoot(curNavPath)
|
||||
bToRoot := node.BackToRootPath()
|
||||
regex, err := regexp.Compile(find)
|
||||
if err != nil {
|
||||
log.Panicf("could not compile regexp '%s' for assets path: %s", find, err)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package mark2web
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gitbase.de/apairon/mark2web/pkg/helper"
|
||||
@@ -20,13 +22,12 @@ var colCache = make(map[string]*colCacheEntry)
|
||||
func (node *TreeNode) handleCollections() {
|
||||
collections := append(node.Config.Collections, node.Config.This.Collections...)
|
||||
for _, colConfig := range collections {
|
||||
if colConfig != nil {
|
||||
if colConfig.Name == nil || *colConfig.Name == "" {
|
||||
helper.Log.Panicf("missing Name in collection config in '%s'", node.InputPath)
|
||||
}
|
||||
if colConfig.URL == nil || *colConfig.URL == "" {
|
||||
helper.Log.Panicf("missing EntriesJSON in collection config in '%s'", node.InputPath)
|
||||
}
|
||||
if colConfig.Name == nil || *colConfig.Name == "" {
|
||||
helper.Log.Panicf("missing Name in collection config in '%s'", node.InputPath)
|
||||
}
|
||||
if (colConfig.URL == nil || *colConfig.URL == "") &&
|
||||
(colConfig.Directory == nil) {
|
||||
helper.Log.Panicf("missing URL and Directory in collection config in '%s'", node.InputPath)
|
||||
}
|
||||
|
||||
if node.ColMap == nil {
|
||||
@@ -36,21 +37,73 @@ func (node *TreeNode) handleCollections() {
|
||||
ctx["This"] = node.Config.This
|
||||
ctx["Data"] = node.Config.Data
|
||||
|
||||
url, err := pongo2.RenderTemplateString(*colConfig.URL, ctx)
|
||||
if err != nil {
|
||||
helper.Log.Panicf("invalid template string for Collection Element.URL in '%s': %s", node.InputPath, err)
|
||||
}
|
||||
|
||||
var colData interface{}
|
||||
if cacheEntry, ok := colCache[url]; ok {
|
||||
colData = cacheEntry.data
|
||||
cacheEntry.hit++
|
||||
} else {
|
||||
colData = helper.JSONWebRequest(url)
|
||||
colCache[url] = &colCacheEntry{
|
||||
data: colData,
|
||||
navnames: make([]string, 0),
|
||||
|
||||
errSrcText := ""
|
||||
cacheKey := ""
|
||||
|
||||
if colConfig.URL != nil {
|
||||
url, err := pongo2.RenderTemplateString(*colConfig.URL, ctx)
|
||||
if err != nil {
|
||||
helper.Log.Panicf("invalid template string for Collection Element.URL in '%s': %s", node.InputPath, err)
|
||||
}
|
||||
|
||||
errSrcText = "URL " + url
|
||||
cacheKey = url
|
||||
|
||||
if cacheEntry, ok := colCache[url]; ok {
|
||||
colData = cacheEntry.data
|
||||
cacheEntry.hit++
|
||||
} else {
|
||||
helper.Log.Noticef("reading collection from: %s", errSrcText)
|
||||
colData = helper.JSONWebRequest(url)
|
||||
colCache[url] = &colCacheEntry{
|
||||
data: colData,
|
||||
navnames: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
path := node.ResolveInputPath(colConfig.Directory.Path)
|
||||
errSrcText = "DIR " + path
|
||||
|
||||
helper.Log.Noticef("reading collection from: %s", errSrcText)
|
||||
d, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
helper.Log.Panicf("could not read directory '%s': %s", path, err)
|
||||
}
|
||||
|
||||
mStr := "."
|
||||
if colConfig.Directory.MatchFilename != "" {
|
||||
mStr = colConfig.Directory.MatchFilename
|
||||
}
|
||||
matcher, err := regexp.Compile(mStr)
|
||||
if err != nil {
|
||||
helper.Log.Panicf("could not compile regex for MatchFilename '%s' in '%s': %s", mStr, path, err)
|
||||
}
|
||||
|
||||
if colConfig.Directory.ReverseOrder {
|
||||
for i := len(d)/2 - 1; i >= 0; i-- {
|
||||
opp := len(d) - 1 - i
|
||||
d[i], d[opp] = d[opp], d[i]
|
||||
}
|
||||
}
|
||||
|
||||
fcolData := make([]pongo2.Context, 0)
|
||||
for _, fh := range d {
|
||||
if !fh.IsDir() && matcher.MatchString(fh.Name()) {
|
||||
inFile := path + "/" + fh.Name()
|
||||
md, err := ioutil.ReadFile(inFile)
|
||||
if err != nil {
|
||||
helper.Log.Panicf("could not read file '%s': %s", inFile, err)
|
||||
}
|
||||
_, ctx := node.processMarkdownWithHeader(md, inFile)
|
||||
(*ctx)["FilenameMatch"] = helper.GetRegexpParams(matcher, fh.Name())
|
||||
fcolData = append(fcolData, *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
colData = fcolData
|
||||
}
|
||||
|
||||
node.ColMap[*colConfig.Name] = colData
|
||||
@@ -64,7 +117,7 @@ func (node *TreeNode) handleCollections() {
|
||||
entries, ok = colDataMap[navT.EntriesAttribute].([]interface{})
|
||||
if !ok {
|
||||
helper.Log.Debug(spew.Sdump(colDataMap))
|
||||
helper.Log.Panicf("invalid json data in [%s] from url '%s' for entries", navT.EntriesAttribute, url)
|
||||
helper.Log.Panicf("invalid json data in [%s] from '%s' for entries", navT.EntriesAttribute, errSrcText)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -72,7 +125,7 @@ func (node *TreeNode) handleCollections() {
|
||||
}
|
||||
if !ok {
|
||||
helper.Log.Debug(spew.Sdump(colData))
|
||||
helper.Log.Panicf("invalid json data from url '%s', need array of objects for entries or object with configured NavTemplate.EntriesAttribute", url)
|
||||
helper.Log.Panicf("invalid json data from '%s', need array of objects for entries or object with configured NavTemplate.EntriesAttribute", errSrcText)
|
||||
}
|
||||
|
||||
// build navigation with detail sites
|
||||
@@ -85,7 +138,7 @@ func (node *TreeNode) handleCollections() {
|
||||
var jsonCtx map[string]interface{}
|
||||
if jsonCtx, ok = colEl.(map[string]interface{}); !ok {
|
||||
helper.Log.Debug(spew.Sdump(colEl))
|
||||
helper.Log.Panicf("no json object for entry index %d from url '%s'", idx, url)
|
||||
helper.Log.Panicf("no json object for entry index %d from '%s'", idx, errSrcText)
|
||||
}
|
||||
err = helper.Merge(&ctxE, pongo2.Context(jsonCtx))
|
||||
if err != nil {
|
||||
@@ -143,14 +196,14 @@ func (node *TreeNode) handleCollections() {
|
||||
}
|
||||
}
|
||||
|
||||
if l := len(colCache[url].navnames); colCache[url].hit > 1 &&
|
||||
if l := len(colCache[cacheKey].navnames); colCache[cacheKey].hit > 1 &&
|
||||
l > 0 &&
|
||||
navname == colCache[url].navnames[l-1] {
|
||||
navname == colCache[cacheKey].navnames[l-1] {
|
||||
// navname before used same url, so recursion loop
|
||||
helper.Log.Panicf("collection request loop detected for in '%s' for url: %s", node.InputPath, url)
|
||||
helper.Log.Panicf("collection request loop detected for in '%s' for : %s", node.InputPath, errSrcText)
|
||||
}
|
||||
|
||||
colCache[url].navnames = append(colCache[url].navnames, navname)
|
||||
colCache[cacheKey].navnames = append(colCache[cacheKey].navnames, navname)
|
||||
|
||||
node.addSubNode(tpl, goTo, navname, colEl, dataKey, body, navT.Hidden)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,18 @@ package mark2web
|
||||
|
||||
import "gitbase.de/apairon/mark2web/pkg/helper"
|
||||
|
||||
// CollectionDirectoryConfig specifies how to handle a directory of markdown files as a collection
|
||||
type CollectionDirectoryConfig struct {
|
||||
Path string `yaml:"Path"`
|
||||
MatchFilename string `yaml:"MatchFilename"`
|
||||
ReverseOrder bool `yaml:"ReverseOrder"`
|
||||
}
|
||||
|
||||
// CollectionConfig describes a collection
|
||||
type CollectionConfig struct {
|
||||
Name *string `yaml:"Name"`
|
||||
URL *string `yaml:"URL"`
|
||||
Name *string `yaml:"Name"`
|
||||
URL *string `yaml:"URL"`
|
||||
Directory *CollectionDirectoryConfig `yaml:"Directory"`
|
||||
NavTemplate *struct {
|
||||
EntriesAttribute string `yaml:"EntriesAttribute"`
|
||||
GoTo string `yaml:"GoTo"`
|
||||
|
||||
@@ -65,24 +65,104 @@ func (node *TreeNode) ReadContentDir(inBase string, outBase string, dir string,
|
||||
}
|
||||
}
|
||||
|
||||
func (node *TreeNode) processMarkdownWithHeader(md []byte, errorRef string) (*PathConfig, *pongo2.Context) {
|
||||
|
||||
newConfig := new(PathConfig)
|
||||
|
||||
headerRegex := regexp.MustCompile("(?s)^---(.*?)\\r?\\n\\r?---\\r?\\n\\r?")
|
||||
yamlData := headerRegex.Find(md)
|
||||
if string(yamlData) != "" {
|
||||
// replace tabs
|
||||
yamlData = bytes.Replace(yamlData, []byte("\t"), []byte(" "), -1)
|
||||
|
||||
helper.Log.Debugf("found yaml header in '%s', merging config", errorRef)
|
||||
err := yaml.Unmarshal(yamlData, newConfig)
|
||||
if err != nil {
|
||||
helper.Log.Panicf("could not parse YAML header from '%s': %s", errorRef, err)
|
||||
}
|
||||
|
||||
helper.Log.Debug("merging config with upper config")
|
||||
oldThis := newConfig.This
|
||||
helper.Merge(newConfig, node.Config)
|
||||
newConfig.This = oldThis
|
||||
|
||||
helper.Log.Debug(spew.Sdump(newConfig))
|
||||
|
||||
md = headerRegex.ReplaceAll(md, []byte(""))
|
||||
} else {
|
||||
helper.Merge(newConfig, node.Config)
|
||||
}
|
||||
|
||||
// use --- for splitting document in markdown parts
|
||||
regex := regexp.MustCompile("\\r?\\n\\r?---\\r?\\n\\r?")
|
||||
inputParts := regex.Split(string(md), -1)
|
||||
htmlParts := make([]*pongo2.Value, 0)
|
||||
|
||||
chromaRenderer := false
|
||||
chromaStyle := "monokai"
|
||||
if m := newConfig.Markdown; m != nil {
|
||||
if m.ChromaRenderer != nil && *m.ChromaRenderer {
|
||||
chromaRenderer = true
|
||||
}
|
||||
if m.ChromaStyle != nil && *m.ChromaStyle != "" {
|
||||
chromaStyle = *m.ChromaStyle
|
||||
}
|
||||
}
|
||||
for _, iPart := range inputParts {
|
||||
htmlParts = append(htmlParts,
|
||||
pongo2.AsSafeValue(
|
||||
string(helper.RenderMarkdown([]byte(iPart), chromaRenderer, chromaStyle))))
|
||||
}
|
||||
|
||||
// build navigation
|
||||
navMap := make(map[string]*NavElement)
|
||||
navSlice := make([]*NavElement, 0)
|
||||
navActive := make([]*NavElement, 0)
|
||||
node.buildNavigation(&navMap, &navSlice, &navActive)
|
||||
|
||||
// read yaml header as data for template
|
||||
ctx := NewContext()
|
||||
ctx["This"] = newConfig.This
|
||||
ctx["Meta"] = newConfig.Meta
|
||||
ctx["Markdown"] = newConfig.Markdown
|
||||
ctx["Data"] = newConfig.Data
|
||||
ctx["ColMap"] = node.root.ColMap // root as NavMap and NavSlice, for sub go to NavElement.ColMap
|
||||
ctx["NavMap"] = navMap
|
||||
ctx["NavSlice"] = navSlice
|
||||
ctx["NavActive"] = navActive
|
||||
ctx["Body"] = pongo2.AsSafeValue(string(helper.RenderMarkdown(md, chromaRenderer, chromaStyle)))
|
||||
ctx["BodyParts"] = htmlParts
|
||||
ctx["CurrentPath"] = node.CurrentNavPath()
|
||||
// set active nav element
|
||||
if len(navActive) > 0 {
|
||||
ctx["NavElement"] = navActive[len(navActive)-1]
|
||||
} else {
|
||||
// if no active path to content, we are in root dir
|
||||
ctx["NavElement"] = &NavElement{
|
||||
GoTo: node.BackToRootPath(),
|
||||
Active: true,
|
||||
ColMap: node.ColMap,
|
||||
Data: node.Config.Data,
|
||||
This: node.Config.This,
|
||||
SubMap: &navMap,
|
||||
SubSlice: &navSlice,
|
||||
}
|
||||
}
|
||||
|
||||
return newConfig, &ctx
|
||||
}
|
||||
|
||||
// ProcessContent walks recursivly through the input paths and processes all files for output
|
||||
func (node *TreeNode) ProcessContent() {
|
||||
helper.CreateDirectory(node.OutputPath)
|
||||
|
||||
curNavPath := strings.TrimPrefix(node.OutputPath, Config.Directories.Output)
|
||||
curNavPath = strings.TrimPrefix(curNavPath, "/")
|
||||
curNavPath = path.Clean(curNavPath)
|
||||
if curNavPath == "." {
|
||||
curNavPath = ""
|
||||
}
|
||||
|
||||
if node.root != node {
|
||||
// write htaccess for rewrites, root will be written in WriteWebserverConfig()
|
||||
goTo := node.Config.This.GoTo
|
||||
if goTo != nil && *goTo != "" {
|
||||
goToFixed := *goTo
|
||||
if strings.HasPrefix(goToFixed, "/") {
|
||||
goToFixed = helper.BackToRoot(curNavPath) + goToFixed
|
||||
goToFixed = node.BackToRootPath() + goToFixed
|
||||
}
|
||||
goToFixed = path.Clean(goToFixed)
|
||||
|
||||
@@ -91,82 +171,63 @@ func (node *TreeNode) ProcessContent() {
|
||||
}
|
||||
|
||||
for _, file := range node.InputFiles {
|
||||
var input []byte
|
||||
inFile := "InputString"
|
||||
|
||||
if file != "" {
|
||||
inFile = node.InputPath + "/" + file
|
||||
helper.Log.Debugf("reading file: %s", inFile)
|
||||
|
||||
var err error
|
||||
input, err = ioutil.ReadFile(inFile)
|
||||
if err != nil {
|
||||
helper.Log.Panicf("could not read '%s':%s", inFile, err)
|
||||
}
|
||||
helper.Log.Infof("processing input file '%s'", inFile)
|
||||
} else {
|
||||
// use input string if available and input filename == ""
|
||||
var inputString *string
|
||||
if i := node.Config.Index; i != nil {
|
||||
inputString = i.InputString
|
||||
}
|
||||
if inputString != nil {
|
||||
helper.Log.Debugf("using input string instead of file")
|
||||
input = []byte(*inputString)
|
||||
}
|
||||
}
|
||||
|
||||
newConfig := new(PathConfig)
|
||||
|
||||
regex := regexp.MustCompile("(?s)^---(.*?)\\r?\\n\\r?---\\r?\\n\\r?")
|
||||
yamlData := regex.Find(input)
|
||||
if string(yamlData) != "" {
|
||||
// replace tabs
|
||||
yamlData = bytes.Replace(yamlData, []byte("\t"), []byte(" "), -1)
|
||||
|
||||
helper.Log.Debugf("found yaml header in '%s', merging config", inFile)
|
||||
err := yaml.Unmarshal(yamlData, newConfig)
|
||||
if err != nil {
|
||||
helper.Log.Panicf("could not parse YAML header from '%s': %s", inFile, err)
|
||||
}
|
||||
|
||||
helper.Log.Debug("merging config with upper config")
|
||||
oldThis := newConfig.This
|
||||
helper.Merge(newConfig, node.Config)
|
||||
newConfig.This = oldThis
|
||||
|
||||
helper.Log.Debug(spew.Sdump(newConfig))
|
||||
|
||||
input = regex.ReplaceAll(input, []byte(""))
|
||||
} else {
|
||||
helper.Merge(newConfig, node.Config)
|
||||
}
|
||||
|
||||
// ignore ???
|
||||
ignoreFile := false
|
||||
var ignoreRegex *string
|
||||
var stripRegex *string
|
||||
var outputExt *string
|
||||
if f := newConfig.Filename; f != nil {
|
||||
ignoreRegex = f.Ignore
|
||||
stripRegex = f.Strip
|
||||
outputExt = f.OutputExtension
|
||||
}
|
||||
if ignoreRegex != nil && *ignoreRegex != "" {
|
||||
regex, err := regexp.Compile(*ignoreRegex)
|
||||
if err != nil {
|
||||
helper.Log.Panicf("could not compile filename.ignore regexp '%s' for file '%s': %s", *ignoreRegex, inFile, err)
|
||||
if file != "" {
|
||||
inFile = node.InputPath + "/" + file
|
||||
var ignoreRegex *string
|
||||
if f := node.Config.Filename; f != nil {
|
||||
ignoreRegex = f.Ignore
|
||||
}
|
||||
if ignoreRegex != nil && *ignoreRegex != "" {
|
||||
regex, err := regexp.Compile(*ignoreRegex)
|
||||
if err != nil {
|
||||
helper.Log.Panicf("could not compile filename.ignore regexp '%s' for file '%s': %s", *ignoreRegex, inFile, err)
|
||||
}
|
||||
ignoreFile = regex.MatchString(file)
|
||||
}
|
||||
ignoreFile = regex.MatchString(file)
|
||||
}
|
||||
|
||||
if ignoreFile {
|
||||
helper.Log.Infof("ignoring file '%s', because of filename.ignore", inFile)
|
||||
} else {
|
||||
var input []byte
|
||||
|
||||
if file != "" {
|
||||
helper.Log.Debugf("reading file: %s", inFile)
|
||||
|
||||
var err error
|
||||
input, err = ioutil.ReadFile(inFile)
|
||||
if err != nil {
|
||||
helper.Log.Panicf("could not read '%s':%s", inFile, err)
|
||||
}
|
||||
helper.Log.Infof("processing input file '%s'", inFile)
|
||||
} else {
|
||||
// use input string if available and input filename == ""
|
||||
var inputString *string
|
||||
if i := node.Config.Index; i != nil {
|
||||
inputString = i.InputString
|
||||
}
|
||||
if inputString != nil {
|
||||
helper.Log.Debugf("using input string instead of file")
|
||||
input = []byte(*inputString)
|
||||
}
|
||||
}
|
||||
|
||||
newConfig, ctx := node.processMarkdownWithHeader(input, inFile)
|
||||
|
||||
// build output filename
|
||||
outputFilename := file
|
||||
|
||||
var stripRegex *string
|
||||
var outputExt *string
|
||||
if f := newConfig.Filename; f != nil {
|
||||
stripRegex = f.Strip
|
||||
outputExt = f.OutputExtension
|
||||
}
|
||||
|
||||
var indexInputFile *string
|
||||
var indexOutputFile *string
|
||||
if i := newConfig.Index; i != nil {
|
||||
@@ -194,71 +255,14 @@ func (node *TreeNode) ProcessContent() {
|
||||
|
||||
outFile := node.OutputPath + "/" + outputFilename
|
||||
helper.Log.Debugf("using '%s' as output file", outFile)
|
||||
|
||||
// use --- for splitting document in markdown parts
|
||||
regex := regexp.MustCompile("\\r?\\n\\r?---\\r?\\n\\r?")
|
||||
inputParts := regex.Split(string(input), -1)
|
||||
htmlParts := make([]*pongo2.Value, 0)
|
||||
|
||||
chromaRenderer := false
|
||||
chromaStyle := "monokai"
|
||||
if m := newConfig.Markdown; m != nil {
|
||||
if m.ChromaRenderer != nil && *m.ChromaRenderer {
|
||||
chromaRenderer = true
|
||||
}
|
||||
if m.ChromaStyle != nil && *m.ChromaStyle != "" {
|
||||
chromaStyle = *m.ChromaStyle
|
||||
}
|
||||
}
|
||||
for _, iPart := range inputParts {
|
||||
htmlParts = append(htmlParts,
|
||||
pongo2.AsSafeValue(
|
||||
string(helper.RenderMarkdown([]byte(iPart), chromaRenderer, chromaStyle))))
|
||||
}
|
||||
|
||||
// build navigation
|
||||
navMap := make(map[string]*NavElement)
|
||||
navSlice := make([]*NavElement, 0)
|
||||
navActive := make([]*NavElement, 0)
|
||||
buildNavigation(node.root, &navMap, &navSlice, &navActive, curNavPath)
|
||||
|
||||
// read yaml header as data for template
|
||||
ctx := NewContext()
|
||||
ctx["This"] = newConfig.This
|
||||
ctx["Meta"] = newConfig.Meta
|
||||
ctx["Markdown"] = newConfig.Markdown
|
||||
ctx["Data"] = newConfig.Data
|
||||
ctx["ColMap"] = node.root.ColMap // root as NavMap and NavSlice, for sub go to NavElement.ColMap
|
||||
ctx["NavMap"] = navMap
|
||||
ctx["NavSlice"] = navSlice
|
||||
ctx["NavActive"] = navActive
|
||||
ctx["Body"] = pongo2.AsSafeValue(string(helper.RenderMarkdown(input, chromaRenderer, chromaStyle)))
|
||||
ctx["BodyParts"] = htmlParts
|
||||
ctx["CurrentPath"] = curNavPath
|
||||
// set active nav element
|
||||
if len(navActive) > 0 {
|
||||
ctx["NavElement"] = navActive[len(navActive)-1]
|
||||
} else {
|
||||
// if no active path to content, we are in root dir
|
||||
ctx["NavElement"] = &NavElement{
|
||||
GoTo: helper.BackToRoot(curNavPath),
|
||||
Active: true,
|
||||
ColMap: node.ColMap,
|
||||
Data: node.Config.Data,
|
||||
This: node.Config.This,
|
||||
SubMap: &navMap,
|
||||
SubSlice: &navSlice,
|
||||
}
|
||||
}
|
||||
|
||||
helper.Log.Debugf("rendering template '%s' for '%s'", *newConfig.Template, outFile)
|
||||
templateFilename := *newConfig.Template
|
||||
result, err := renderTemplate(*newConfig.Template, node, newConfig, &ctx)
|
||||
result, err := renderTemplate(*newConfig.Template, node, newConfig, ctx)
|
||||
if err != nil {
|
||||
helper.Log.Panicf("could not execute template '%s' for input file '%s': %s", templateFilename, inFile, err)
|
||||
}
|
||||
|
||||
result = fixAssetsPath(result, curNavPath)
|
||||
result = node.fixAssetsPath(result)
|
||||
|
||||
helper.Log.Noticef("writing to output file: %s", outFile)
|
||||
err = ioutil.WriteFile(outFile, []byte(result), 0644)
|
||||
|
||||
@@ -25,7 +25,11 @@ type NavElement struct {
|
||||
}
|
||||
|
||||
// buildNavigation builds the navigation trees for use in templates
|
||||
func buildNavigation(tree *TreeNode, curNavMap *map[string]*NavElement, curNavSlice *[]*NavElement, navActive *[]*NavElement, activeNav string) {
|
||||
func (node *TreeNode) buildNavigation(curNavMap *map[string]*NavElement, curNavSlice *[]*NavElement, navActive *[]*NavElement) {
|
||||
buildNavigationRecursive(node.root, curNavMap, curNavSlice, navActive, node.CurrentNavPath(), node.BackToRootPath())
|
||||
}
|
||||
|
||||
func buildNavigationRecursive(tree *TreeNode, curNavMap *map[string]*NavElement, curNavSlice *[]*NavElement, navActive *[]*NavElement, activeNav string, backToRoot string) {
|
||||
for _, el := range tree.Sub {
|
||||
if el.Hidden {
|
||||
continue // ignore hidden nav points from collections
|
||||
@@ -88,8 +92,7 @@ func buildNavigation(tree *TreeNode, curNavMap *map[string]*NavElement, curNavSl
|
||||
|
||||
if activeNav != "" && activeNav != "/" {
|
||||
// calculate relative path
|
||||
bToRoot := helper.BackToRoot(activeNav)
|
||||
navEl.GoTo = bToRoot + navEl.GoTo
|
||||
navEl.GoTo = backToRoot + navEl.GoTo
|
||||
navEl.GoTo = path.Clean(navEl.GoTo)
|
||||
}
|
||||
|
||||
@@ -98,6 +101,6 @@ func buildNavigation(tree *TreeNode, curNavMap *map[string]*NavElement, curNavSl
|
||||
*curNavSlice = append(*curNavSlice, &navEl)
|
||||
}
|
||||
|
||||
buildNavigation(el, &subMap, &subSlice, navActive, activeNav)
|
||||
buildNavigationRecursive(el, &subMap, &subSlice, navActive, activeNav, backToRoot)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,36 +3,58 @@ package mark2web
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"gitbase.de/apairon/mark2web/pkg/helper"
|
||||
)
|
||||
|
||||
// ResolveNavPath fixes nav target relative to current navigation path
|
||||
func ResolveNavPath(target string) string {
|
||||
curNavPath := (*CurrentContext)["CurrentPath"].(string)
|
||||
func (node *TreeNode) ResolveNavPath(target string) string {
|
||||
if strings.HasPrefix(target, "/") {
|
||||
target = helper.BackToRoot(curNavPath) + target
|
||||
target = node.BackToRootPath() + target
|
||||
}
|
||||
target = path.Clean(target)
|
||||
return target
|
||||
}
|
||||
|
||||
// ResolveOutputPath fixes output directory relative to current navigation path
|
||||
func ResolveOutputPath(target string) string {
|
||||
func (node *TreeNode) ResolveOutputPath(target string) string {
|
||||
if strings.HasPrefix(target, "/") {
|
||||
target = Config.Directories.Output + "/" + target
|
||||
} else {
|
||||
target = CurrentTreeNode.OutputPath + "/" + target
|
||||
target = node.OutputPath + "/" + target
|
||||
}
|
||||
return path.Clean(target)
|
||||
}
|
||||
|
||||
// ResolveInputPath fixes input directory relative to current navigation path
|
||||
func ResolveInputPath(target string) string {
|
||||
func (node *TreeNode) ResolveInputPath(target string) string {
|
||||
if strings.HasPrefix(target, "/") {
|
||||
target = Config.Directories.Input + "/" + target
|
||||
} else {
|
||||
target = CurrentTreeNode.InputPath + "/" + target
|
||||
target = node.InputPath + "/" + target
|
||||
}
|
||||
return path.Clean(target)
|
||||
}
|
||||
|
||||
// CurrentNavPath is current navigation path for this node
|
||||
func (node *TreeNode) CurrentNavPath() string {
|
||||
curNavPath := strings.TrimPrefix(node.OutputPath, Config.Directories.Output)
|
||||
curNavPath = strings.TrimPrefix(curNavPath, "/")
|
||||
curNavPath = path.Clean(curNavPath)
|
||||
if curNavPath == "." {
|
||||
curNavPath = ""
|
||||
}
|
||||
|
||||
return curNavPath
|
||||
}
|
||||
|
||||
// BackToRootPath builds ../../ string
|
||||
func (node *TreeNode) BackToRootPath() string {
|
||||
curNavPath := node.CurrentNavPath()
|
||||
|
||||
tmpPath := ""
|
||||
if curNavPath != "" {
|
||||
for i := strings.Count(curNavPath, "/") + 1; i > 0; i-- {
|
||||
tmpPath += "../"
|
||||
}
|
||||
}
|
||||
return tmpPath
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user