2019-03-21 14:55:40 +01:00
|
|
|
package mark2web
|
|
|
|
|
|
|
|
import (
|
2019-03-22 17:22:03 +01:00
|
|
|
"io/ioutil"
|
2019-03-21 14:55:40 +01:00
|
|
|
"path"
|
2019-03-22 17:22:03 +01:00
|
|
|
"regexp"
|
2019-03-21 14:55:40 +01:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"gitbase.de/apairon/mark2web/pkg/helper"
|
2019-03-25 09:28:58 +01:00
|
|
|
"gitbase.de/apairon/mark2web/pkg/logger"
|
2019-03-27 12:52:30 +01:00
|
|
|
"gitbase.de/apairon/mark2web/pkg/webrequest"
|
2019-03-21 14:55:40 +01:00
|
|
|
"github.com/davecgh/go-spew/spew"
|
2022-02-28 11:43:47 +01:00
|
|
|
"github.com/flosch/pongo2/v4"
|
2019-03-21 14:55:40 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type colCacheEntry struct {
|
|
|
|
data interface{}
|
|
|
|
hit int
|
|
|
|
navnames []string
|
|
|
|
}
|
|
|
|
|
|
|
|
var colCache = make(map[string]*colCacheEntry)
|
|
|
|
|
|
|
|
func (node *TreeNode) handleCollections() {
|
|
|
|
collections := append(node.Config.Collections, node.Config.This.Collections...)
|
|
|
|
for _, colConfig := range collections {
|
2019-03-22 17:22:03 +01:00
|
|
|
if colConfig.Name == nil || *colConfig.Name == "" {
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Exit("missing Name in collection config in '%s'", node.InputPath)
|
2019-03-22 17:22:03 +01:00
|
|
|
}
|
|
|
|
if (colConfig.URL == nil || *colConfig.URL == "") &&
|
|
|
|
(colConfig.Directory == nil) {
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Exit("missing URL and Directory in collection config in '%s'", node.InputPath)
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if node.ColMap == nil {
|
|
|
|
node.ColMap = make(helper.MapString)
|
|
|
|
}
|
|
|
|
ctx := NewContext()
|
|
|
|
ctx["This"] = node.Config.This
|
|
|
|
ctx["Data"] = node.Config.Data
|
|
|
|
|
|
|
|
var colData interface{}
|
2019-03-22 17:22:03 +01:00
|
|
|
|
|
|
|
errSrcText := ""
|
|
|
|
cacheKey := ""
|
|
|
|
|
|
|
|
if colConfig.URL != nil {
|
|
|
|
url, err := pongo2.RenderTemplateString(*colConfig.URL, ctx)
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Eexit(err, "invalid template string for Collection Element.URL in '%s'", node.InputPath)
|
2019-03-22 17:22:03 +01:00
|
|
|
|
|
|
|
errSrcText = "URL " + url
|
|
|
|
cacheKey = url
|
|
|
|
|
|
|
|
if cacheEntry, ok := colCache[url]; ok {
|
|
|
|
colData = cacheEntry.data
|
|
|
|
cacheEntry.hit++
|
|
|
|
} else {
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.N("reading collection from: %s", errSrcText)
|
2019-03-27 12:52:30 +01:00
|
|
|
colData = webrequest.GetJSON(url)
|
2019-03-22 17:22:03 +01:00
|
|
|
colCache[url] = &colCacheEntry{
|
|
|
|
data: colData,
|
|
|
|
navnames: make([]string, 0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-21 14:55:40 +01:00
|
|
|
} else {
|
2019-03-22 17:22:03 +01:00
|
|
|
path := node.ResolveInputPath(colConfig.Directory.Path)
|
|
|
|
errSrcText = "DIR " + path
|
|
|
|
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.N("reading collection from: %s", errSrcText)
|
2019-03-22 17:22:03 +01:00
|
|
|
d, err := ioutil.ReadDir(path)
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Eexit(err, "could not read directory '%s'", path)
|
2019-03-22 17:22:03 +01:00
|
|
|
|
|
|
|
mStr := "."
|
|
|
|
if colConfig.Directory.MatchFilename != "" {
|
|
|
|
mStr = colConfig.Directory.MatchFilename
|
|
|
|
}
|
|
|
|
matcher, err := regexp.Compile(mStr)
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Eexit(err, "could not compile regex for MatchFilename '%s' in '%s'", mStr, path)
|
2019-03-22 17:22:03 +01:00
|
|
|
|
|
|
|
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]
|
|
|
|
}
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
2019-03-22 17:22:03 +01:00
|
|
|
|
|
|
|
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)
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Eexit(err, "could not read file '%s'", inFile)
|
|
|
|
|
2019-03-22 17:22:03 +01:00
|
|
|
_, ctx := node.processMarkdownWithHeader(md, inFile)
|
|
|
|
(*ctx)["FilenameMatch"] = helper.GetRegexpParams(matcher, fh.Name())
|
|
|
|
fcolData = append(fcolData, *ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
colData = fcolData
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
node.ColMap[*colConfig.Name] = colData
|
|
|
|
|
|
|
|
if navT := colConfig.NavTemplate; navT != nil {
|
|
|
|
var entries []interface{}
|
|
|
|
var ok bool
|
|
|
|
if navT.EntriesAttribute != "" {
|
|
|
|
var colDataMap map[string]interface{}
|
|
|
|
if colDataMap, ok = colData.(map[string]interface{}); ok {
|
|
|
|
entries, ok = colDataMap[navT.EntriesAttribute].([]interface{})
|
|
|
|
if !ok {
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.D(spew.Sdump(colDataMap))
|
|
|
|
logger.Exit("invalid json data in [%s] from '%s' for entries", navT.EntriesAttribute, errSrcText)
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
entries, ok = colData.([]interface{})
|
|
|
|
}
|
|
|
|
if !ok {
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.D(spew.Sdump(colData))
|
|
|
|
logger.Exit("invalid json data from '%s', need array of objects for entries or object with configured NavTemplate.EntriesAttribute", errSrcText)
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// build navigation with detail sites
|
|
|
|
for idx, colEl := range entries {
|
|
|
|
ctxE := make(pongo2.Context)
|
|
|
|
err := helper.Merge(&ctxE, ctx)
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Eexit(err, "could not merge context in '%s'", node.InputPath)
|
|
|
|
|
2019-03-21 14:55:40 +01:00
|
|
|
var jsonCtx map[string]interface{}
|
|
|
|
if jsonCtx, ok = colEl.(map[string]interface{}); !ok {
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.D(spew.Sdump(colEl))
|
|
|
|
logger.Exit("no json object for entry index %d from '%s'", idx, errSrcText)
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
err = helper.Merge(&ctxE, pongo2.Context(jsonCtx))
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Eexit(err, "could not merge context in '%s'", node.InputPath)
|
2019-03-21 14:55:40 +01:00
|
|
|
|
|
|
|
tpl := ""
|
|
|
|
if navT.Template != "" {
|
|
|
|
tpl, err = pongo2.RenderTemplateString(navT.Template, ctxE)
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Eexit(err, "invalid template string for NavTemplate.Template in '%s'", node.InputPath)
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
if tpl == "" {
|
|
|
|
tpl = *node.Config.Template
|
|
|
|
}
|
|
|
|
|
|
|
|
dataKey := ""
|
|
|
|
if navT.DataKey != "" {
|
|
|
|
dataKey, err = pongo2.RenderTemplateString(navT.DataKey, ctxE)
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Eexit(err, "invalid template string for NavTemplate.DataKey in '%s'", node.InputPath)
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
goTo, err := pongo2.RenderTemplateString(navT.GoTo, ctxE)
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Eexit(err, "invalid template string for NavTemplate.GoTo in '%s'", node.InputPath)
|
|
|
|
|
2019-03-21 14:55:40 +01:00
|
|
|
goTo = strings.Trim(goTo, "/")
|
|
|
|
goTo = path.Clean(goTo)
|
|
|
|
|
|
|
|
if strings.Contains(goTo, "..") {
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Exit("going back via .. in NavTemplate.GoTo forbidden in collection config in '%s': %s", node.InputPath, goTo)
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
if goTo == "." {
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Exit("invalid config '.' for NavTemplate.GoTo in collection config in '%s'", node.InputPath)
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
if goTo == "" {
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Exit("missing NavTemplate.GoTo in collection config in '%s'", node.InputPath)
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
navname := ""
|
|
|
|
if navT.Navname != "" {
|
|
|
|
navname, err = pongo2.RenderTemplateString(navT.Navname, ctxE)
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Eexit(err, "invalid template string for NavTemplate.Navname in '%s'", node.InputPath)
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
body := ""
|
|
|
|
if navT.Body != "" {
|
|
|
|
body, err = pongo2.RenderTemplateString(navT.Body, ctxE)
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Eexit(err, "invalid template string for NavTemplate.Body in '%s'", node.InputPath)
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
|
2019-03-22 17:22:03 +01:00
|
|
|
if l := len(colCache[cacheKey].navnames); colCache[cacheKey].hit > 1 &&
|
2019-03-21 14:55:40 +01:00
|
|
|
l > 0 &&
|
2019-03-22 17:22:03 +01:00
|
|
|
navname == colCache[cacheKey].navnames[l-1] {
|
2019-03-21 14:55:40 +01:00
|
|
|
// navname before used same url, so recursion loop
|
2019-03-29 15:49:25 +01:00
|
|
|
logger.Exit("collection request loop detected for in '%s' for : %s", node.InputPath, errSrcText)
|
2019-03-21 14:55:40 +01:00
|
|
|
}
|
|
|
|
|
2019-03-22 17:22:03 +01:00
|
|
|
colCache[cacheKey].navnames = append(colCache[cacheKey].navnames, navname)
|
2019-03-21 14:55:40 +01:00
|
|
|
|
|
|
|
node.addSubNode(tpl, goTo, navname, colEl, dataKey, body, navT.Hidden)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|