generate details sites from fnRender
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
650bdc2fd6
commit
39f1932cc3
9
.gitmodules
vendored
9
.gitmodules
vendored
@ -25,9 +25,6 @@
|
||||
[submodule "vendor/github.com/davecgh/go-spew"]
|
||||
path = vendor/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"]
|
||||
path = vendor/github.com/imdario/mergo
|
||||
url = https://github.com/imdario/mergo
|
||||
@ -40,12 +37,6 @@
|
||||
[submodule "vendor/github.com/juju/errors"]
|
||||
path = vendor/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"]
|
||||
path = vendor/github.com/flosch/pongo2-addons
|
||||
url = https://github.com/flosch/pongo2-addons
|
||||
|
92
config/tree.go
Normal file
92
config/tree.go
Normal 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
24
helper/dir.go
Normal 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)
|
||||
}
|
||||
}
|
@ -11,9 +11,12 @@ import (
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
// BackToRoot builds ../../ string
|
||||
func BackToRoot(curNavPath string) string {
|
||||
tmpPath := ""
|
||||
if curNavPath != "" {
|
||||
@ -30,8 +33,10 @@ func SetTemplateDir(dir string) {
|
||||
}
|
||||
|
||||
// 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
|
||||
currentTreeNodeConfig = treeNodeConfig
|
||||
currentPathConfig = pathConfig
|
||||
templateFile := templateDir + "/" + filename
|
||||
template := templateCache[templateFile]
|
||||
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 {
|
||||
if find := config.Config.Assets.FixTemplate.Find; find != "" {
|
||||
Log.Debugf("fixing assets paths for path '%s'", curNavPath)
|
||||
|
@ -4,10 +4,14 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"gitbase.de/apairon/mark2web/config"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/extemporalgenome/slug"
|
||||
"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
|
||||
func RenderFn(templateFilename, outDir, subCtxName, ctx *pongo2.Value) *pongo2.Value {
|
||||
currentContext[subCtxName.String()] = ctx
|
||||
result, err := RenderTemplate(templateFilename.String(), currentContext)
|
||||
oDirSlug := slug.Slug(outDir.String())
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
currentContext = oldContext
|
||||
currentTreeNodeConfig = oldNodeConfig
|
||||
|
||||
result = FixAssetsPath(
|
||||
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)
|
||||
}
|
||||
|
144
main.go
144
main.go
@ -7,18 +7,15 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
|
||||
"gitbase.de/apairon/mark2web/config"
|
||||
"gitbase.de/apairon/mark2web/helper"
|
||||
"github.com/Depado/bfchroma"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/extemporalgenome/slug"
|
||||
"github.com/flosch/pongo2"
|
||||
"github.com/gosimple/slug"
|
||||
cpy "github.com/otiai10/copy"
|
||||
"gopkg.in/russross/blackfriday.v2"
|
||||
"gopkg.in/yaml.v2"
|
||||
@ -38,88 +35,9 @@ var log = helper.Log
|
||||
var inDir *string
|
||||
var outDir *string
|
||||
|
||||
// ThisPathConfig is struct for This in paths yaml
|
||||
type ThisPathConfig struct {
|
||||
Navname *string `yaml:"Navname"`
|
||||
GoTo *string `yaml:"GoTo"`
|
||||
Data interface{} `yaml:"Data"`
|
||||
}
|
||||
var contentConfig = new(config.PathConfigTree)
|
||||
|
||||
type indexStruct struct {
|
||||
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) {
|
||||
func readContentDir(inBase string, outBase string, dir string, conf *config.PathConfig, tree *config.PathConfigTree) {
|
||||
inPath := inBase
|
||||
if dir != "" {
|
||||
inPath += "/" + dir
|
||||
@ -135,14 +53,14 @@ func readContentDir(inBase string, outBase string, dir string, conf *PathConfig,
|
||||
tree.InputPath = inPath
|
||||
|
||||
// read config
|
||||
newConfig := new(PathConfig)
|
||||
newConfig := new(config.PathConfig)
|
||||
log.Debug("looking for config.yml ...")
|
||||
configFile := inPath + "/config.yml"
|
||||
if _, err = os.Stat(configFile); os.IsNotExist(err) {
|
||||
log.Debug("no config.yml found in this directory, using upper configs")
|
||||
merge(newConfig, conf)
|
||||
config.Merge(newConfig, conf)
|
||||
// remove this
|
||||
newConfig.This = ThisPathConfig{}
|
||||
newConfig.This = config.ThisPathConfig{}
|
||||
} else {
|
||||
log.Debug("reading config...")
|
||||
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")
|
||||
oldThis := newConfig.This
|
||||
merge(newConfig, conf)
|
||||
config.Merge(newConfig, conf)
|
||||
newConfig.This = oldThis
|
||||
|
||||
log.Debug(spew.Sdump(newConfig))
|
||||
@ -183,7 +101,7 @@ func readContentDir(inBase string, outBase string, dir string, conf *PathConfig,
|
||||
tree.Config.This.Navname = &navname
|
||||
}
|
||||
|
||||
stripedDir = slug.Make(stripedDir)
|
||||
stripedDir = slug.Slug(stripedDir)
|
||||
outPath := outBase + "/" + stripedDir
|
||||
outPath = path.Clean(outPath)
|
||||
|
||||
@ -217,9 +135,9 @@ func readContentDir(inBase string, outBase string, dir string, conf *PathConfig,
|
||||
p := inPath + "/" + f.Name()
|
||||
if f.IsDir() {
|
||||
log.Debugf("DIR %s", p)
|
||||
newTree := new(PathConfigTree)
|
||||
newTree := new(config.PathConfigTree)
|
||||
if tree.Sub == nil {
|
||||
tree.Sub = make([]*PathConfigTree, 0)
|
||||
tree.Sub = make([]*config.PathConfigTree, 0)
|
||||
}
|
||||
tree.Sub = append(tree.Sub, newTree)
|
||||
readContentDir(inPath, outPath, f.Name(), newConfig, newTree)
|
||||
@ -234,13 +152,13 @@ type navElement struct {
|
||||
|
||||
Data interface{}
|
||||
|
||||
This ThisPathConfig
|
||||
This config.ThisPathConfig
|
||||
|
||||
SubMap *map[string]*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 {
|
||||
var ignNav *string
|
||||
if p := el.Config.Path; p != nil {
|
||||
@ -312,24 +230,8 @@ func buildNavigation(conf *PathConfigTree, curNavMap *map[string]*navElement, cu
|
||||
}
|
||||
}
|
||||
|
||||
func processContent(conf *PathConfigTree) {
|
||||
log.Debugf("trying to create output directory: %s", 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)
|
||||
}
|
||||
func processContent(conf *config.PathConfigTree) {
|
||||
helper.CreateDirectory(conf.OutputPath)
|
||||
|
||||
curNavPath := strings.TrimPrefix(conf.OutputPath, *outDir)
|
||||
curNavPath = strings.TrimPrefix(curNavPath, "/")
|
||||
@ -370,7 +272,7 @@ RewriteRule ^$ %{REQUEST_URI}`+goToFixed+`/ [R,L]
|
||||
}
|
||||
log.Infof("processing input file '%s'", inFile)
|
||||
|
||||
newConfig := new(PathConfig)
|
||||
newConfig := new(config.PathConfig)
|
||||
|
||||
regex := regexp.MustCompile("(?s)^---(.*?)\\r?\\n\\r?---\\r?\\n\\r?")
|
||||
yamlData := regex.Find(input)
|
||||
@ -383,14 +285,14 @@ RewriteRule ^$ %{REQUEST_URI}`+goToFixed+`/ [R,L]
|
||||
|
||||
log.Debug("merging config with upper config")
|
||||
oldThis := newConfig.This
|
||||
merge(newConfig, conf.Config)
|
||||
config.Merge(newConfig, conf.Config)
|
||||
newConfig.This = oldThis
|
||||
|
||||
log.Debug(spew.Sdump(newConfig))
|
||||
|
||||
input = regex.ReplaceAll(input, []byte(""))
|
||||
} else {
|
||||
merge(newConfig, conf.Config)
|
||||
config.Merge(newConfig, conf.Config)
|
||||
}
|
||||
|
||||
// ignore ???
|
||||
@ -484,7 +386,7 @@ RewriteRule ^$ %{REQUEST_URI}`+goToFixed+`/ [R,L]
|
||||
buildNavigation(contentConfig, &navMap, &navSlice, &navActive, curNavPath)
|
||||
|
||||
// read yaml header as data for template
|
||||
ctx := make(pongo2.Context)
|
||||
ctx := make(map[string]interface{})
|
||||
ctx["This"] = newConfig.This
|
||||
ctx["Meta"] = newConfig.Meta
|
||||
ctx["Data"] = newConfig.Data
|
||||
@ -502,7 +404,7 @@ RewriteRule ^$ %{REQUEST_URI}`+goToFixed+`/ [R,L]
|
||||
|
||||
log.Debugf("rendering template '%s' for '%s'", *newConfig.Template, outFile)
|
||||
templateFilename := *newConfig.Template
|
||||
result, err := helper.RenderTemplate(*newConfig.Template, ctx)
|
||||
result, err := helper.RenderTemplate(*newConfig.Template, conf, newConfig, &ctx)
|
||||
if err != nil {
|
||||
log.Panicf("could not execute template '%s' for input file '%s': %s", templateFilename, inFile, err)
|
||||
}
|
||||
@ -632,17 +534,17 @@ func main() {
|
||||
defaultFilenameIgnore := "^_"
|
||||
defaultFilenameOutputExtension := "html"
|
||||
|
||||
defaultPathConfig := new(PathConfig)
|
||||
defaultPathConfig := new(config.PathConfig)
|
||||
defaultPathConfig.Template = &defaultTemplate
|
||||
defaultPathConfig.Index = &indexStruct{
|
||||
defaultPathConfig.Index = &config.IndexConfig{
|
||||
InputFile: &defaultInputFile,
|
||||
OutputFile: &defaultOutputFile,
|
||||
}
|
||||
defaultPathConfig.Path = &pathStruct{
|
||||
defaultPathConfig.Path = &config.DirnameConfig{
|
||||
Strip: &defaultPathStrip,
|
||||
IgnoreForNav: &defaultPathIgnoreForNav,
|
||||
}
|
||||
defaultPathConfig.Filename = &filenameStruct{
|
||||
defaultPathConfig.Filename = &config.FilenameConfig{
|
||||
Strip: &defaultFilenameStrip,
|
||||
Ignore: &defaultFilenameIgnore,
|
||||
OutputExtension: &defaultFilenameOutputExtension,
|
||||
|
1
vendor/github.com/aymerick/raymond
generated
vendored
1
vendor/github.com/aymerick/raymond
generated
vendored
@ -1 +0,0 @@
|
||||
Subproject commit b565731e1464263de0bda75f2e45d97b54b60110
|
1
vendor/github.com/gosimple/slug
generated
vendored
1
vendor/github.com/gosimple/slug
generated
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 42ac18c34c2c6f4843fa48167317dcaa9416d1e2
|
1
vendor/github.com/rainycape/unidecode
generated
vendored
1
vendor/github.com/rainycape/unidecode
generated
vendored
@ -1 +0,0 @@
|
||||
Subproject commit cb7f23ec59bec0d61b19c56cd88cee3d0cc1870c
|
@ -1,9 +1,10 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block part0 %}
|
||||
<h1>{{ details.title }}</h1>
|
||||
<h1>{{ details.title }}</h1>
|
||||
{{ details.teaser|markdown }}
|
||||
{% endblock part0 %}
|
||||
|
||||
{% block part1 %}
|
||||
|
||||
{{ details.body|markdown }}
|
||||
{% endblock part1 %}
|
Loading…
Reference in New Issue
Block a user