Skip to content
Snippets Groups Projects
generate.go 5.09 KiB
Newer Older
package html

import (
	"encoding/json"
	"github.com/andybalholm/cascadia"
Volker Schukai's avatar
Volker Schukai committed
	"github.com/charmbracelet/log"
	"gitlab.schukai.com/oss/bob/types"
	"gitlab.schukai.com/oss/libraries/go/markup/html/engine"
	"golang.org/x/net/html"
	"gopkg.in/yaml.v3"
	"os"
	"path"
	"strings"
)

Volker Schukai's avatar
Volker Schukai committed
func GenerateFiles(dataPath, templates, out string) error {
Volker Schukai's avatar
Volker Schukai committed
	templatesDir := path.Dir(templates)

	yamlFile, err := os.ReadFile(dataPath)
	if err != nil {
		return err
	}

	storage := types.NewPageDataStorage()
	if err := yaml.Unmarshal(yamlFile, storage); err != nil {
		return err
	}

	for name, page := range storage {
Volker Schukai's avatar
Volker Schukai committed
		p := path.Join(templatesDir, name)
Volker Schukai's avatar
Volker Schukai committed
		log.Info("Generating with template " + p)
		generatedHtml, err := Generate(page, p)
		if err != nil {
Volker Schukai's avatar
Volker Schukai committed
			log.Error("Error " + err.Error() + " while generating " + p)
Volker Schukai's avatar
Volker Schukai committed
		outFile := path.Join(out, page.Export)

		dir := path.Dir(outFile)
		if err := os.MkdirAll(dir, 0755); err != nil {
Volker Schukai's avatar
Volker Schukai committed

			log.Error("Error " + err.Error() + " while creating directory " + dir)
Volker Schukai's avatar
Volker Schukai committed
			return err
		}
Volker Schukai's avatar
Volker Schukai committed
		log.Info("Writing " + outFile)
		err = os.WriteFile(outFile, []byte(generatedHtml), 0644)
		if err != nil {
Volker Schukai's avatar
Volker Schukai committed
			log.Error("Error " + err.Error() + " while writing " + outFile)
			return err
		}

	}

	return nil
}

func Generate(data *types.PageData, name string) (string, error) {

	dataset := make(map[any]any)
	dataset["lang"] = data.Lang
	dataset["title"] = data.Title
Volker Schukai's avatar
Volker Schukai committed
	if data.Title == "" {
		log.Warn("No title set for " + name)
	}

	log.Info("Generating " + data.Export + " with template " + name)

	dataset["meta"] = make(map[any]any)
	for k, v := range data.Meta {
		dataset["meta"].(map[any]any)[k] = v
	}
	dataset["images"] = make(map[any]any, 0)
	for _, v := range data.Images {
		dataset["images"].(map[any]any)[v.Id] = map[any]any{
			"source": v.Source,
			"alt":    v.Alt,
			"title":  v.Title,
			"id":     v.Id,
		}
	}

	dataset["anchors"] = make(map[any]any, 0)
	for _, v := range data.Anchors {
		dataset["anchors"].(map[any]any)[v.Id] = map[any]any{
			"href":     v.Href,
			"hreflang": v.HrefLang,
			"title":    v.Title,
			"id":       v.Id,
		}
	}

	dataset["text"] = make(map[any]any, 0)
	for _, v := range data.Text {
		dataset["text"].(map[any]any)[v.Id] = map[any]any{"text": v.Text}
	}

	dataset["translations"] = make(map[string]any, 0)
	for _, v := range data.Translations {
		j, err := json.Marshal(v.KeyValues)
		if err != nil {
			return "", err
		}
		dataset["translations"].(map[string]any)[v.Id] = map[string]string{
			"content": string(j),
		}

	}

	e := engine.New(dataset)

	template, err := os.ReadFile(name)
	if err != nil {
		return "", err
	}

	stringWriter := &strings.Builder{}
	e.ProcessHtml(stringWriter, strings.NewReader(string(template)))

	return doModifications(data, stringWriter.String())

}

func doModifications(page *types.PageData, from string) (string, error) {

Volker Schukai's avatar
Volker Schukai committed
	log.Info("Modifications for " + page.Export)

	node, err := html.Parse(strings.NewReader(from))
	if err != nil {
		return "", err
	}

Volker Schukai's avatar
Volker Schukai committed
	if len(page.Modifications.Remove) == 0 {
		log.Info("No remove modifications set for " + page.Export)
	} else {
		log.Infof("There are %d remove modifications set for %s", len(page.Modifications.Remove), page.Export)
	}

	for _, m := range page.Modifications.Remove {

		selector, err := cascadia.Parse(m)
		if err != nil {
			return "", err
		}
		list := cascadia.QueryAll(node, selector)
		for _, n := range list {
			if n.Parent != nil {
				n.Parent.RemoveChild(n)
			}
		}
Volker Schukai's avatar
Volker Schukai committed
	if len(page.Modifications.Add) == 0 {
		log.Info("No add modifications set for " + page.Export)
	} else {
		log.Infof("There are %d add modifications set for %s", len(page.Modifications.Add), page.Export)
	}

	for _, m := range page.Modifications.Add {
		selector, err := cascadia.Parse(m.Selector)
		if err != nil {
			return "", err
		}
		list := cascadia.QueryAll(node, selector)
		for _, n := range list {
			if n.Parent != nil {

				nodes, err := html.ParseFragment(strings.NewReader(m.Html), n)
				if err != nil {
					return "", err
				}

				for _, added := range nodes {
					n.InsertBefore(added, n.FirstChild)
				}
			}
		}
	}

Volker Schukai's avatar
Volker Schukai committed
	if len(page.Modifications.SetAttribute) == 0 {
		log.Info("No set attribute modifications set for " + page.Export)
	} else {
		log.Infof("There are %d set attribute modifications set for %s", len(page.Modifications.SetAttribute), page.Export)
	}

	for _, m := range page.Modifications.SetAttribute {
		selector, err := cascadia.Parse(m.Selector)
		if err != nil {
			return "", err
		}
		list := cascadia.QueryAll(node, selector)
		for _, n := range list {
			if n.Parent != nil {

				removeAttr(n, m.Name)

				attr := n.Attr
				attr = append(attr, html.Attribute{
					Key: m.Name,
					Val: m.Value,
				})

Volker Schukai's avatar
Volker Schukai committed
				log.Info("Set attribute " + m.Name + " to " + m.Value + " for " + page.Export)

				n.Attr = attr
			}
		}
	}

	stringWriter := &strings.Builder{}
	err = html.Render(stringWriter, node)
	if err != nil {
		return "", err
	}
	return stringWriter.String(), nil

func removeAttr(n *html.Node, search string) {
	i := -1
	for index, attr := range n.Attr {
		if attr.Key == search {
			i = index
			break
		}
	}
	if i != -1 {
		n.Attr = append(n.Attr[:i], n.Attr[i+1:]...)
	}

}