package mark2web import ( "bytes" "io/ioutil" "path" "regexp" "strings" "gitbase.de/apairon/mark2web/config" "gitbase.de/apairon/mark2web/context" "gitbase.de/apairon/mark2web/helper" "github.com/davecgh/go-spew/spew" "github.com/flosch/pongo2" cpy "github.com/otiai10/copy" "gopkg.in/yaml.v2" ) // ReadContentDir walks through content directory and builds the tree of configurations func ReadContentDir(inBase string, outBase string, dir string, conf *config.PathConfig, tree *config.PathConfigTree) { context.FillNodeConfig(tree, inBase, outBase, dir, conf) files, err := ioutil.ReadDir(tree.InputPath) if err != nil { helper.Log.Panic(err) } // first only files for _, f := range files { p := tree.InputPath + "/" + f.Name() if !f.IsDir() && f.Name() != "config.yml" { switch path.Ext(f.Name()) { case ".md": helper.Log.Debugf(".MD %s", p) if tree.InputFiles == nil { tree.InputFiles = make([]string, 0) } tree.InputFiles = append(tree.InputFiles, f.Name()) break default: helper.Log.Debugf("FIL %s", p) if tree.OtherFiles == nil { tree.OtherFiles = make([]string, 0) } tree.OtherFiles = append(tree.OtherFiles, f.Name()) } } } // only directorys, needed config before for _, f := range files { p := tree.InputPath + "/" + f.Name() if f.IsDir() { helper.Log.Debugf("DIR %s", p) newTree := new(config.PathConfigTree) if tree.Sub == nil { tree.Sub = make([]*config.PathConfigTree, 0) } tree.Sub = append(tree.Sub, newTree) ReadContentDir(tree.InputPath, tree.OutputPath, f.Name(), tree.Config, newTree) } } } // ProcessContent walks recursivly through the input paths and processes all files for output func ProcessContent(rootConf, conf *config.PathConfigTree) { helper.CreateDirectory(conf.OutputPath) curNavPath := strings.TrimPrefix(conf.OutputPath, config.Config.Directories.Output) curNavPath = strings.TrimPrefix(curNavPath, "/") curNavPath = path.Clean(curNavPath) if curNavPath == "." { curNavPath = "" } goTo := conf.Config.This.GoTo if goTo != nil && *goTo != "" { goToFixed := *goTo if strings.HasPrefix(goToFixed, "/") { goToFixed = helper.BackToRoot(curNavPath) + goToFixed } goToFixed = path.Clean(goToFixed) switch config.Config.Webserver.Type { case "apache": htaccessFile := conf.OutputPath + "/.htaccess" helper.Log.Noticef("writing '%s' with redirect to: %s", htaccessFile, goToFixed) err := ioutil.WriteFile(htaccessFile, []byte(`RewriteEngine on RewriteRule ^$ %{REQUEST_URI}`+goToFixed+`/ [R,L] `), 0644) if err != nil { helper.Log.Panicf("could not write '%s': %s", htaccessFile, err) } break } } for _, file := range conf.InputFiles { var input []byte inFile := "InputString" if file != "" { inFile = conf.InputPath + "/" + file helper.Log.Debugf("reading file: %s", inFile) var err error input, err = ioutil.ReadFile(inFile) if err != nil { helper.Log.Panicf("could not read '%s':%s", inFile, err) } helper.Log.Infof("processing input file '%s'", inFile) } else { // use input string if available and input filename == "" var inputString *string if i := conf.Config.Index; i != nil { inputString = i.InputString } if inputString != nil { helper.Log.Debugf("using input string instead of file") input = []byte(*inputString) } } newConfig := new(config.PathConfig) regex := regexp.MustCompile("(?s)^---(.*?)\\r?\\n\\r?---\\r?\\n\\r?") yamlData := regex.Find(input) if string(yamlData) != "" { // replace tabs yamlData = bytes.Replace(yamlData, []byte("\t"), []byte(" "), -1) helper.Log.Debugf("found yaml header in '%s', merging config", inFile) err := yaml.Unmarshal(yamlData, newConfig) if err != nil { helper.Log.Panicf("could not parse YAML header from '%s': %s", inFile, err) } helper.Log.Debug("merging config with upper config") oldThis := newConfig.This helper.Merge(newConfig, conf.Config) newConfig.This = oldThis helper.Log.Debug(spew.Sdump(newConfig)) input = regex.ReplaceAll(input, []byte("")) } else { helper.Merge(newConfig, conf.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 outputFilename := file var indexInputFile *string var indexOutputFile *string if i := newConfig.Index; i != nil { indexInputFile = i.InputFile indexOutputFile = i.OutputFile } if indexInputFile != nil && *indexInputFile == file && indexOutputFile != nil && *indexOutputFile != "" { outputFilename = *indexOutputFile } else { if stripRegex != nil && *stripRegex != "" { regex, err := regexp.Compile(*stripRegex) if err != nil { helper.Log.Panicf("could not compile filename.strip regexp '%s' for file '%s': %s", *stripRegex, inFile, err) } outputFilename = regex.ReplaceAllString(outputFilename, "$1") } if outputExt != nil && *outputExt != "" { outputFilename += "." + *outputExt } } outFile := conf.OutputPath + "/" + outputFilename helper.Log.Debugf("using '%s' as output file", outFile) // use --- for splitting document in markdown parts regex := regexp.MustCompile("\\r?\\n\\r?---\\r?\\n\\r?") inputParts := regex.Split(string(input), -1) htmlParts := make([]*pongo2.Value, 0) chromaRenderer := false chromaStyle := "monokai" if m := newConfig.Markdown; m != nil { if m.ChromaRenderer != nil && *m.ChromaRenderer { chromaRenderer = true } if m.ChromaStyle != nil && *m.ChromaStyle != "" { chromaStyle = *m.ChromaStyle } } for _, iPart := range inputParts { htmlParts = append(htmlParts, pongo2.AsSafeValue( string(helper.RenderMarkdown([]byte(iPart), chromaRenderer, chromaStyle)))) } // build navigation navMap := make(map[string]*NavElement) navSlice := make([]*NavElement, 0) navActive := make([]*NavElement, 0) BuildNavigation(rootConf, &navMap, &navSlice, &navActive, curNavPath) // read yaml header as data for template ctx := context.NewContext() ctx["This"] = newConfig.This ctx["Meta"] = newConfig.Meta ctx["Data"] = newConfig.Data ctx["ColMap"] = rootConf.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: rootConf.ColMap, Data: rootConf.Config.Data, This: rootConf.Config.This, SubMap: &navMap, SubSlice: &navSlice, } } helper.Log.Debugf("rendering template '%s' for '%s'", *newConfig.Template, outFile) templateFilename := *newConfig.Template result, err := RenderTemplate(*newConfig.Template, conf, newConfig, &ctx) if err != nil { helper.Log.Panicf("could not execute template '%s' for input file '%s': %s", templateFilename, inFile, err) } result = FixAssetsPath(result, curNavPath) helper.Log.Noticef("writing to output file: %s", outFile) err = ioutil.WriteFile(outFile, []byte(result), 0644) if err != nil { helper.Log.Panicf("could not write to output file '%s': %s", outFile, err) } //fmt.Println(string(html)) } } // process other files, copy... for _, file := range conf.OtherFiles { switch config.Config.OtherFiles.Action { case "copy": from := conf.InputPath + "/" + file to := conf.OutputPath + "/" + file helper.Log.Noticef("copying file from '%s' to '%s'", from, to) err := cpy.Copy(from, to) if err != nil { helper.Log.Panicf("could not copy file from '%s' to '%s': %s", from, to, err) } } } i := 0 for i < len(conf.Sub) { ProcessContent(rootConf, conf.Sub[i]) i++ } }