Skip to content
Snippets Groups Projects
Verified Commit fec03e94 authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

feat: #1

parent e2b64018
No related branches found
No related tags found
No related merge requests found
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="run example prepare " type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="bob" />
<working_directory value="$PROJECT_DIR$/../../alvine/local-dev/components/alvine/apps/test/source" />
<parameters value="--verbose template prepare --input $PROJECT_DIR$/development/examples/example1/template/ --output $PROJECT_DIR$/development/examples/example1/build --data-file=$PROJECT_DIR$/development/examples/example1/pages/en.yaml" />
<EXTENSION ID="com.fapiko.jetbrains.plugins.better_direnv.runconfigs.GolandRunConfigurationExtension">
<option name="DIRENV_ENABLED" value="false" />
<option name="DIRENV_TRUSTED" value="false" />
</EXTENSION>
<kind value="PACKAGE" />
<package value="gitlab.schukai.com/oss/bob" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/application/source/main.go" />
<method v="2" />
</configuration>
</component>
\ No newline at end of file
# Bob
bob is an HTML and HTML fragment builder
Bob is an HTML and HTML fragment builder
## Documentation
......@@ -14,7 +14,8 @@ wget -O ~/.local/bin/bob http://download.schukai.com/tools/bob/bob-$( uname -s |
### Nix/Flake Support
This repository contains a file called flake.nix. You can install this program using the nix package manager.
This repository contains a file called flake.nix. You can install this program
using the [nix package manager](https://nixos.org/).
## Usage
......@@ -27,7 +28,7 @@ bob template prepare --input ./templates/ --output ./output/ --data-file ./data.
```
This will create files in the `./output/` directory with all parsed templates from `./templates/` directory.
Also, a data YAML. This data YAML is used to generate the final files.
Also, a data YAML. This data YAML is used to generate the final files with the `bob html generate` command.
This command prepares the title, description, keywords, and other metadata for the templates.
Furthermore, it will parse the templates for images, anchors, and text.
......@@ -43,6 +44,21 @@ If the argument `--data-file` is not set, the data YAML will be written to `./ou
| `<a href="https://gitlab.schukai.com/oss/bob">` | `<a href="https://gitlab.schukai.com/oss/bob" data-attributes="href path:a.id1004.href">` |
| `<p>Bob is a html and html fragment builder</p>` | `<p><span data-attributes="text path:p.id1005.text">Bob is a html and html fragment builder</span></p>` |
If you want to translate the text, you can copy the default `data.yaml` to a new file and translate the text there.
A good practice is to use the language code as the file name.
For example, `de.yaml` for German, `en.yaml` for English, etc.
Beside text, images and metadata, special attributes are also extracted.
For example, the Monster datatable headers `data-monster-head` are extracted.
```html
<monster-datatable>
<template id="datatable-order-list-row">
<div data-monster-head="OID" ...></a></div>
...
```
#### HTML
......@@ -54,9 +70,10 @@ will be processed.
```bash
bob html generate --input ./input/ --output ./output/
bob html generate --input ./input/ --output ./output/ --data-files ./pages/
```
If the `--data-files' attribute is not defined, the `--input' directory is used.
The yaml looks like:
......@@ -117,8 +134,8 @@ test1.html:
The translations are set in a json inside a script tag.
The modifications run last. Here you can remove tags, add inhaklt and set attributes.
The `modifications' rules are executed last. Here you can remove tags, add content and set attributes.
##### Sync
......
......@@ -20,13 +20,14 @@ type Definition struct {
Prepare struct {
Input string `short:"i" long:"input" description:"Directory with html files to prepare" required:"true"`
Output string `short:"o" long:"output" description:"Directory to save prepared html files" required:"true"`
DataFile string `short:"d" long:"data-file" description:"Name of the data file to use, default is data.yaml"`
DataFile string `short:"d" long:"data-file" description:"Name of the main data file" default:"data.yaml"`
} `command:"prepare" description:"Prepare content from a file" call:"PrepareTemplate"`
} `command:"template" description:"Template commands"`
HTML struct {
Generate struct {
Input string `short:"i" long:"input" description:"Directory with prepared html files" required:"true"`
Output string `short:"o" long:"output" description:"Directory to save generated template files" required:"true"`
DataFiles string `short:"d" long:"data-files" description:"Directory with data files" required:"true"`
} `command:"generate" description:"Generate html files from a file" call:"GenerateHTML"`
Sync struct {
Specification string `short:"s" long:"specification" description:"Specification file" required:"true"`
......@@ -55,7 +56,11 @@ func (d *Definition) SyncHTML(s *xflags.Settings[Definition]) {
}
func (d *Definition) GenerateHTML(s *xflags.Settings[Definition]) {
err := filepath.Walk(d.HTML.Generate.Input, func(p string, info os.FileInfo, err error) error {
if d.HTML.Generate.DataFiles == "" {
d.HTML.Generate.DataFiles = d.HTML.Generate.Input
}
err := filepath.Walk(d.HTML.Generate.DataFiles, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
......@@ -70,7 +75,7 @@ func (d *Definition) GenerateHTML(s *xflags.Settings[Definition]) {
return nil
}
return html2.GenerateFiles(p, d.HTML.Generate.Output)
return html2.GenerateFiles(p, d.HTML.Generate.Input, d.HTML.Generate.Output)
})
if err != nil {
......
package constants
const DataBobReferenceAttributeKey = "data-bob-reference"
......@@ -12,9 +12,9 @@ import (
"strings"
)
func GenerateFiles(dataPath, out string) error {
func GenerateFiles(dataPath, templates, out string) error {
dir := path.Dir(dataPath)
templatesDir := path.Dir(templates)
yamlFile, err := os.ReadFile(dataPath)
if err != nil {
......@@ -27,7 +27,7 @@ func GenerateFiles(dataPath, out string) error {
}
for name, page := range storage {
p := path.Join(dir, name)
p := path.Join(templatesDir, name)
generatedHtml, err := Generate(page, p)
if err != nil {
return err
......
......@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"github.com/andybalholm/cascadia"
"gitlab.schukai.com/oss/bob/constants"
"gitlab.schukai.com/oss/bob/types"
"gitlab.schukai.com/oss/bob/util"
"golang.org/x/net/html"
......@@ -173,11 +174,11 @@ func prepareAnchors(node *html.Node, storage *types.PageData) {
hreflang := util.GetAttribute(n.Attr, "hreflang")
href := util.GetAttribute(n.Attr, "href")
id := util.GetOrCreateId(n, title+hreflang+href)
id := util.GetOrCreateReference(n, title+hreflang+href)
if id == "" {
id, _ = util.RandomString(8)
n.Attr = removeAttribute(n.Attr, "id")
n.Attr = append(n.Attr, html.Attribute{Key: "id", Val: id})
n.Attr = removeAttribute(n.Attr, constants.DataBobReferenceAttributeKey)
n.Attr = append(n.Attr, html.Attribute{Key: constants.DataBobReferenceAttributeKey, Val: id})
}
setDataAttributesAttribute(n, attributeAttributes, "href", "path:anchors."+id+".href")
......@@ -231,11 +232,11 @@ func prepareImages(node *html.Node, storage *types.PageData) {
title := util.GetAttribute(n.Attr, "title")
source := util.GetAttribute(n.Attr, "src")
id := util.GetOrCreateId(n, alt+title+source)
id := util.GetOrCreateReference(n, alt+title+source)
if id == "" {
id, _ = util.RandomString(8)
n.Attr = removeAttribute(n.Attr, "id")
n.Attr = append(n.Attr, html.Attribute{Key: "id", Val: id})
n.Attr = removeAttribute(n.Attr, constants.DataBobReferenceAttributeKey)
n.Attr = append(n.Attr, html.Attribute{Key: constants.DataBobReferenceAttributeKey, Val: id})
}
setDataAttributesAttribute(n, attributeAttributes, "src", "path:content."+id+".src")
......@@ -284,7 +285,7 @@ func prepareTranslationJson(node *html.Node, storage *types.PageData) {
for _, n := range list {
id := util.GetAttribute(n.Attr, "id")
id := util.GetAttribute(n.Attr, constants.DataBobReferenceAttributeKey)
typ := util.GetAttribute(n.Attr, "type")
n.Attr = removeAttribute(n.Attr, attributeReplace)
......@@ -377,19 +378,7 @@ func runNodes(n *html.Node, storage *types.PageData) {
}
func checkNodes(n *html.Node, storage *types.PageData) {
if n.Parent != nil {
if n.Parent.Type == html.ElementNode {
if n.Parent.Data == "script" || n.Parent.Data == "style" {
return
}
}
}
if n.Type != html.TextNode {
return
}
func handleTextNode(n *html.Node, storage *types.PageData) {
content := strings.TrimSpace(n.Data)
if content == "" {
return
......@@ -406,7 +395,7 @@ func checkNodes(n *html.Node, storage *types.PageData) {
Data: atom.Span.String(),
Attr: []html.Attribute{
{
Key: "id",
Key: constants.DataBobReferenceAttributeKey,
Val: id,
},
{
......@@ -424,6 +413,58 @@ func checkNodes(n *html.Node, storage *types.PageData) {
Id: id,
Text: content,
})
}
func checkNodes(n *html.Node, storage *types.PageData) {
if n.Parent != nil {
if n.Parent.Type == html.ElementNode {
if n.Parent.Data == "script" || n.Parent.Data == "style" {
return
}
}
}
if n.Type == html.TextNode {
handleTextNode(n, storage)
} else if n.Type == html.ElementNode {
switch n.Data {
case "monster-datatable":
checkMonsterDatatableHead(n, storage)
}
}
}
func checkMonsterDatatableHead(n *html.Node, storage *types.PageData) {
selector, err := cascadia.Parse("[data-monster-head]")
if err != nil {
return
}
list := cascadia.QueryAll(n, selector)
if list == nil {
return
}
for _, div := range list {
id := util.GetAttribute(div.Attr, constants.DataBobReferenceAttributeKey)
if id == "" {
id = util.GetNextId()
div.Attr = append(div.Attr, html.Attribute{Key: constants.DataBobReferenceAttributeKey, Val: id})
}
head := util.GetAttribute(div.Attr, "data-monster-head")
div.Attr = removeAttribute(div.Attr, "data-attributes")
div.Attr = append(div.Attr, html.Attribute{Key: "data-attributes", Val: "data-monster-head path:text." + id + ".text"})
storage.Text = append(storage.Text, types.Text{
Id: id,
Text: head,
})
}
}
......
......@@ -2,6 +2,7 @@ package util
import (
"errors"
"gitlab.schukai.com/oss/bob/constants"
"golang.org/x/net/html"
"os"
"path"
......@@ -37,11 +38,11 @@ func GetNextId() string {
}
func GetOrCreateId(node *html.Node, text string) string {
func GetOrCreateReference(node *html.Node, text string) string {
var err error
nodeId := GetAttribute(node.Attr, "id")
nodeId := GetAttribute(node.Attr, constants.DataBobReferenceAttributeKey)
if nodeId != "" {
return nodeId
}
......@@ -51,7 +52,7 @@ func GetOrCreateId(node *html.Node, text string) string {
nodeId = GetNextId()
}
node.Attr = append(node.Attr, html.Attribute{Key: "id", Val: nodeId})
node.Attr = append(node.Attr, html.Attribute{Key: constants.DataBobReferenceAttributeKey, Val: nodeId})
return nodeId
}
......
test.html:
export: en/test.html
lang: en
title: Bad Request
meta:
author: schukai GmbH
description: The request was malformed or invalid.
images:
- id: tickyesdata-image-gi-4013311193
source: |-
data:image/gif;base64,R0lGODdhEAAQAMwAAPj7+FmhUYjNfGuxYYDJdYTIeanOpT+DOTuANXi/bGOrWj6CONzv2sPjv2Cm
V1unU4zPgI/Sg6DJnJ3ImTh8Mtbs00aNP1CZSGy0YqLEn47RgXW8amasW7XWsmmvX2iuXiwAAAAAEAAQAAAFVyAgjmRpnihqGCkpDQ
PbGkNUOFk6DZqgHCNGg2T4QAQBoIiRSAwBE4VA4FACKgkB5NGReASFZEmxsQ0whPDi9BiACYQAInXhwOUtgCUQoORFCGt/g4QAIQA7
alt: tick
title: "yes"
anchors:
- id: yes-a-html
href: /a.html
hreflang: ""
title: "Yes"
- id: QPCI6WO0
href: ""
hreflang: ""
title: ""
text:
- text: Bad Request
id: bad-request
- text: The request was incorrect, the server could not process it.
id: the-request-was-inco-2640993422
- text: Try submitting your
id: try-submitting-your
- text: request
id: request
- text: again (sometimes this helps).
id: again-sometimes-this-2086042570
- text: Contact support if you continue to have problems.
id: contact-support-if-y-3404332025
- text: |-
If you received this message as a result of a request to the server API, then check the structure
against the documentation.
id: if-you-received-this-423958995
- text: 'You can try the following steps:'
id: you-can-try-the-foll-3363859033
- text: OID
id: id1000
- text: date
id: id1001
- text: username
id: id1002
- text: customer
id: id1003
- text: zipcode
id: id1004
- text: city
id: id1005
- text: country
id: id1006
- text: street
id: id1007
- text: order state
id: id1008
- text: workflow state
id: id1009
- text: total
id: id1010
- text: company
id: id1011
- text: channel order number
id: id1012
- text: RESUBMISSIONDATE!!
id: id1013
- text: payment type
id: id1014
- text: ','
id: id1015
translations: []
modifications:
remove: []
add: []
attributes: []
test1.html:
export: de/test1.html
lang: de
......@@ -44,7 +124,7 @@ test1.html:
- text: 'You can try the following steps:'
id: you-can-try-the-foll-3363859033
translations:
- id: translations
- id: ""
type: application/json
translations:
key1: value1
......@@ -53,15 +133,9 @@ test1.html:
one: value3
two: value4
modifications:
remove:
- .gradient
add:
- selector: .infobox
html: <b><span>GURKE</span></b>
attributes:
- selector: '#mainscript'
name: type
value: YES
remove: []
add: []
attributes: []
test2.html:
export: en/test2.html
lang: en
......@@ -167,7 +241,7 @@ test4.html:
- text: one-time password
id: one-time-password
- text: .
id: id1000
id: id1016
translations: []
modifications:
remove: []
......
......@@ -40,26 +40,26 @@
<img width="16" height="16" alt="tick" title="yes" src="data:image/gif;base64,R0lGODdhEAAQAMwAAPj7+FmhUYjNfGuxYYDJdYTIeanOpT+DOTuANXi/bGOrWj6CONzv2sPjv2Cm
V1unU4zPgI/Sg6DJnJ3ImTh8Mtbs00aNP1CZSGy0YqLEn47RgXW8amasW7XWsmmvX2iuXiwAAAAAEAAQAAAFVyAgjmRpnihqGCkpDQ
PbGkNUOFk6DZqgHCNGg2T4QAQBoIiRSAwBE4VA4FACKgkB5NGReASFZEmxsQ0whPDi9BiACYQAInXhwOUtgCUQoORFCGt/g4QAIQA7" id="tickyesdata-image-gi-4013311193" data-attributes="alt path:content.tickyesdata-image-gi-4013311193.alt,src path:content.tickyesdata-image-gi-4013311193.src,title path:content.tickyesdata-image-gi-4013311193.title"/>
<h1 class="deco"><span id="bad-request" data-replace-self="path:text.bad-request.text">Bad Request</span></h1>
<p><span id="the-request-was-inco-2640993422" data-replace-self="path:text.the-request-was-inco-2640993422.text">The request was incorrect, the server could not process it.
</span></p><p>
<h1 class="deco">Bad Request</h1>
<p>The request was incorrect, the server could not process it.
</p><p>
</p><div class="infobox">
<div>
<ul>
<li><span id="try-submitting-your" data-replace-self="path:text.try-submitting-your.text">Try submitting your </span><a href="/a.html" title="Yes" id="yes-a-html"
<li>Try submitting your<a href="/a.html" title="Yes" id="yes-a-html"
data-attributes="href path:anchors.yes-a-html.href,title path:anchors.yes-a-html.title,hreflang path:anchors.yes-a-html.hreflang">request</a> again (sometimes this helps).</li>
<li><span id="contact-support-if-y-3404332025" data-replace-self="path:text.contact-support-if-y-3404332025.text">Contact support if you continue to have problems.</span></li>
<li>Contact support if you continue to have problems.</li>
<li><span id="if-you-received-this-423958995" data-replace-self="path:text.if-you-received-this-423958995.text">If you received this message as a result of a request to the server API, then check the structure
<li>If you received this message as a result of a request to the server API, then check the structure
against the documentation.
</span></li>
</li>
</ul>
<p><span id="you-can-try-the-foll-3363859033" data-replace-self="path:text.you-can-try-the-foll-3363859033.text">You can try the following steps:</span></p>
<p>You can try the following steps:</p>
</div>
</div>
......@@ -68,5 +68,25 @@ data-attributes="href path:anchors.yes-a-html.href,title path:anchors.yes-a-html
</monster-state>
<monster-datatable id="datatable-order-list" data-monster-datasource-selector="#orderListDatasource">
<template id="datatable-order-list-row">
<div data-monster-head="OID" data-monster-mode="fixed" data-monster-sortable="oid" data-monster-grid-template="0.5fr"><a data-monster-attributes="href path:datatable-order-list-row.oid | tostring | prefix:/app/commerce/order-detail?oid=" data-monster-replace="path:datatable-order-list-row.oid"></a></div>
<div data-monster-head="date" data-monster-sortable="orderDate" data-monster-replace="path:datatable-order-list-row.orderDate | datetimeformat"></div>
<div data-monster-head="username" data-monster-mode="hidden" data-monster-replace="path:datatable-order-list-row.userName"></div>
<div data-monster-head="customer">, </div>
<div data-monster-head="zipcode" data-monster-replace="path:datatable-order-list-row.deliveryAddressZipcode"></div>
<div data-monster-head="city" data-monster-replace="path:datatable-order-list-row.deliveryAddressCity"></div>
<div data-monster-head="country" data-monster-replace="path:datatable-order-list-row.deliveryAddressCountry | prefix:country_ | i18n"></div>
<div data-monster-head="street" data-monster-mode="hidden" data-monster-replace="path:datatable-order-list-row.deliveryAddressAddress1"></div>
<div data-monster-head="order state" data-monster-align="end"></div>
<div data-monster-head="workflow state" data-monster-align="end" data-monster-replace="path:datatable-order-list-row.workflowState"></div>
<div data-monster-head="total" data-monster-align="end"></div>
<div data-monster-head="company" data-monster-grid-template="0.8fr" data-monster-replace="path:datatable-order-list-row.companySHID | tostring | prefix:&lt;img src=&#39;/alvine/upload/company/files/ | suffix:/shopicon.gif&#39;&gt;"></div>
<div data-monster-head="channel order number" data-monster-replace="path:datatable-order-list-row.channelOrderID"></div>
<div data-monster-head="resubmissionDate" data-monster-mode="hidden"></div>
<div data-monster-head="payment type" data-monster-replace="path:datatable-order-list-row.localStrings.paymentType"></div>
</template>
</monster-datatable>
</body></html>
\ No newline at end of file
......@@ -3,11 +3,11 @@
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1689667485,
"narHash": "sha256-tLNoMRSPLlW1D4wgNpSIPUUHd6x1GdjAhETLpRTKnfo=",
"lastModified": 1690413082,
"narHash": "sha256-CPR3WcnrrIiDZJiMo4RlyZB0M3576pHmtlTUnMUTugA=",
"owner": "cachix",
"repo": "devenv",
"rev": "6f8add968bc12bf81d845eb7dc684a0733bb1518",
"rev": "148c4a21e50428728e97f3cdf59166b6007db8a7",
"type": "github"
},
"original": {
......@@ -74,11 +74,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1689631193,
"narHash": "sha256-AGSkBZaiTODQc8eT1rZDrQIjtb8JtFwJ0wVPzArlrnM=",
"lastModified": 1690441914,
"narHash": "sha256-Ac+kJQ5z9MDAMyzSc0i0zJDx2i3qi9NjlW5Lz285G/I=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "57695599bdc4f7bfe5d28cfa23f14b3d8bdf8a5f",
"rev": "db8672b8d0a2593c2405aed0c1dfa64b2a2f428f",
"type": "github"
},
"original": {
......@@ -115,11 +115,11 @@
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1689668210,
"narHash": "sha256-XAATwDkaUxH958yXLs1lcEOmU6pSEIkatY3qjqk8X0E=",
"lastModified": 1690464206,
"narHash": "sha256-38V4kmOh6ikpfGiAS+Kt2H/TA2DubSqE66veP/jmB4Q=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "eb433bff05b285258be76513add6f6c57b441775",
"rev": "9289996dcac62fd45836db7c07b87d2521eb526d",
"type": "github"
},
"original": {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment