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:
parent
4a9a3eec06
commit
ff1da084af
@ -2,6 +2,7 @@ NEUERUNGEN:
|
|||||||
|
|
||||||
- Cached Collection Webrequests
|
- Cached Collection Webrequests
|
||||||
- recursive Collections
|
- recursive Collections
|
||||||
|
- Datei basierte Collections
|
||||||
- markdown-Filter `s=SYNTAX_HIGHLIGHT_SHEMA` Parameter
|
- markdown-Filter `s=SYNTAX_HIGHLIGHT_SHEMA` Parameter
|
||||||
- image_process nutzt alle CPU-Kerne
|
- image_process nutzt alle CPU-Kerne
|
||||||
- GZIP/Brotli Vor-Komprimierung der Inhalte und Assets
|
- GZIP/Brotli Vor-Komprimierung der Inhalte und Assets
|
||||||
|
@ -130,7 +130,7 @@ func ImageProcessFilter(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// local file
|
// local file
|
||||||
imgSource = mark2web.ResolveInputPath(imgSource)
|
imgSource = mark2web.CurrentTreeNode.ResolveInputPath(imgSource)
|
||||||
if p.Filename == "" {
|
if p.Filename == "" {
|
||||||
p.Filename = fmt.Sprintf(
|
p.Filename = fmt.Sprintf(
|
||||||
"%s_%s",
|
"%s_%s",
|
||||||
@ -142,7 +142,7 @@ func ImageProcessFilter(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *
|
|||||||
|
|
||||||
var imgTarget string
|
var imgTarget string
|
||||||
if p.TargetDir != "" {
|
if p.TargetDir != "" {
|
||||||
imgTarget = mark2web.ResolveOutputPath(
|
imgTarget = mark2web.CurrentTreeNode.ResolveOutputPath(
|
||||||
path.Clean(p.TargetDir) + "/" +
|
path.Clean(p.TargetDir) + "/" +
|
||||||
p.Filename,
|
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 {
|
} else {
|
||||||
imgTarget = mark2web.ResolveOutputPath(p.Filename)
|
imgTarget = mark2web.CurrentTreeNode.ResolveOutputPath(p.Filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
if f, err := os.Stat(imgTarget); err == nil && !f.IsDir() {
|
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)
|
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")
|
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
|
// RelativePathFilter returns the relative path to navpoint based on current nav
|
||||||
func RelativePathFilter(in, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
|
func RelativePathFilter(in, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
|
||||||
return pongo2.AsValue(
|
return pongo2.AsValue(
|
||||||
mark2web.ResolveNavPath(
|
mark2web.CurrentTreeNode.ResolveNavPath(
|
||||||
in.String(),
|
in.String(),
|
||||||
),
|
),
|
||||||
), nil
|
), nil
|
||||||
|
@ -17,11 +17,24 @@ func TestRelativePathFilter(t *testing.T) {
|
|||||||
"testabs": "/abs",
|
"testabs": "/abs",
|
||||||
"testsub": "../sub/rel",
|
"testsub": "../sub/rel",
|
||||||
}
|
}
|
||||||
Convey("parse template", func() {
|
|
||||||
mark2web.CurrentContext = &pongo2.Context{
|
mark2web.Config.Directories.Output = "../../test/out"
|
||||||
"CurrentPath": "sub",
|
|
||||||
|
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() {
|
||||||
|
|
||||||
output, err := pongo2.RenderTemplateString("{{ testrel|relative_path }}", ctx)
|
output, err := pongo2.RenderTemplateString("{{ testrel|relative_path }}", ctx)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(output, ShouldEqual, "rel")
|
So(output, ShouldEqual, "rel")
|
||||||
|
@ -2,7 +2,6 @@ package helper
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateDirectory creates direcory with all missing parents and panic if error
|
// 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)
|
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
|
// 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 != "" {
|
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
|
repl := Config.Assets.FixTemplate.Replace
|
||||||
toPath := Config.Assets.ToPath
|
toPath := Config.Assets.ToPath
|
||||||
|
|
||||||
bToRoot := helper.BackToRoot(curNavPath)
|
bToRoot := node.BackToRootPath()
|
||||||
regex, err := regexp.Compile(find)
|
regex, err := regexp.Compile(find)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("could not compile regexp '%s' for assets path: %s", find, err)
|
log.Panicf("could not compile regexp '%s' for assets path: %s", find, err)
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package mark2web
|
package mark2web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"path"
|
"path"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitbase.de/apairon/mark2web/pkg/helper"
|
"gitbase.de/apairon/mark2web/pkg/helper"
|
||||||
@ -20,13 +22,12 @@ var colCache = make(map[string]*colCacheEntry)
|
|||||||
func (node *TreeNode) handleCollections() {
|
func (node *TreeNode) handleCollections() {
|
||||||
collections := append(node.Config.Collections, node.Config.This.Collections...)
|
collections := append(node.Config.Collections, node.Config.This.Collections...)
|
||||||
for _, colConfig := range collections {
|
for _, colConfig := range collections {
|
||||||
if colConfig != nil {
|
|
||||||
if colConfig.Name == nil || *colConfig.Name == "" {
|
if colConfig.Name == nil || *colConfig.Name == "" {
|
||||||
helper.Log.Panicf("missing Name in collection config in '%s'", node.InputPath)
|
helper.Log.Panicf("missing Name in collection config in '%s'", node.InputPath)
|
||||||
}
|
}
|
||||||
if colConfig.URL == nil || *colConfig.URL == "" {
|
if (colConfig.URL == nil || *colConfig.URL == "") &&
|
||||||
helper.Log.Panicf("missing EntriesJSON in collection config in '%s'", node.InputPath)
|
(colConfig.Directory == nil) {
|
||||||
}
|
helper.Log.Panicf("missing URL and Directory in collection config in '%s'", node.InputPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.ColMap == nil {
|
if node.ColMap == nil {
|
||||||
@ -36,16 +37,25 @@ func (node *TreeNode) handleCollections() {
|
|||||||
ctx["This"] = node.Config.This
|
ctx["This"] = node.Config.This
|
||||||
ctx["Data"] = node.Config.Data
|
ctx["Data"] = node.Config.Data
|
||||||
|
|
||||||
|
var colData interface{}
|
||||||
|
|
||||||
|
errSrcText := ""
|
||||||
|
cacheKey := ""
|
||||||
|
|
||||||
|
if colConfig.URL != nil {
|
||||||
url, err := pongo2.RenderTemplateString(*colConfig.URL, ctx)
|
url, err := pongo2.RenderTemplateString(*colConfig.URL, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.Log.Panicf("invalid template string for Collection Element.URL in '%s': %s", node.InputPath, err)
|
helper.Log.Panicf("invalid template string for Collection Element.URL in '%s': %s", node.InputPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var colData interface{}
|
errSrcText = "URL " + url
|
||||||
|
cacheKey = url
|
||||||
|
|
||||||
if cacheEntry, ok := colCache[url]; ok {
|
if cacheEntry, ok := colCache[url]; ok {
|
||||||
colData = cacheEntry.data
|
colData = cacheEntry.data
|
||||||
cacheEntry.hit++
|
cacheEntry.hit++
|
||||||
} else {
|
} else {
|
||||||
|
helper.Log.Noticef("reading collection from: %s", errSrcText)
|
||||||
colData = helper.JSONWebRequest(url)
|
colData = helper.JSONWebRequest(url)
|
||||||
colCache[url] = &colCacheEntry{
|
colCache[url] = &colCacheEntry{
|
||||||
data: colData,
|
data: colData,
|
||||||
@ -53,6 +63,49 @@ func (node *TreeNode) handleCollections() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} 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
|
node.ColMap[*colConfig.Name] = colData
|
||||||
|
|
||||||
if navT := colConfig.NavTemplate; navT != nil {
|
if navT := colConfig.NavTemplate; navT != nil {
|
||||||
@ -64,7 +117,7 @@ func (node *TreeNode) handleCollections() {
|
|||||||
entries, ok = colDataMap[navT.EntriesAttribute].([]interface{})
|
entries, ok = colDataMap[navT.EntriesAttribute].([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
helper.Log.Debug(spew.Sdump(colDataMap))
|
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 {
|
} else {
|
||||||
@ -72,7 +125,7 @@ func (node *TreeNode) handleCollections() {
|
|||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
helper.Log.Debug(spew.Sdump(colData))
|
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
|
// build navigation with detail sites
|
||||||
@ -85,7 +138,7 @@ func (node *TreeNode) handleCollections() {
|
|||||||
var jsonCtx map[string]interface{}
|
var jsonCtx map[string]interface{}
|
||||||
if jsonCtx, ok = colEl.(map[string]interface{}); !ok {
|
if jsonCtx, ok = colEl.(map[string]interface{}); !ok {
|
||||||
helper.Log.Debug(spew.Sdump(colEl))
|
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))
|
err = helper.Merge(&ctxE, pongo2.Context(jsonCtx))
|
||||||
if err != nil {
|
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 &&
|
l > 0 &&
|
||||||
navname == colCache[url].navnames[l-1] {
|
navname == colCache[cacheKey].navnames[l-1] {
|
||||||
// navname before used same url, so recursion loop
|
// 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)
|
node.addSubNode(tpl, goTo, navname, colEl, dataKey, body, navT.Hidden)
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,18 @@ package mark2web
|
|||||||
|
|
||||||
import "gitbase.de/apairon/mark2web/pkg/helper"
|
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
|
// CollectionConfig describes a collection
|
||||||
type CollectionConfig struct {
|
type CollectionConfig struct {
|
||||||
Name *string `yaml:"Name"`
|
Name *string `yaml:"Name"`
|
||||||
URL *string `yaml:"URL"`
|
URL *string `yaml:"URL"`
|
||||||
|
Directory *CollectionDirectoryConfig `yaml:"Directory"`
|
||||||
NavTemplate *struct {
|
NavTemplate *struct {
|
||||||
EntriesAttribute string `yaml:"EntriesAttribute"`
|
EntriesAttribute string `yaml:"EntriesAttribute"`
|
||||||
GoTo string `yaml:"GoTo"`
|
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
|
// ProcessContent walks recursivly through the input paths and processes all files for output
|
||||||
func (node *TreeNode) ProcessContent() {
|
func (node *TreeNode) ProcessContent() {
|
||||||
helper.CreateDirectory(node.OutputPath)
|
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 {
|
if node.root != node {
|
||||||
// write htaccess for rewrites, root will be written in WriteWebserverConfig()
|
// write htaccess for rewrites, root will be written in WriteWebserverConfig()
|
||||||
goTo := node.Config.This.GoTo
|
goTo := node.Config.This.GoTo
|
||||||
if goTo != nil && *goTo != "" {
|
if goTo != nil && *goTo != "" {
|
||||||
goToFixed := *goTo
|
goToFixed := *goTo
|
||||||
if strings.HasPrefix(goToFixed, "/") {
|
if strings.HasPrefix(goToFixed, "/") {
|
||||||
goToFixed = helper.BackToRoot(curNavPath) + goToFixed
|
goToFixed = node.BackToRootPath() + goToFixed
|
||||||
}
|
}
|
||||||
goToFixed = path.Clean(goToFixed)
|
goToFixed = path.Clean(goToFixed)
|
||||||
|
|
||||||
@ -91,11 +171,31 @@ func (node *TreeNode) ProcessContent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, file := range node.InputFiles {
|
for _, file := range node.InputFiles {
|
||||||
var input []byte
|
|
||||||
inFile := "InputString"
|
inFile := "InputString"
|
||||||
|
|
||||||
|
// ignore ???
|
||||||
|
ignoreFile := false
|
||||||
if file != "" {
|
if file != "" {
|
||||||
inFile = node.InputPath + "/" + 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
helper.Log.Debugf("reading file: %s", inFile)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -116,57 +216,18 @@ func (node *TreeNode) ProcessContent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newConfig := new(PathConfig)
|
newConfig, ctx := node.processMarkdownWithHeader(input, inFile)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
ignoreFile = regex.MatchString(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ignoreFile {
|
|
||||||
helper.Log.Infof("ignoring file '%s', because of filename.ignore", inFile)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// build output filename
|
// build output filename
|
||||||
outputFilename := file
|
outputFilename := file
|
||||||
|
|
||||||
|
var stripRegex *string
|
||||||
|
var outputExt *string
|
||||||
|
if f := newConfig.Filename; f != nil {
|
||||||
|
stripRegex = f.Strip
|
||||||
|
outputExt = f.OutputExtension
|
||||||
|
}
|
||||||
|
|
||||||
var indexInputFile *string
|
var indexInputFile *string
|
||||||
var indexOutputFile *string
|
var indexOutputFile *string
|
||||||
if i := newConfig.Index; i != nil {
|
if i := newConfig.Index; i != nil {
|
||||||
@ -194,71 +255,14 @@ func (node *TreeNode) ProcessContent() {
|
|||||||
|
|
||||||
outFile := node.OutputPath + "/" + outputFilename
|
outFile := node.OutputPath + "/" + outputFilename
|
||||||
helper.Log.Debugf("using '%s' as output file", outFile)
|
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)
|
helper.Log.Debugf("rendering template '%s' for '%s'", *newConfig.Template, outFile)
|
||||||
templateFilename := *newConfig.Template
|
templateFilename := *newConfig.Template
|
||||||
result, err := renderTemplate(*newConfig.Template, node, newConfig, &ctx)
|
result, err := renderTemplate(*newConfig.Template, node, newConfig, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.Log.Panicf("could not execute template '%s' for input file '%s': %s", templateFilename, inFile, err)
|
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)
|
helper.Log.Noticef("writing to output file: %s", outFile)
|
||||||
err = ioutil.WriteFile(outFile, []byte(result), 0644)
|
err = ioutil.WriteFile(outFile, []byte(result), 0644)
|
||||||
|
@ -25,7 +25,11 @@ type NavElement struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// buildNavigation builds the navigation trees for use in templates
|
// 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 {
|
for _, el := range tree.Sub {
|
||||||
if el.Hidden {
|
if el.Hidden {
|
||||||
continue // ignore hidden nav points from collections
|
continue // ignore hidden nav points from collections
|
||||||
@ -88,8 +92,7 @@ func buildNavigation(tree *TreeNode, curNavMap *map[string]*NavElement, curNavSl
|
|||||||
|
|
||||||
if activeNav != "" && activeNav != "/" {
|
if activeNav != "" && activeNav != "/" {
|
||||||
// calculate relative path
|
// calculate relative path
|
||||||
bToRoot := helper.BackToRoot(activeNav)
|
navEl.GoTo = backToRoot + navEl.GoTo
|
||||||
navEl.GoTo = bToRoot + navEl.GoTo
|
|
||||||
navEl.GoTo = path.Clean(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)
|
*curNavSlice = append(*curNavSlice, &navEl)
|
||||||
}
|
}
|
||||||
|
|
||||||
buildNavigation(el, &subMap, &subSlice, navActive, activeNav)
|
buildNavigationRecursive(el, &subMap, &subSlice, navActive, activeNav, backToRoot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,36 +3,58 @@ package mark2web
|
|||||||
import (
|
import (
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitbase.de/apairon/mark2web/pkg/helper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ResolveNavPath fixes nav target relative to current navigation path
|
// ResolveNavPath fixes nav target relative to current navigation path
|
||||||
func ResolveNavPath(target string) string {
|
func (node *TreeNode) ResolveNavPath(target string) string {
|
||||||
curNavPath := (*CurrentContext)["CurrentPath"].(string)
|
|
||||||
if strings.HasPrefix(target, "/") {
|
if strings.HasPrefix(target, "/") {
|
||||||
target = helper.BackToRoot(curNavPath) + target
|
target = node.BackToRootPath() + target
|
||||||
}
|
}
|
||||||
target = path.Clean(target)
|
target = path.Clean(target)
|
||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveOutputPath fixes output directory relative to current navigation path
|
// 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, "/") {
|
if strings.HasPrefix(target, "/") {
|
||||||
target = Config.Directories.Output + "/" + target
|
target = Config.Directories.Output + "/" + target
|
||||||
} else {
|
} else {
|
||||||
target = CurrentTreeNode.OutputPath + "/" + target
|
target = node.OutputPath + "/" + target
|
||||||
}
|
}
|
||||||
return path.Clean(target)
|
return path.Clean(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveInputPath fixes input directory relative to current navigation path
|
// 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, "/") {
|
if strings.HasPrefix(target, "/") {
|
||||||
target = Config.Directories.Input + "/" + target
|
target = Config.Directories.Input + "/" + target
|
||||||
} else {
|
} else {
|
||||||
target = CurrentTreeNode.InputPath + "/" + target
|
target = node.InputPath + "/" + target
|
||||||
}
|
}
|
||||||
return path.Clean(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
|
||||||
|
}
|
||||||
|
@ -10,23 +10,16 @@ Data:
|
|||||||
---
|
---
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
Damit die korrekten Versionsinformationen dynamisch in das finale mark2web-Binary eingefügt wurde, ist eine manuelle Installation aus dem Git-Repository sinnvoll.
|
|
||||||
Da die benötigten Pakete über die Go "vendor"-Funktionalität eingebunden sind ist ein `git submodule --init --recursive` nötig, wie im folgenden Abschnitt zu sehen ist:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
mkdir -p $GOPATH/src/gitbase.de/apairon
|
go get -v gitbase.de/apairon/mark2web/cmd/mark2web
|
||||||
git clone https://gitbase.de/apairon/mark2web.git $GOPATH/src/gitbase.de/apairon/mark2web
|
|
||||||
|
|
||||||
cd $GOPATH/src/gitbase.de/apairon/mark2web
|
# setze Versioninformationen ins Binary
|
||||||
git submodule update --init --recursive
|
pkg=$GOPATH/src/gitbase.de/apairon/mark2web
|
||||||
|
go install -v -ldflags "-X main.Version=`cat $pkg/build/VERSION` -X main.GitHash=`git --git-dir $pkg/.git rev-parse HEAD` -X main.BuildTime=`date -u '+%Y-%m-%d_%I:%M:%S%p'`" gitbase.de/apairon/mark2web/cmd/mark2web
|
||||||
./build.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Eine Installation über `go install gitbase.de/apairon/mark2web` wird derzeit noch nicht unterstützt, da dabei die Informationen für `mark2web -version` nicht generiert werden.
|
|
||||||
|
|
||||||
## Releases
|
## Releases
|
||||||
|
|
||||||
Vorkompilierte Binaries finden Sie auf der [Releases-Seite auf gitbase.de](https://gitbase.de/apairon/mark2web/releases).
|
Vorkompilierte Binaries finden Sie auf der [Releases-Seite auf gitbase.de](https://gitbase.de/apairon/mark2web/releases).
|
Binary file not shown.
Before Width: | Height: | Size: 790 KiB |
@ -1,150 +0,0 @@
|
|||||||
---
|
|
||||||
Data:
|
|
||||||
background: /img/wire.jpg
|
|
||||||
|
|
||||||
slider:
|
|
||||||
- img: /img/wire.jpg
|
|
||||||
alt:
|
|
||||||
opacity: 1
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Konfiguration
|
|
||||||
|
|
||||||
Die Konfigurationsdatein sind im YAML-Format gehalten (siehe: [Wikipedia](https://de.wikipedia.org/wiki/YAML)).
|
|
||||||
|
|
||||||
## globale Einstellungen
|
|
||||||
|
|
||||||
Die obersten Verzeichnis sich befindende Datei `config.yml` kann z.B. folgenden Inhalt haben:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
Webserver:
|
|
||||||
Type: "apache"
|
|
||||||
|
|
||||||
Assets:
|
|
||||||
FromPath: "assets"
|
|
||||||
ToPath: "assets"
|
|
||||||
Action: "copy"
|
|
||||||
FixTemplate:
|
|
||||||
Find: "\\.\\./assets/"
|
|
||||||
Replace: ""
|
|
||||||
|
|
||||||
OtherFiles:
|
|
||||||
Action: "copy"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Sektion `Webserver:`
|
|
||||||
|
|
||||||
#### `Type:`
|
|
||||||
|
|
||||||
Derzeit wird hier nur der Wert `apache` unterstützt. Ist dieser Wert gesetzt werden automatische .htaccess-Dateien mit mod_rewrite-Anweisungen generiert, die eine saubere Weiterleitung bei entsprechenden Konfig-Anweisungen im `content`-Verzeichnis ermöglichen.
|
|
||||||
|
|
||||||
### Sektion `Assets:`
|
|
||||||
|
|
||||||
`Assets:` steuert, wie mit Bild/JS/CSS Dateien umgegangen werden soll.
|
|
||||||
|
|
||||||
#### `FromPath:`
|
|
||||||
|
|
||||||
Lage des Asset-Verzeichnis unterhalb des `content`-Verzeichnis
|
|
||||||
|
|
||||||
#### `ToPath:`
|
|
||||||
|
|
||||||
Zielverzeichnis im Ausgabe-Verzeichnis der fertig generierten Website
|
|
||||||
|
|
||||||
#### `Action:`
|
|
||||||
|
|
||||||
Derzeit nur `copy`, also das Kopieren der Dateien und Unterordner ins Zielverzeichnis
|
|
||||||
|
|
||||||
#### `FixTemplate:`
|
|
||||||
|
|
||||||
Wenn hier `Find:` (regulärer Ausdruck) und `Replace:` (Ersetzung) angeben sind, werden die gefundenden Pfadangaben in der generierten HTML-Dateien durch den korrekten relativen Pfad zum Asset-Verzeichnis ersetzt.
|
|
||||||
|
|
||||||
### Sektion `OtherFiles:`
|
|
||||||
|
|
||||||
`OtherFiles:` definiert, wie mit anderen Dateien innerhalb des `content`-Verzeichnis umgegangen werden soll.
|
|
||||||
|
|
||||||
#### `Action:`
|
|
||||||
|
|
||||||
Derzeit nur `copy`, also das Kopieren der Dateien in das entsprechende Unterverzeichnis im Ausgabe-Verzeichnis
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Konfiguration im `content`-Verzeichnis
|
|
||||||
|
|
||||||
Im `content`-Verzeichnis, sowie in jedem Unterverzeichnis unterhalb von `content` kann sich eine `config.yml`-Datei befinden, wie aus folgendem Beispiel:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
This:
|
|
||||||
GoTo: "/de/service/impressum/"
|
|
||||||
Navname: "Impressumsverweis"
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
oder
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
This:
|
|
||||||
Navname: "FAQ's"
|
|
||||||
Data:
|
|
||||||
slogan: "Wer nicht fragt, bekommt keine Antwort."
|
|
||||||
|
|
||||||
Template: "base.html"
|
|
||||||
|
|
||||||
Index:
|
|
||||||
InputFile: "README.md"
|
|
||||||
OutputFile: "index.html"
|
|
||||||
|
|
||||||
Meta:
|
|
||||||
Title: "Fragen und Antworten"
|
|
||||||
Description: "Dies ist die Fragen und Antworten Unterseite."
|
|
||||||
Keywords: "FAQ, Fragen, Antworten"
|
|
||||||
|
|
||||||
Data:
|
|
||||||
background: "bg.jpg"
|
|
||||||
slider:
|
|
||||||
- img: "assets/img/slider1.jpg"
|
|
||||||
alt: "Alternativtext 1"
|
|
||||||
- img: "assets/img/slider2.jpg"
|
|
||||||
alt: "Alternativtext 2"
|
|
||||||
- img: "assets/img/slider3.jpg"
|
|
||||||
alt: "Alternativtext 3"
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### `This:` Sektion
|
|
||||||
|
|
||||||
Sämtlich Werte unterhalb dieser Sektion gelten nur für den Inhalt, bzw. Navigationspunkt in dessen Ordner sich die `config.yml` befindet. Die Werte werden nicht an Unterordner wertervererbt.
|
|
||||||
|
|
||||||
#### `GoTo:`
|
|
||||||
|
|
||||||
Falls der Navigationspunkt selbst keinen Inhalt darstellen soll, sondern nur weiterleiten soll, so wird hier das Weiterleitungsziel eingegeben.
|
|
||||||
Das Ziel ist der absolute (startend mit `/`) oder relative Pfad zum Zielnavigationspunkt.
|
|
||||||
Die Schreibweise des Pfades ist so zu verwenden, wie der Pfad nach Umschreibung und Säuberung des Pfades im Zielverzeichnis dargestellt wird.
|
|
||||||
Aus `de/mainnav/03_Fragen und Antworten` wird also z.B. `de/mainnav/fragen-und-antworten`.
|
|
||||||
|
|
||||||
#### `Navname:`
|
|
||||||
|
|
||||||
Dieser Wert überschreibt den aus dem Ordnernamen automatisch abgeleiteten Navigationspunkt-Namen. Dies ist zum Beispiel dann nützlich, wenn Sonderzeichen im Verzeichnisnamen nicht vorkommen sollen, aber im Namen des Navigationspunkts gebraucht werden.
|
|
||||||
|
|
||||||
#### `Data:`
|
|
||||||
|
|
||||||
Unterhalb von `Data:` können beliebige Datenstrukturen erfasst werden. Da diese Struktur unterhalb von `This:` angeordnet ist, werden auch die Daten nicht weiter an Unterordner vererbt.
|
|
||||||
Hier können z.B. Informationen zum Navigationspunkt abgelegt werden, die im Template Zusatzinformationen darstellen (z.B. ein Slogan zu einem Navigationspunkt).
|
|
||||||
|
|
||||||
### `Meta:` Sektion
|
|
||||||
|
|
||||||
Unter `Title:`, `Description:` und `Keywords:` werden die typischen Metaangaben abgelegt, die im
|
|
||||||
|
|
||||||
```html
|
|
||||||
<head>
|
|
||||||
...
|
|
||||||
</head>
|
|
||||||
```
|
|
||||||
|
|
||||||
übllicherweise Verwendung finden. Die entsprechenden Platzhalter stehen im Template zur Verfügung.
|
|
||||||
|
|
||||||
`Meta:` vererbt seine individuellen Informationen an die Unterordner weiter, sofern diese dort nicht selbst in einer `config.yml` oder im Kopf der Markdown-Datei definiert sind.
|
|
||||||
|
|
||||||
### `Data:` Sektion
|
|
||||||
|
|
||||||
`Data:` an dieser Stelle kann, wie auch `Data:` unterhalb von `This:`, beliebige Daten aufnehmen. Die Daten hier allerdings werden an Unterordner weitervererbt, sofern diese nicht dort oder in der Markdown-Datei selbst festegelegt überschrieben wurden.
|
|
@ -1,3 +0,0 @@
|
|||||||
This:
|
|
||||||
Data:
|
|
||||||
teaser: Globale Konfiguration und individuelle Content-Einstellungen
|
|
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
Template: base_doc.html
|
||||||
|
|
||||||
|
Data:
|
||||||
|
background: /img/wire.jpg
|
||||||
|
|
||||||
|
slider:
|
||||||
|
- img: /img/wire.jpg
|
||||||
|
alt:
|
||||||
|
opacity: 1
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Konfiguration
|
||||||
|
|
||||||
|
Die Konfigurationsdatein sind im YAML-Format gehalten (siehe: [Wikipedia](https://de.wikipedia.org/wiki/YAML)).
|
@ -0,0 +1,17 @@
|
|||||||
|
Die obersten Verzeichnis sich befindende Datei `config.yml` kann z.B. folgenden Inhalt haben:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
Webserver:
|
||||||
|
Type: "apache"
|
||||||
|
|
||||||
|
Assets:
|
||||||
|
FromPath: "assets"
|
||||||
|
ToPath: "assets"
|
||||||
|
Action: "copy"
|
||||||
|
FixTemplate:
|
||||||
|
Find: "\\.\\./assets/"
|
||||||
|
Replace: ""
|
||||||
|
|
||||||
|
OtherFiles:
|
||||||
|
Action: "copy"
|
||||||
|
```
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Derzeit wird hier nur der Wert `apache` unterstützt. Ist dieser Wert gesetzt werden automatische .htaccess-Dateien mit mod_rewrite-Anweisungen generiert, die eine saubere Weiterleitung bei entsprechenden Konfig-Anweisungen im `content`-Verzeichnis ermöglichen.
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`Assets:` steuert, wie mit Bild/JS/CSS Dateien umgegangen werden soll.
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Lage des Asset-Verzeichnis unterhalb des `content`-Verzeichnis
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Zielverzeichnis im Ausgabe-Verzeichnis der fertig generierten Website
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Derzeit nur `copy`, also das Kopieren der Dateien und Unterordner ins Zielverzeichnis
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Wenn hier `Find:` (regulärer Ausdruck) und `Replace:` (Ersetzung) angeben sind, werden die gefundenden Pfadangaben in der generierten HTML-Dateien durch den korrekten relativen Pfad zum Asset-Verzeichnis ersetzt.
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`OtherFiles:` definiert, wie mit anderen Dateien innerhalb des `content`-Verzeichnis umgegangen werden soll.
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Derzeit nur `copy`, also das Kopieren der Dateien in das entsprechende Unterverzeichnis im Ausgabe-Verzeichnis
|
@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Im `content`-Verzeichnis, sowie in jedem Unterverzeichnis unterhalb von `content` kann sich eine `config.yml`-Datei befinden, wie aus folgendem Beispiel:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
This:
|
||||||
|
GoTo: "/de/service/impressum/"
|
||||||
|
Navname: "Impressumsverweis"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
oder
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
This:
|
||||||
|
Navname: "FAQ's"
|
||||||
|
Data:
|
||||||
|
slogan: "Wer nicht fragt, bekommt keine Antwort."
|
||||||
|
|
||||||
|
Template: "base.html"
|
||||||
|
|
||||||
|
Index:
|
||||||
|
InputFile: "README.md"
|
||||||
|
OutputFile: "index.html"
|
||||||
|
|
||||||
|
Meta:
|
||||||
|
Title: "Fragen und Antworten"
|
||||||
|
Description: "Dies ist die Fragen und Antworten Unterseite."
|
||||||
|
Keywords: "FAQ, Fragen, Antworten"
|
||||||
|
|
||||||
|
Data:
|
||||||
|
background: "bg.jpg"
|
||||||
|
slider:
|
||||||
|
- img: "assets/img/slider1.jpg"
|
||||||
|
alt: "Alternativtext 1"
|
||||||
|
- img: "assets/img/slider2.jpg"
|
||||||
|
alt: "Alternativtext 2"
|
||||||
|
- img: "assets/img/slider3.jpg"
|
||||||
|
alt: "Alternativtext 3"
|
||||||
|
|
||||||
|
```
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Sämtlich Werte unterhalb dieser Sektion gelten nur für den Inhalt, bzw. Navigationspunkt in dessen Ordner sich die `config.yml` befindet. Die Werte werden nicht an Unterordner wertervererbt.
|
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Falls der Navigationspunkt selbst keinen Inhalt darstellen soll, sondern nur weiterleiten soll, so wird hier das Weiterleitungsziel eingegeben.
|
||||||
|
Das Ziel ist der absolute (startend mit `/`) oder relative Pfad zum Zielnavigationspunkt.
|
||||||
|
Die Schreibweise des Pfades ist so zu verwenden, wie der Pfad nach Umschreibung und Säuberung des Pfades im Zielverzeichnis dargestellt wird.
|
||||||
|
Aus `de/mainnav/03_Fragen und Antworten` wird also z.B. `de/mainnav/fragen-und-antworten`.
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Dieser Wert überschreibt den aus dem Ordnernamen automatisch abgeleiteten Navigationspunkt-Namen. Dies ist zum Beispiel dann nützlich, wenn Sonderzeichen im Verzeichnisnamen nicht vorkommen sollen, aber im Namen des Navigationspunkts gebraucht werden.
|
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Unterhalb von `Data:` können beliebige Datenstrukturen erfasst werden. Da diese Struktur unterhalb von `This:` angeordnet ist, werden auch die Daten nicht weiter an Unterordner vererbt.
|
||||||
|
Hier können z.B. Informationen zum Navigationspunkt abgelegt werden, die im Template Zusatzinformationen darstellen (z.B. ein Slogan zu einem Navigationspunkt).
|
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Unter `Title:`, `Description:` und `Keywords:` werden die typischen Metaangaben abgelegt, die im
|
||||||
|
|
||||||
|
```html
|
||||||
|
<head>
|
||||||
|
...
|
||||||
|
</head>
|
||||||
|
```
|
||||||
|
|
||||||
|
übllicherweise Verwendung finden. Die entsprechenden Platzhalter stehen im Template zur Verfügung.
|
||||||
|
|
||||||
|
`Meta:` vererbt seine individuellen Informationen an die Unterordner weiter, sofern diese dort nicht selbst in einer `config.yml` oder im Kopf der Markdown-Datei definiert sind.
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
Data:
|
||||||
|
Version: "ab v1.0"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`Data:` an dieser Stelle kann, wie auch `Data:` unterhalb von `This:`, beliebige Daten aufnehmen. Die Daten hier allerdings werden an Unterordner weitervererbt, sofern diese nicht dort oder in der Markdown-Datei selbst festegelegt überschrieben wurden.
|
@ -0,0 +1,9 @@
|
|||||||
|
This:
|
||||||
|
Data:
|
||||||
|
teaser: Globale Konfiguration und individuelle Content-Einstellungen
|
||||||
|
Collections:
|
||||||
|
- Name: doccoll
|
||||||
|
Directory:
|
||||||
|
Path: "."
|
||||||
|
MatchFilename: "^_\\d+(?P<lowdash>_*)(?P<title>.+)\\.md"
|
||||||
|
ReverseOrder: False
|
@ -293,3 +293,13 @@ code.language-mermaid svg {
|
|||||||
display:inline-block;
|
display:inline-block;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.versionBadge {
|
||||||
|
text-align: right;
|
||||||
|
color: #444;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 3px;
|
||||||
|
background: #ddd;
|
||||||
|
border: #444 solid 1px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
19
website/templates/base_doc.html
Normal file
19
website/templates/base_doc.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block part0 %}
|
||||||
|
{{ Body }}
|
||||||
|
{% endblock part0 %}
|
||||||
|
|
||||||
|
{% block part1 %}
|
||||||
|
{% for e in NavElement.ColMap.doccoll %}
|
||||||
|
{% with e.FilenameMatch.lowdash|count + 1 as h %}
|
||||||
|
<h{{ h }}>
|
||||||
|
{{ e.FilenameMatch.title }}
|
||||||
|
{% if e.Data.Version %}
|
||||||
|
<span class="versionBadge">{{ e.Data.Version }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</h{{ h }}>
|
||||||
|
{% endwith %}
|
||||||
|
{{ e.Body }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock part1 %}
|
5
website/templates/filters/count.js
Normal file
5
website/templates/filters/count.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
function count(el, param) {
|
||||||
|
return el.length
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = count;
|
Loading…
Reference in New Issue
Block a user