generate details sites from fnRender
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Sebastian Frank 2019-02-28 10:43:30 +01:00
parent 650bdc2fd6
commit 39f1932cc3
Signed by: apairon
GPG Key ID: 7270D06DDA7FE8C3
10 changed files with 208 additions and 142 deletions

9
.gitmodules vendored
View File

@ -25,9 +25,6 @@
[submodule "vendor/github.com/davecgh/go-spew"] [submodule "vendor/github.com/davecgh/go-spew"]
path = vendor/github.com/davecgh/go-spew path = vendor/github.com/davecgh/go-spew
url = https://github.com/davecgh/go-spew url = https://github.com/davecgh/go-spew
[submodule "vendor/github.com/aymerick/raymond"]
path = vendor/github.com/aymerick/raymond
url = https://github.com/aymerick/raymond
[submodule "vendor/github.com/imdario/mergo"] [submodule "vendor/github.com/imdario/mergo"]
path = vendor/github.com/imdario/mergo path = vendor/github.com/imdario/mergo
url = https://github.com/imdario/mergo url = https://github.com/imdario/mergo
@ -40,12 +37,6 @@
[submodule "vendor/github.com/juju/errors"] [submodule "vendor/github.com/juju/errors"]
path = vendor/github.com/juju/errors path = vendor/github.com/juju/errors
url = https://github.com/juju/errors url = https://github.com/juju/errors
[submodule "vendor/github.com/gosimple/slug"]
path = vendor/github.com/gosimple/slug
url = https://github.com/gosimple/slug
[submodule "vendor/github.com/rainycape/unidecode"]
path = vendor/github.com/rainycape/unidecode
url = https://github.com/rainycape/unidecode
[submodule "vendor/github.com/flosch/pongo2-addons"] [submodule "vendor/github.com/flosch/pongo2-addons"]
path = vendor/github.com/flosch/pongo2-addons path = vendor/github.com/flosch/pongo2-addons
url = https://github.com/flosch/pongo2-addons url = https://github.com/flosch/pongo2-addons

92
config/tree.go Normal file
View File

@ -0,0 +1,92 @@
package config
import (
"reflect"
"github.com/imdario/mergo"
)
// ThisPathConfig is struct for This in paths yaml
type ThisPathConfig struct {
Navname *string `yaml:"Navname"`
GoTo *string `yaml:"GoTo"`
Data interface{} `yaml:"Data"`
}
// IndexConfig describes index input and output file
type IndexConfig struct {
InputFile *string `yaml:"InputFile"`
OutputFile *string `yaml:"OutputFile"`
}
// MetaData describes meta data for current site/tree node
type MetaData struct {
Title *string `yaml:"Title"`
Description *string `yaml:"Description"`
Keywords *string `yaml:"Keywords"`
}
// DirnameConfig describes how to handle directory names
type DirnameConfig struct {
Strip *string `yaml:"Strip"`
IgnoreForNav *string `yaml:"IgnoreForNav"`
}
// FilenameConfig describes how to handle filenames
type FilenameConfig struct {
Strip *string `yaml:"Strip"`
Ignore *string `yaml:"Ignore"`
OutputExtension *string `yaml:"OutputExtension"`
}
// MarkdownConfig describes markdown handling
type MarkdownConfig struct {
ChromaRenderer *bool `yaml:"ChromaRenderer"`
ChromaStyle *string `yaml:"ChromaStyle"`
}
// PathConfig of subdir
type PathConfig struct {
This ThisPathConfig `yaml:"This"`
Template *string `yaml:"Template"`
Index *IndexConfig `yaml:"Index"`
Meta *MetaData `yaml:"Meta"`
Path *DirnameConfig `yaml:"Path"`
Filename *FilenameConfig `yaml:"Filename"`
Markdown *MarkdownConfig `yaml:"Markdown"`
Data interface{} `yaml:"Data"`
}
// PathConfigTree is complete config tree of content dir
type PathConfigTree struct {
InputPath string
OutputPath string
InputFiles []string
OtherFiles []string
Config *PathConfig
Sub []*PathConfigTree
}
type ptrTransformer struct{}
func (t ptrTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
if typ.Kind() == reflect.Ptr {
return func(dst, src reflect.Value) error {
if dst.CanSet() {
if dst.IsNil() {
dst.Set(src)
}
}
return nil
}
}
return nil
}
// Merge merges 2 objects or maps
func Merge(dst, src interface{}) error {
return mergo.Merge(dst, src, mergo.WithTransformers(ptrTransformer{}))
}

24
helper/dir.go Normal file
View File

@ -0,0 +1,24 @@
package helper
import "os"
// CreateDirectory creates direcory with all missing parents and panic if error
func CreateDirectory(dir string) {
Log.Debugf("trying to create output directory: %s", dir)
if dirH, err := os.Stat(dir); os.IsNotExist(err) {
err := os.MkdirAll(dir, 0755)
if err != nil {
Log.Panicf("could not create output directory '%s': %s", dir, err)
}
Log.Noticef("created output directory: %s", dir)
} else if dirH != nil {
if dirH.IsDir() {
Log.Noticef("output directory '%s' already exists", dir)
} else {
Log.Panicf("output directory '%s' is no directory", dir)
}
} else {
Log.Panicf("unknown error for output directory '%s': %s", dir, err)
}
}

View File

@ -11,9 +11,12 @@ import (
) )
var templateCache = make(map[string]*pongo2.Template) var templateCache = make(map[string]*pongo2.Template)
var currentContext map[string]interface{} var currentContext *map[string]interface{}
var currentTreeNodeConfig *config.PathConfigTree
var currentPathConfig *config.PathConfig
var templateDir string var templateDir string
// BackToRoot builds ../../ string
func BackToRoot(curNavPath string) string { func BackToRoot(curNavPath string) string {
tmpPath := "" tmpPath := ""
if curNavPath != "" { if curNavPath != "" {
@ -30,8 +33,10 @@ func SetTemplateDir(dir string) {
} }
// RenderTemplate renders a pongo2 template with context // RenderTemplate renders a pongo2 template with context
func RenderTemplate(filename string, ctx map[string]interface{}) (string, error) { func RenderTemplate(filename string, treeNodeConfig *config.PathConfigTree, pathConfig *config.PathConfig, ctx *map[string]interface{}) (string, error) {
currentContext = ctx currentContext = ctx
currentTreeNodeConfig = treeNodeConfig
currentPathConfig = pathConfig
templateFile := templateDir + "/" + filename templateFile := templateDir + "/" + filename
template := templateCache[templateFile] template := templateCache[templateFile]
if template == nil { if template == nil {
@ -43,9 +48,10 @@ func RenderTemplate(filename string, ctx map[string]interface{}) (string, error)
} }
} }
return template.Execute(ctx) return template.Execute(*ctx)
} }
// FixAssetsPath replaces assets path based on current path
func FixAssetsPath(str, curNavPath string) string { func FixAssetsPath(str, curNavPath string) string {
if find := config.Config.Assets.FixTemplate.Find; find != "" { if find := config.Config.Assets.FixTemplate.Find; find != "" {
Log.Debugf("fixing assets paths for path '%s'", curNavPath) Log.Debugf("fixing assets paths for path '%s'", curNavPath)

View File

@ -4,10 +4,14 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"path"
"strings" "strings"
"gitbase.de/apairon/mark2web/config"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/extemporalgenome/slug"
"github.com/flosch/pongo2" "github.com/flosch/pongo2"
) )
@ -52,16 +56,65 @@ func RequestFn(url *pongo2.Value, args ...*pongo2.Value) *pongo2.Value {
// RenderFn renders a pongo2 template with additional context // RenderFn renders a pongo2 template with additional context
func RenderFn(templateFilename, outDir, subCtxName, ctx *pongo2.Value) *pongo2.Value { func RenderFn(templateFilename, outDir, subCtxName, ctx *pongo2.Value) *pongo2.Value {
currentContext[subCtxName.String()] = ctx oDirSlug := slug.Slug(outDir.String())
result, err := RenderTemplate(templateFilename.String(), currentContext) navPath := path.Clean((*currentContext)["CurrentPath"].(string) + "/" + oDirSlug)
outputPath := path.Clean(currentTreeNodeConfig.OutputPath + "/" + oDirSlug)
CreateDirectory(outputPath)
newContext := make(map[string]interface{})
if err := config.Merge(&newContext, *currentContext); err != nil {
Log.Panicf("unable to merge context")
}
newContext[subCtxName.String()] = ctx
newContext["CurrentPath"] = navPath
newNodeConfig := new(config.PathConfigTree)
if err := config.Merge(newNodeConfig, currentTreeNodeConfig); err != nil {
Log.Panicf("unable to merge tree node config")
}
newNodeConfig.OutputPath = outputPath
// remember old to set after recursion
oldContext := currentContext
oldNodeConfig := currentTreeNodeConfig
result, err := RenderTemplate(
templateFilename.String(),
newNodeConfig,
currentPathConfig,
&newContext,
)
if err != nil { if err != nil {
panic(err) panic(err)
} }
currentContext = oldContext
currentTreeNodeConfig = oldNodeConfig
result = FixAssetsPath( result = FixAssetsPath(
result, result,
currentContext["CurrentPath"].(string), navPath,
) )
spew.Dump(result) //spew.Dump(result)
// build output filename
outputFilename := "index.html"
var indexOutputFile *string
if i := currentPathConfig.Index; i != nil {
indexOutputFile = i.OutputFile
}
if indexOutputFile != nil && *indexOutputFile != "" {
outputFilename = *indexOutputFile
}
outFile := outputPath + "/" + outputFilename
Log.Debugf("using '%s' as output file", outFile)
Log.Noticef("writing to output file: %s", outFile)
err = ioutil.WriteFile(outFile, []byte(result), 0644)
if err != nil {
log.Panicf("could not write to output file '%s': %s", outFile, err)
}
return pongo2.AsValue(nil) return pongo2.AsValue(nil)
} }

144
main.go
View File

@ -7,18 +7,15 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path"
"reflect"
"regexp" "regexp"
"strings" "strings"
"github.com/imdario/mergo"
"gitbase.de/apairon/mark2web/config" "gitbase.de/apairon/mark2web/config"
"gitbase.de/apairon/mark2web/helper" "gitbase.de/apairon/mark2web/helper"
"github.com/Depado/bfchroma" "github.com/Depado/bfchroma"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/extemporalgenome/slug"
"github.com/flosch/pongo2" "github.com/flosch/pongo2"
"github.com/gosimple/slug"
cpy "github.com/otiai10/copy" cpy "github.com/otiai10/copy"
"gopkg.in/russross/blackfriday.v2" "gopkg.in/russross/blackfriday.v2"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
@ -38,88 +35,9 @@ var log = helper.Log
var inDir *string var inDir *string
var outDir *string var outDir *string
// ThisPathConfig is struct for This in paths yaml var contentConfig = new(config.PathConfigTree)
type ThisPathConfig struct {
Navname *string `yaml:"Navname"`
GoTo *string `yaml:"GoTo"`
Data interface{} `yaml:"Data"`
}
type indexStruct struct { func readContentDir(inBase string, outBase string, dir string, conf *config.PathConfig, tree *config.PathConfigTree) {
InputFile *string `yaml:"InputFile"`
OutputFile *string `yaml:"OutputFile"`
}
type metaStruct struct {
Title *string `yaml:"Title"`
Description *string `yaml:"Description"`
Keywords *string `yaml:"Keywords"`
}
type pathStruct struct {
Strip *string `yaml:"Strip"`
IgnoreForNav *string `yaml:"IgnoreForNav"`
}
type filenameStruct struct {
Strip *string `yaml:"Strip"`
Ignore *string `yaml:"Ignore"`
OutputExtension *string `yaml:"OutputExtension"`
}
type markdownStruct struct {
ChromaRenderer *bool `yaml:"ChromaRenderer"`
ChromaStyle *string `yaml:"ChromaStyle"`
}
// PathConfig of subdir
type PathConfig struct {
This ThisPathConfig `yaml:"This"`
Template *string `yaml:"Template"`
Index *indexStruct `yaml:"Index"`
Meta *metaStruct `yaml:"Meta"`
Path *pathStruct `yaml:"Path"`
Filename *filenameStruct `yaml:"Filename"`
Markdown *markdownStruct `yaml:"Markdown"`
Data interface{} `yaml:"Data"`
}
// PathConfigTree is complete config tree of content dir
type PathConfigTree struct {
InputPath string
OutputPath string
InputFiles []string
OtherFiles []string
Config *PathConfig
Sub []*PathConfigTree
}
var contentConfig = new(PathConfigTree)
type ptrTransformer struct{}
func (t ptrTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
if typ.Kind() == reflect.Ptr {
return func(dst, src reflect.Value) error {
if dst.CanSet() {
if dst.IsNil() {
dst.Set(src)
}
}
return nil
}
}
return nil
}
func merge(dst, src interface{}) error {
return mergo.Merge(dst, src, mergo.WithTransformers(ptrTransformer{}))
}
func readContentDir(inBase string, outBase string, dir string, conf *PathConfig, tree *PathConfigTree) {
inPath := inBase inPath := inBase
if dir != "" { if dir != "" {
inPath += "/" + dir inPath += "/" + dir
@ -135,14 +53,14 @@ func readContentDir(inBase string, outBase string, dir string, conf *PathConfig,
tree.InputPath = inPath tree.InputPath = inPath
// read config // read config
newConfig := new(PathConfig) newConfig := new(config.PathConfig)
log.Debug("looking for config.yml ...") log.Debug("looking for config.yml ...")
configFile := inPath + "/config.yml" configFile := inPath + "/config.yml"
if _, err = os.Stat(configFile); os.IsNotExist(err) { if _, err = os.Stat(configFile); os.IsNotExist(err) {
log.Debug("no config.yml found in this directory, using upper configs") log.Debug("no config.yml found in this directory, using upper configs")
merge(newConfig, conf) config.Merge(newConfig, conf)
// remove this // remove this
newConfig.This = ThisPathConfig{} newConfig.This = config.ThisPathConfig{}
} else { } else {
log.Debug("reading config...") log.Debug("reading config...")
data, err := ioutil.ReadFile(configFile) data, err := ioutil.ReadFile(configFile)
@ -156,7 +74,7 @@ func readContentDir(inBase string, outBase string, dir string, conf *PathConfig,
log.Debug("merging config with upper config") log.Debug("merging config with upper config")
oldThis := newConfig.This oldThis := newConfig.This
merge(newConfig, conf) config.Merge(newConfig, conf)
newConfig.This = oldThis newConfig.This = oldThis
log.Debug(spew.Sdump(newConfig)) log.Debug(spew.Sdump(newConfig))
@ -183,7 +101,7 @@ func readContentDir(inBase string, outBase string, dir string, conf *PathConfig,
tree.Config.This.Navname = &navname tree.Config.This.Navname = &navname
} }
stripedDir = slug.Make(stripedDir) stripedDir = slug.Slug(stripedDir)
outPath := outBase + "/" + stripedDir outPath := outBase + "/" + stripedDir
outPath = path.Clean(outPath) outPath = path.Clean(outPath)
@ -217,9 +135,9 @@ func readContentDir(inBase string, outBase string, dir string, conf *PathConfig,
p := inPath + "/" + f.Name() p := inPath + "/" + f.Name()
if f.IsDir() { if f.IsDir() {
log.Debugf("DIR %s", p) log.Debugf("DIR %s", p)
newTree := new(PathConfigTree) newTree := new(config.PathConfigTree)
if tree.Sub == nil { if tree.Sub == nil {
tree.Sub = make([]*PathConfigTree, 0) tree.Sub = make([]*config.PathConfigTree, 0)
} }
tree.Sub = append(tree.Sub, newTree) tree.Sub = append(tree.Sub, newTree)
readContentDir(inPath, outPath, f.Name(), newConfig, newTree) readContentDir(inPath, outPath, f.Name(), newConfig, newTree)
@ -234,13 +152,13 @@ type navElement struct {
Data interface{} Data interface{}
This ThisPathConfig This config.ThisPathConfig
SubMap *map[string]*navElement SubMap *map[string]*navElement
SubSlice *[]*navElement SubSlice *[]*navElement
} }
func buildNavigation(conf *PathConfigTree, curNavMap *map[string]*navElement, curNavSlice *[]*navElement, navActive *[]*navElement, activeNav string) { func buildNavigation(conf *config.PathConfigTree, curNavMap *map[string]*navElement, curNavSlice *[]*navElement, navActive *[]*navElement, activeNav string) {
for _, el := range conf.Sub { for _, el := range conf.Sub {
var ignNav *string var ignNav *string
if p := el.Config.Path; p != nil { if p := el.Config.Path; p != nil {
@ -312,24 +230,8 @@ func buildNavigation(conf *PathConfigTree, curNavMap *map[string]*navElement, cu
} }
} }
func processContent(conf *PathConfigTree) { func processContent(conf *config.PathConfigTree) {
log.Debugf("trying to create output directory: %s", conf.OutputPath) helper.CreateDirectory(conf.OutputPath)
if dirH, err := os.Stat(conf.OutputPath); os.IsNotExist(err) {
err := os.MkdirAll(conf.OutputPath, 0755)
if err != nil {
log.Panicf("could not create output directory '%s': %s", conf.OutputPath, err)
}
log.Noticef("created output directory: %s", conf.OutputPath)
} else if dirH != nil {
if dirH.IsDir() {
log.Noticef("output directory '%s' already exists", conf.OutputPath)
} else {
log.Panicf("output directory '%s' is no directory", conf.OutputPath)
}
} else {
log.Panicf("unknown error for output directory '%s': %s", conf.OutputPath, err)
}
curNavPath := strings.TrimPrefix(conf.OutputPath, *outDir) curNavPath := strings.TrimPrefix(conf.OutputPath, *outDir)
curNavPath = strings.TrimPrefix(curNavPath, "/") curNavPath = strings.TrimPrefix(curNavPath, "/")
@ -370,7 +272,7 @@ RewriteRule ^$ %{REQUEST_URI}`+goToFixed+`/ [R,L]
} }
log.Infof("processing input file '%s'", inFile) log.Infof("processing input file '%s'", inFile)
newConfig := new(PathConfig) newConfig := new(config.PathConfig)
regex := regexp.MustCompile("(?s)^---(.*?)\\r?\\n\\r?---\\r?\\n\\r?") regex := regexp.MustCompile("(?s)^---(.*?)\\r?\\n\\r?---\\r?\\n\\r?")
yamlData := regex.Find(input) yamlData := regex.Find(input)
@ -383,14 +285,14 @@ RewriteRule ^$ %{REQUEST_URI}`+goToFixed+`/ [R,L]
log.Debug("merging config with upper config") log.Debug("merging config with upper config")
oldThis := newConfig.This oldThis := newConfig.This
merge(newConfig, conf.Config) config.Merge(newConfig, conf.Config)
newConfig.This = oldThis newConfig.This = oldThis
log.Debug(spew.Sdump(newConfig)) log.Debug(spew.Sdump(newConfig))
input = regex.ReplaceAll(input, []byte("")) input = regex.ReplaceAll(input, []byte(""))
} else { } else {
merge(newConfig, conf.Config) config.Merge(newConfig, conf.Config)
} }
// ignore ??? // ignore ???
@ -484,7 +386,7 @@ RewriteRule ^$ %{REQUEST_URI}`+goToFixed+`/ [R,L]
buildNavigation(contentConfig, &navMap, &navSlice, &navActive, curNavPath) buildNavigation(contentConfig, &navMap, &navSlice, &navActive, curNavPath)
// read yaml header as data for template // read yaml header as data for template
ctx := make(pongo2.Context) ctx := make(map[string]interface{})
ctx["This"] = newConfig.This ctx["This"] = newConfig.This
ctx["Meta"] = newConfig.Meta ctx["Meta"] = newConfig.Meta
ctx["Data"] = newConfig.Data ctx["Data"] = newConfig.Data
@ -502,7 +404,7 @@ RewriteRule ^$ %{REQUEST_URI}`+goToFixed+`/ [R,L]
log.Debugf("rendering template '%s' for '%s'", *newConfig.Template, outFile) log.Debugf("rendering template '%s' for '%s'", *newConfig.Template, outFile)
templateFilename := *newConfig.Template templateFilename := *newConfig.Template
result, err := helper.RenderTemplate(*newConfig.Template, ctx) result, err := helper.RenderTemplate(*newConfig.Template, conf, newConfig, &ctx)
if err != nil { if err != nil {
log.Panicf("could not execute template '%s' for input file '%s': %s", templateFilename, inFile, err) log.Panicf("could not execute template '%s' for input file '%s': %s", templateFilename, inFile, err)
} }
@ -632,17 +534,17 @@ func main() {
defaultFilenameIgnore := "^_" defaultFilenameIgnore := "^_"
defaultFilenameOutputExtension := "html" defaultFilenameOutputExtension := "html"
defaultPathConfig := new(PathConfig) defaultPathConfig := new(config.PathConfig)
defaultPathConfig.Template = &defaultTemplate defaultPathConfig.Template = &defaultTemplate
defaultPathConfig.Index = &indexStruct{ defaultPathConfig.Index = &config.IndexConfig{
InputFile: &defaultInputFile, InputFile: &defaultInputFile,
OutputFile: &defaultOutputFile, OutputFile: &defaultOutputFile,
} }
defaultPathConfig.Path = &pathStruct{ defaultPathConfig.Path = &config.DirnameConfig{
Strip: &defaultPathStrip, Strip: &defaultPathStrip,
IgnoreForNav: &defaultPathIgnoreForNav, IgnoreForNav: &defaultPathIgnoreForNav,
} }
defaultPathConfig.Filename = &filenameStruct{ defaultPathConfig.Filename = &config.FilenameConfig{
Strip: &defaultFilenameStrip, Strip: &defaultFilenameStrip,
Ignore: &defaultFilenameIgnore, Ignore: &defaultFilenameIgnore,
OutputExtension: &defaultFilenameOutputExtension, OutputExtension: &defaultFilenameOutputExtension,

1
vendor/github.com/aymerick/raymond generated vendored

@ -1 +0,0 @@
Subproject commit b565731e1464263de0bda75f2e45d97b54b60110

1
vendor/github.com/gosimple/slug generated vendored

@ -1 +0,0 @@
Subproject commit 42ac18c34c2c6f4843fa48167317dcaa9416d1e2

@ -1 +0,0 @@
Subproject commit cb7f23ec59bec0d61b19c56cd88cee3d0cc1870c

View File

@ -1,9 +1,10 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block part0 %} {% block part0 %}
<h1>{{ details.title }}</h1> <h1>{{ details.title }}</h1>
{{ details.teaser|markdown }}
{% endblock part0 %} {% endblock part0 %}
{% block part1 %} {% block part1 %}
{{ details.body|markdown }}
{% endblock part1 %} {% endblock part1 %}