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

fix: prepare command

parent 5426dbfd
No related branches found
No related tags found
No related merge requests found
Showing
with 885 additions and 373 deletions
......@@ -9,7 +9,8 @@ To check out docs and examples, visit [gitlab.schukai.com/oss/bob](https://gitla
## Installation
```bash
wget -O ~/.local/bin/bob http://download.schukai.com/tools/bob/bob-$( uname -s | tr [:upper:] [:lower:])-$(echo `uname -m | sed s/aarch64/arm64/ | sed s/x86_64/amd64/`) && chmod u+x ~/.local/bin/bob
wget -O ~/.local/bin/bob \
http://download.schukai.com/tools/bob/bob-$( uname -s | tr [:upper:] [:lower:])-$(echo `uname -m | sed s/aarch64/arm64/ | sed s/x86_64/amd64/`) && chmod u+x ~/.local/bin/bob
```
### Nix/Flake Support
......@@ -24,7 +25,10 @@ using the [nix package manager](https://nixos.org/).
#### Prepare
```bash
bob template prepare --input ./templates/ --output ./output/ --data-file ./data.yaml
bob template prepare \
--input ./templates/ \
--output ./output/ \
--data-file ./data.yaml \
```
This will create files in the `./output/` directory with all parsed templates from `./templates/` directory.
......@@ -59,6 +63,25 @@ For example, the Monster datatable headers `data-monster-head` are extracted.
...
```
If you want to add Javascript translations, a consecutive ID is assigned if not
specified separately. This is not ideal for several reasons. It is better to assign
your own ID. To be independent of HTML, JavaScript and CSS selectors, there is a
separate attribute `data-bob-reference` for this.
```html
<script type="application/json"
data-monster-role="translations"
data-bob-reference="my-translation">
{
"key1": "value1",
"key2": "value2",
"key3": {
"one": "value3",
"two": "value4"
}
}
```
#### HTML
......@@ -161,7 +184,8 @@ sync:
- title
```
With the above specification, the `head` node from `./source.html` will be synced to all files in `./` except `./source.html`.
With the above specification, the `head` node from `./source.html` will
be synced to all files in `./` except `./source.html`.
Furthermore, the `title` node will be kept.
Relative paths are relative to the specification file. Absolute paths are absolute, obviously.
......
......@@ -33,6 +33,7 @@
httpie
netcat
memcached
treefmt
fd
];
......
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
title: TESTx
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: test-link-test-html
href: /test.html
hreflang: ""
title: test-link
- id: yes-a-html
href: /a.html
hreflang: ""
title: "Yes"
text:
- text: test-link
id: test-link
- text: Bad xxxx
id: bad-xxxx
- 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
translations:
- id: ""
type: application/json
translations:
key1: value1
key2: value2
key3:
one: value3
two: value4
modifications:
remove: []
add: []
attributes: []
test2.html:
export: en/test2.html
lang: en
title: TEST2
meta:
author: sch2ukai GmbH
description: The 2request 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"
text:
- text: Bad xxxx
id: bad-xxxx
- 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
translations: []
modifications:
remove: []
add: []
attributes: []
test3.html:
export: en/test3.html
lang: en
title: TEST3
meta:
author: schukai3 GmbH
description: The 3request 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"
text:
- text: Bad xxxx
id: bad-xxxx
- 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
translations: []
modifications:
remove: []
add: []
attributes: []
test4.html:
export: en/test4.html
lang: en
title: ""
meta: {}
images: []
anchors:
- id: a-html
href: a.html
hreflang: ""
title: ""
text:
- text: Das ist ein Text
id: das-ist-ein-text
- text: one-time password
id: one-time-password
- text: .
id: id1016
translations: []
modifications:
remove: []
add: []
attributes: []
snippet:
-
source: ../template/test.html
selector: 'monster-state'
destination: ../snippets/meta/container.html
attribute:
- selector: 'li'
name: 'data-state'
value: 'monster'
replacement:
-
selector: 'li>span'
content: '!!!!!!!!!!!!!!!!!!'
sync:
- source:
path: '../original/test1.html'
selector: head
destination:
path: '../original/'
exclude:
- ../original/test1.html
keep:
- title
- meta[name=description]
- meta[name=author]
# - source:
# path: '../original/test1.html'
# selector: '.deco'
# destination:
# path: '../original/'
# exclude:
# - ../original/test1.html
test.html:
export: en/test.html
lang: en
title: 333333
meta:
author: 1111
description: 22222
images:
- id: tickyesdata-image-gi-3753669760
source: |-
data:image/gif;base64,R0lGODdhEAAQAMwAAPj7+FmhUYjNfGuxYYDJdYTIeanOpT+DOTuANXi/bGOrWj6CONzv2sPjv2Cm
V1unU4zPgI/Sg6DJnJ3ImTh8Mtbs00aNP1CZSGy0YqLEn47RgXW8amasW7XWsmmvX2iuXiwAAAAAEAAQAAAFVyAgjmRpnihqGCkpDQ
PbGkNUOFk6DZqgHCNGg2T4QAQBoIiRSAwBE4VA4FACKgkB5NGReASFZEmxsQ0whPDi9BiACYQAInXhwOUtgCUQoORFCGt/g4QAIQA7
alt: tick
title: "yes"
anchors:
- id: mmmm
href: mmmm
hreflang: ""
title: ""
- id: yes-a-html
href: /a.html
hreflang: ""
title: "Yes"
- id: struct-a-data-monste-1746242989
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: GURKE
id: gurke
- 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: oid
- text: date
id: date
- text: username
id: username
- text: customer
id: customer
- text: zipcode
id: zipcode
- text: city
id: city
- text: country
id: country
- text: street
id: street
- text: order state
id: order-state
- text: workflow state
id: workflow-state
- text: total
id: total
- text: company
id: company
- text: channel order number
id: channel-order-number
- text: resubmissionDate
id: resubmission-date
- text: payment type
id: payment-type
translations:
- id: the-translation
type: application/json
translations:
key1: translation1
key2:
other: translation2
key3:
other: translation3
key5: translation4
modifications:
remove: []
add: []
attributes: []
<monster-state>
<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>
</p><div class="infobox">
<div>
<ul>
<li data-state="monster">!!!!!!!!!!!!!!!!!!<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 data-state="monster">!!!!!!!!!!!!!!!!!!</li>
<li data-state="monster">!!!!!!!!!!!!!!!!!!</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>
</div>
</div>
</monster-state>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en" data-attributes="lang path:lang">
<head>
<style>
*:not(:defined) {
visibility: hidden;
}
</style>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="robots" content="noindex"/>
<link rel="icon" type="image/x-icon" href="/asset/icon/favicon.svg"/>
<meta name="theme-color" content="#c10000"/>
<link rel="apple-touch-icon" sizes="180x180" href="/asset/icon/apple-touch-icon.png"/>
<link rel="icon" type="image/png" sizes="32x32" href="/asset/icon/favicon-32x32.png"/>
<link rel="icon" type="image/png" sizes="16x16" href="/asset/icon/favicon-16x16.png"/>
<link rel="mask-icon" href="/asset/icon/safari-pinned-tab.svg" color="#fd1d1d"/>
<meta name="msapplication-TileColor" content="#fd1d1d"/>
<meta name="msapplication-TileImage" content="/asset/icon/mstile-144x144.png"/>
<title data-replace="path:title">Bad Request</title>
<meta name="description" content="The request was malformed or invalid."
data-attributes="content path:meta.description"/>
<meta name="author" content="schukai GmbH" data-attributes="content path:meta.author"/>
<link rel="icon" href="/favicon.ico"/>
<link rel="icon" href="/favicon.svg" type="image/svg+xml"/>
<link rel="apple-touch-icon" href="/apple-touch-icon.png"/>
<script src="/script/main.mjs" type="module"></script>
<script type="application/json" data-monster-role="translations" data-bob-reference="the-translation">
{
"key1": "translation1",
"key2": {
"other": "translation2"
},
"key3": {
"other": "translation3"
},
"key5": "translation4"
}
</script>
</head>
<body>
<header>
<div class="gradient"></div>
</header>
<monster-state>
<a href="mmmm"><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"/></a>
<h1 class="deco">Bad Request</h1>
<p>The request was incorrect, the server could not process it.
</p>
<p>
</p>
<a href="mmmm">GURKE</a>
<div class="infobox">
<div>
<ul>
<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>Contact support if you continue to have problems.</li>
<li>If you received this message as a result of a request to the server API, then check the structure
against the documentation.
</li>
</ul>
<p>You can try the following steps:</p>
<p>You can try the following steps:</p>
</div>
</div>
</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">auto</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="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
......@@ -28,23 +28,66 @@
packages = forAllSystems (system:
let
pkgs = nixpkgsFor.${system};
hashes = import ./hashes.nix;
projectDefinition = import ./project.nix;
projectHash =
if (self ? shortRev)
then self.shortRev
else "dirty";
lastModifiedDate =
self.lastModifiedDate or self.lastModified or "19700101";
sourcePath = ././source;
in
{
bob = pkgs.buildGoModule {
name = "bob";
pname = projectDefinition.name;
version = projectDefinition.version;
# In 'nix develop', we don't need a copy of the source tree
# in the Nix store.
src = ././source;
vendorSha256 = hashes.hashValue;
src = sourcePath;
tags = []; # add your tags here (eq "netgo" "osusergo" "static_build")
ldflags = [
"-X '${projectDefinition.modulePath}/internal/release.version=${projectDefinition.version}'"
"-X '${projectDefinition.modulePath}/internal/release.commit=${projectHash}'"
"-X '${projectDefinition.modulePath}/internal/release.name=${projectDefinition.name}'"
"-X '${projectDefinition.modulePath}/internal/release.mnemonic=${projectDefinition.mnemonic}'"
"-X '${projectDefinition.modulePath}/internal/release.buildDate=${lastModifiedDate}'"
];
meta = with nixpkgs.legacyPackages.${system}.lib; {
description = "Bob: The HTML and HTML fragment builder";
homepage = "https://gitlab.schukai.com/oss/bob";
vendorHash = projectDefinition.vendorHash;
proxyVendor = true;
meta = with system.lib; {
description = projectDefinition.description;
homepage = "https://" + projectDefinition.modulePath;
license = licenses.mit;
maintainers = with maintainers; [ "schukai GmbH" ];
};
maintainers = with maintainers; ["schukai GmbH"];
};
buildInputs = [pkgs.bash];
nativeBuildInputs = with system; [pkgs.alejandra pkgs.shellcheck pkgs.shfmt];
doCheck = true;
checkPhase = ''
cd ${sourcePath}
if ! go test -v ./... 2>&1 | cat ; then
echo "Test failed."
exit 1
fi
echo "Test passed: '$out' is as expected."
'';
};
});
......
{
hashValue = "sha256-M4pZBhlt0CrRgAf9DxRUVNmA2sYxDYIccreOQy7qmrk=";
}
{
name = "Bob";
mnemonic = "bob";
description = "The HTML and HTML fragment builder.";
supportedSystems = ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"];
compileForSystems = ["linux/arm64" "linux/amd64" "darwin/amd64" "windows/amd64"];
modulePath = "gitlab.schukai.com/oss/bob";
k8s = {
loglevel = "info";
port = 80;
replicas = 1;
};
registry = "docker-registry.schukai.com:443";
setup = "done";
version = "0.1.0"; ## do not change this line; it will be updated automatically
vendorHash = "sha256-M4pZBhlt0CrRgAf9DxRUVNmA2sYxDYIccreOQy7qmrk=";
}
\ No newline at end of file
......@@ -2,6 +2,7 @@ package main
import (
"fmt"
"github.com/charmbracelet/log"
html2 "gitlab.schukai.com/oss/bob/html"
template2 "gitlab.schukai.com/oss/bob/template"
"gitlab.schukai.com/oss/bob/types"
......@@ -15,7 +16,6 @@ import (
type Definition struct {
Help struct {
} `command:"help" call:"PrintHelp" description:"Prints this help message"`
Verbose bool `short:"v" long:"verbose" description:"Show verbose debug information"`
Template struct {
Prepare struct {
Input string `short:"i" long:"input" description:"Directory with html files to prepare" required:"true"`
......@@ -106,9 +106,10 @@ func (d *Definition) PrepareTemplate(s *xflags.Settings[Definition]) {
}
}
if d.Verbose {
fmt.Printf("Loaded data file %s\n", i)
}
log.Info("Loaded data file " + i)
// reset all content entries
storage.ResetContent()
}
toDelete := []string{}
......@@ -131,9 +132,7 @@ func (d *Definition) PrepareTemplate(s *xflags.Settings[Definition]) {
return nil
}
if d.Verbose {
fmt.Printf("Prepare %s\n", path)
}
log.Info("Prepare " + path)
key, err := template2.PrepareHtmlFile(path, d.Template.Prepare.Output, storage)
if err != nil {
......@@ -152,9 +151,7 @@ func (d *Definition) PrepareTemplate(s *xflags.Settings[Definition]) {
for _, v := range toDelete {
if d.Verbose {
fmt.Printf("Delete %s\n", v)
}
log.Info("Delete " + v)
delete(storage, v)
}
......@@ -175,9 +172,7 @@ func (d *Definition) PrepareTemplate(s *xflags.Settings[Definition]) {
s.AddError(err)
}
if d.Verbose {
fmt.Printf("Saved data file %s\n", o)
}
log.Info("Saved data file " + o)
}
......
......@@ -12,7 +12,19 @@ require (
)
require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/charmbracelet/lipgloss v0.9.1 // indirect
github.com/charmbracelet/log v0.3.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/volker-schukai/tokenizer v1.0.0 // indirect
gitlab.schukai.com/oss/libraries/go/utilities/data.git v0.2.0 // indirect
gitlab.schukai.com/oss/libraries/go/utilities/pathfinder v0.9.2 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/sys v0.16.0 // indirect
)
......@@ -2,10 +2,32 @@ github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x0
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw=
github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
......@@ -44,6 +66,8 @@ golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
......@@ -71,7 +95,10 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
......
package html
import (
"bytes"
"github.com/andybalholm/cascadia"
"gitlab.schukai.com/oss/bob/types"
"golang.org/x/net/html"
"gopkg.in/yaml.v3"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
)
// Helper function to create a temporary file with content
func createTempFile(content string) (string, error) {
tmpfile, err := ioutil.TempFile("", "example.*.html")
if err != nil {
return "", err
}
if _, err := tmpfile.Write([]byte(content)); err != nil {
return "", err
}
if err := tmpfile.Close(); err != nil {
return "", err
}
return tmpfile.Name(), nil
}
// TestCutHtml tests the CutHtml function
func TestCutHtml(t *testing.T) {
// Create temporary directory
tempDir, err := ioutil.TempDir("", "testcuthtml")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tempDir)
// Create a temporary source HTML file
sourceHTML := `<div><p class="content">Original content</p></div>`
sourcePath := filepath.Join(tempDir, "source.html")
if err := ioutil.WriteFile(sourcePath, []byte(sourceHTML), 0644); err != nil {
t.Fatalf("Failed to write source HTML file: %v", err)
}
// Create a SnippetsSpecification in YAML format
spec := types.SnippetsSpecification{
Snippets: []types.Snippet{
{
Source: sourcePath,
Destination: sourcePath,
Replacement: []types.ContentReplacement{
{
Selector: ".content",
Content: `<p class="content">Replaced content</p>`,
},
},
},
},
}
specBytes, err := yaml.Marshal(spec)
if err != nil {
t.Fatalf("Failed to marshal YAML: %v", err)
}
// Write the YAML to a temporary file
specPath := filepath.Join(tempDir, "spec.yaml")
if err := ioutil.WriteFile(specPath, specBytes, 0644); err != nil {
t.Fatalf("Failed to write spec YAML file: %v", err)
}
// Run the CutHtml function
if err := CutHtml(specPath); err != nil {
t.Fatalf("CutHtml failed: %v", err)
}
// Verify the result
modifiedHTML, err := ioutil.ReadFile(sourcePath)
if err != nil {
t.Fatalf("Failed to read modified HTML file: %v", err)
}
expectedHTML := `<div><p class="content">Replaced content</p></div>`
if string(modifiedHTML) != expectedHTML {
t.Errorf("Expected HTML to be '%v', but got '%v'", expectedHTML, string(modifiedHTML))
}
}
// TestSetAttributes tests the setAttributes function
func TestSetAttributes(t *testing.T) {
// Example HTML node
rawHTML := `<div><p class="old-class">Hello</p></div>`
node, _ := html.Parse(strings.NewReader(rawHTML))
attrs := []types.Attributes{{Selector: "p", Name: "class", Value: "new-class"}}
// Perform the attribute setting
err := setAttributes(node, attrs)
if err != nil {
t.Errorf("setAttributes failed: %v", err)
}
// Check if the attribute was set correctly
query, _ := cascadia.Compile("p")
pNode := query.MatchFirst(node)
if pNode == nil {
t.Errorf("p node not found")
} else if pNode.Attr[0].Val != "new-class" {
t.Errorf("Attribute not set correctly, got: %s, want: new-class", pNode.Attr[0].Val)
}
}
// TestRemoveAttribute tests the removeAttribute function
func TestRemoveAttribute(t *testing.T) {
// Example attributes
attrs := []html.Attribute{{Key: "class", Val: "old-class"}, {Key: "id", Val: "test-id"}}
// Remove the 'class' attribute
updatedAttrs := removeAttribute(attrs, "class")
// Check if the 'class' attribute is removed
for _, attr := range updatedAttrs {
if attr.Key == "class" {
t.Errorf("Attribute 'class' was not removed")
}
}
}
func TestReplaceNodes(t *testing.T) {
// Example HTML node
rawHTML := `<div><p class="target">Old Content</p><p class="untouched">Don't touch this</p></div>`
node, _ := html.Parse(strings.NewReader(rawHTML))
replacements := []types.ContentReplacement{
{Selector: ".target", Content: "<p>New Content</p>"},
}
// Perform the replacement
replacedNode, err := replaceNodes(node, replacements)
if err != nil {
t.Fatalf("replaceNodes failed: %v", err)
}
// Convert the node back to HTML for easy verification
var buf bytes.Buffer
html.Render(&buf, replacedNode)
replacedHTML := buf.String()
// Expected HTML after replacement
expectedHTML := `<html><head></head><body><div><p>New Content</p><p class="untouched">Don't touch this</p></div></body></html>`
// Verify the replacement
if replacedHTML != expectedHTML {
t.Errorf("Expected HTML to be '%v', but got '%v'", expectedHTML, replacedHTML)
}
}
......@@ -3,6 +3,7 @@ package html
import (
"encoding/json"
"github.com/andybalholm/cascadia"
"github.com/charmbracelet/log"
"gitlab.schukai.com/oss/bob/types"
"gitlab.schukai.com/oss/libraries/go/markup/html/engine"
"golang.org/x/net/html"
......@@ -28,8 +29,10 @@ func GenerateFiles(dataPath, templates, out string) error {
for name, page := range storage {
p := path.Join(templatesDir, name)
log.Info("Generating with template " + p)
generatedHtml, err := Generate(page, p)
if err != nil {
log.Error("Error " + err.Error() + " while generating " + p)
return err
}
......@@ -37,11 +40,15 @@ func GenerateFiles(dataPath, templates, out string) error {
dir := path.Dir(outFile)
if err := os.MkdirAll(dir, 0755); err != nil {
log.Error("Error " + err.Error() + " while creating directory " + dir)
return err
}
log.Info("Writing " + outFile)
err = os.WriteFile(outFile, []byte(generatedHtml), 0644)
if err != nil {
log.Error("Error " + err.Error() + " while writing " + outFile)
return err
}
......@@ -55,10 +62,17 @@ func Generate(data *types.PageData, name string) (string, error) {
dataset := make(map[any]any)
dataset["lang"] = data.Lang
dataset["title"] = data.Title
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{
......@@ -112,11 +126,19 @@ func Generate(data *types.PageData, name string) (string, error) {
func doModifications(page *types.PageData, from string) (string, error) {
log.Info("Modifications for " + page.Export)
node, err := html.Parse(strings.NewReader(from))
if err != nil {
return "", err
}
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)
......@@ -129,7 +151,12 @@ func doModifications(page *types.PageData, from string) (string, error) {
n.Parent.RemoveChild(n)
}
}
}
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 {
......@@ -153,6 +180,12 @@ func doModifications(page *types.PageData, from string) (string, error) {
}
}
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 {
......@@ -170,6 +203,8 @@ func doModifications(page *types.PageData, from string) (string, error) {
Val: m.Value,
})
log.Info("Set attribute " + m.Name + " to " + m.Value + " for " + page.Export)
n.Attr = attr
}
}
......
package template
import (
"bytes"
"encoding/json"
"fmt"
"github.com/andybalholm/cascadia"
"github.com/charmbracelet/log"
"gitlab.schukai.com/oss/bob/constants"
"gitlab.schukai.com/oss/bob/types"
"gitlab.schukai.com/oss/bob/util"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
"path"
"regexp"
"strings"
)
......@@ -32,11 +35,6 @@ func removeAttribute(attrs []html.Attribute, key string) []html.Attribute {
return result
}
//func setDataAttribute(node *html.Node, name, attribute, instruction string) {
// node.Attr = removeAttribute(node.Attr, name)
// node.Attr = append(node.Attr, html.Attribute{Key: name, Val: attribute + " " + instruction})
//}
func setDataAttributesAttribute(node *html.Node, name, attribute, instruction string) {
value := util.GetAttribute(node.Attr, attributeAttributes)
......@@ -164,27 +162,65 @@ func prepareAnchors(node *html.Node, storage *types.PageData) {
return
}
copyOfAnchors := make([]types.Anchor, len(storage.Anchors))
copy(copyOfAnchors, storage.Anchors)
storage.Anchors = []types.Anchor{}
for _, n := range list {
logContent := buildLogContent(n, "")
log.Info("Found a node: " + logContent)
title := util.GetAttribute(n.Attr, "title")
hreflang := util.GetAttribute(n.Attr, "hreflang")
href := util.GetAttribute(n.Attr, "href")
id := util.GetOrCreateReference(n, title+hreflang+href)
k := title + hreflang + href
if k == "" {
log.Warn("The anchor '" + logContent + "' has no title, hreflang and href attribute! Will use the content of the anchor as key.")
k = logContent
}
id := util.GetOrCreateReference(n, k)
if id == "" {
id, _ = util.RandomString(8)
n.Attr = removeAttribute(n.Attr, constants.DataBobReferenceAttributeKey)
n.Attr = append(n.Attr, html.Attribute{Key: constants.DataBobReferenceAttributeKey, Val: id})
id = util.GetNextId()
}
// check if id is already in use
found := false
sameContent := false
for _, t := range storage.Anchors {
if t.Id == id {
// check if the content is the same
if t.Title == title && t.HrefLang == hreflang && t.Href == href {
sameContent = true
continue
}
found = true
break
}
}
if sameContent {
log.Info("The ID " + id + " for the anchor '" + logContent + "' is already in use and the content is the same! Will use this ID for the anchor.")
} else if found {
originalId := id
id = util.GetNextId()
log.Warn("We want to use the ID " + originalId + " for the anchor '" + logContent + "', but this ID is already in use! " +
"This could be a problem if you want to use the anchor in a translation. " +
"Please change the anchor or add the attribute '" + constants.DataBobReferenceAttributeKey + "' to the tag. " +
"Will use now use ID: " + id + " instead.")
}
setDataAttributesAttribute(n, attributeAttributes, "href", "path:anchors."+id+".href")
setDataAttributesAttribute(n, attributeAttributes, "title", "path:anchors."+id+".title")
setDataAttributesAttribute(n, attributeAttributes, "hreflang", "path:anchors."+id+".hreflang")
n.Attr = removeAttribute(n.Attr, constants.DataBobReferenceAttributeKey)
n.Attr = append(n.Attr, html.Attribute{Key: constants.DataBobReferenceAttributeKey, Val: id})
if sameContent {
continue
}
log.Info("Added anchor: " + id + " to storage")
storage.Anchors = append(storage.Anchors, types.Anchor{
Id: id,
Title: title,
......@@ -194,20 +230,6 @@ func prepareAnchors(node *html.Node, storage *types.PageData) {
}
for _, anchor := range copyOfAnchors {
foundIndex := -1
for index, i := range storage.Images {
if i.Id == anchor.Id {
foundIndex = index
break
}
}
if foundIndex > -1 {
storage.Anchors[foundIndex] = anchor
}
}
}
func prepareImages(node *html.Node, storage *types.PageData) {
......@@ -222,27 +244,57 @@ func prepareImages(node *html.Node, storage *types.PageData) {
return
}
copyOfImages := make([]types.Image, len(storage.Images))
copy(copyOfImages, storage.Images)
storage.Images = []types.Image{}
for _, n := range list {
logContent := buildLogContent(n, "")
log.Info("Found img node: " + logContent)
alt := util.GetAttribute(n.Attr, "alt")
title := util.GetAttribute(n.Attr, "title")
source := util.GetAttribute(n.Attr, "src")
id := util.GetOrCreateReference(n, alt+title+source)
if id == "" {
id, _ = util.RandomString(8)
n.Attr = removeAttribute(n.Attr, constants.DataBobReferenceAttributeKey)
n.Attr = append(n.Attr, html.Attribute{Key: constants.DataBobReferenceAttributeKey, Val: id})
id = util.GetNextId()
}
// check if id is already in use
found := false
sameContent := false
for _, t := range storage.Images {
if t.Id == id {
// check if the content is the same
if t.Alt == alt && t.Title == title && t.Source == source {
sameContent = true
continue
}
found = true
break
}
}
if sameContent {
log.Info("The ID " + id + " for the image '" + logContent + "' is already in use and the content is the same! Will use this ID for the image.")
} else if found {
originalId := id
id = util.GetNextId()
log.Warn("We want to use the ID " + originalId + " for the image '" + logContent + "', but this ID is already in use! " +
"This could be a problem if you want to use the image in a translation. " +
"Please change the image or add the attribute '" + constants.DataBobReferenceAttributeKey + "' to the tag. " +
"Will use now use ID: " + id + " instead.")
}
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")
setDataAttributesAttribute(n, attributeAttributes, "alt", "path:content."+id+".alt")
setDataAttributesAttribute(n, attributeAttributes, "title", "path:content."+id+".title")
if sameContent {
continue
}
storage.Images = append(storage.Images, types.Image{
Id: id,
Alt: alt,
......@@ -252,24 +304,10 @@ func prepareImages(node *html.Node, storage *types.PageData) {
}
for _, image := range copyOfImages {
foundIndex := -1
for index, i := range storage.Images {
if i.Id == image.Id {
foundIndex = index
break
}
}
if foundIndex > -1 {
storage.Images[foundIndex] = image
}
}
}
func prepareTranslationJson(node *html.Node, storage *types.PageData) {
selector, err := cascadia.Parse("script[data-monster-role=translation]")
selector, err := cascadia.Parse("script[data-monster-role=translations]")
if err != nil {
return
}
......@@ -279,14 +317,29 @@ func prepareTranslationJson(node *html.Node, storage *types.PageData) {
return
}
copyOfTranslations := make([]types.Translations, len(storage.Translations))
copy(copyOfTranslations, storage.Translations)
storage.Translations = []types.Translations{}
for _, n := range list {
id := util.GetAttribute(n.Attr, constants.DataBobReferenceAttributeKey)
typ := util.GetAttribute(n.Attr, "type")
if id == "" {
id = util.GetNextId()
}
log.Info("Found json translation: " + id)
found := false
for _, t := range storage.Translations {
if t.Id == id {
found = true
break
}
}
if found {
log.Info("The ID " + id + " for the json translation is already in use! If this is not intended, please change the ID in the HTML file. Will use ID: " + id)
continue
}
typ_ := util.GetAttribute(n.Attr, "type")
n.Attr = removeAttribute(n.Attr, attributeReplace)
n.Attr = append(n.Attr, html.Attribute{Key: attributeReplace, Val: "path:translations." + id + ".content"})
......@@ -306,28 +359,15 @@ func prepareTranslationJson(node *html.Node, storage *types.PageData) {
fmt.Println(err)
}
log.Info("Added json translation: " + id + " (" + typ_ + ") to storage")
storage.Translations = append(storage.Translations, types.Translations{
Id: id,
Type: typ,
Type: typ_,
KeyValues: t,
})
}
for _, translation := range copyOfTranslations {
foundIndex := -1
for index, t := range storage.Translations {
if t.Id == translation.Id {
foundIndex = index
break
}
}
if foundIndex > -1 {
storage.Translations[foundIndex] = translation
}
}
}
func prepareTextNodes(node *html.Node, storage *types.PageData) {
......@@ -342,26 +382,8 @@ func prepareTextNodes(node *html.Node, storage *types.PageData) {
return
}
copyOfTextNodes := make([]types.Text, len(storage.Text))
copy(copyOfTextNodes, storage.Text)
storage.Text = []types.Text{}
runNodes(body, storage)
for _, text := range copyOfTextNodes {
foundIndex := -1
for index, t := range storage.Text {
if t.Id == text.Id {
foundIndex = index
break
}
}
if foundIndex > -1 {
storage.Text[foundIndex] = text
}
}
}
func runNodes(n *html.Node, storage *types.PageData) {
......@@ -378,15 +400,67 @@ func runNodes(n *html.Node, storage *types.PageData) {
}
func renderNode(n *html.Node) string {
var buf bytes.Buffer
html.Render(&buf, n)
return buf.String()
}
func removeDuplicateWhitespaces(str string) string {
re := regexp.MustCompile(`\s+`)
return re.ReplaceAllString(str, " ")
}
func handleTextNode(n *html.Node, storage *types.PageData) {
content := strings.TrimSpace(n.Data)
if content == "" {
return
}
logContent := buildLogContent(n.Parent, content)
log.Info("Found text node: " + logContent)
if n.Parent != nil {
if n.Parent.Type == html.ElementNode {
if util.HasAttribute(n.Parent.Attr, "data-monster-head") {
return
}
}
}
id, err := util.BuildTextKey(content)
if err != nil || id == "" {
id = util.GetNextId()
log.Warn("Could not build text key for: '" + logContent + "', using ID: " + id + " instead.")
} else {
log.Info("Create text key: " + id)
}
// check if id is already in use
found := false
sameContent := false
for _, t := range storage.Text {
if t.Id == id {
// check if the content is the same
if t.Text == content {
sameContent = true
continue
}
found = true
break
}
}
if sameContent {
log.Info("The ID " + id + " for the text '" + logContent + "' is already in use and the content is the same! Will use this ID for the text.")
} else if found {
originalId := id
id = util.GetNextId()
log.Warn("We want to use the ID " + originalId + " for the text '" + logContent +
"', but this ID is already in use! " +
"This could be a problem if you want to use the text in a translation. " +
"Please change the text or the ID in the HTML file. " +
"Will use now use ID: " + id + " instead.")
}
parent := n.Parent
......@@ -404,17 +478,42 @@ func handleTextNode(n *html.Node, storage *types.PageData) {
},
},
}
parent.InsertBefore(span, n)
parent.RemoveChild(n)
span.AppendChild(n)
if sameContent {
return
}
log.Info("Added text: " + id + " (" + logContent + ") to storage")
storage.Text = append(storage.Text, types.Text{
Id: id,
Text: content,
})
}
func buildLogContent(n *html.Node, content string) string {
logContent := ""
if len(content) == 0 {
logContent = "Struct: " + renderNode(n)
} else if len(content) < 10 {
logContent = "Parent struct for (" + content + "): " + renderNode(n)
} else {
logContent = content
}
if len(logContent) > 100 {
logContent = logContent[:100] + "..."
}
logContent = removeDuplicateWhitespaces(logContent)
return logContent
}
func checkNodes(n *html.Node, storage *types.PageData) {
if n.Parent != nil {
if n.Parent.Type == html.ElementNode {
......@@ -450,30 +549,57 @@ func checkMonsterDatatableHead(n *html.Node, storage *types.PageData) {
for _, div := range list {
head := util.GetAttribute(div.Attr, "data-monster-head")
logContent := buildLogContent(div, head)
id := util.GetAttribute(div.Attr, constants.DataBobReferenceAttributeKey)
id := util.GetOrCreateReference(div, head)
if id == "" {
id = util.GetNextId()
log.Warn("Could not build key for: '" + logContent + "', using ID: " + id + " instead.")
} else {
log.Info("Create key: " + id)
}
headID, _ := util.BuildTextKey(head)
if headID == "" {
id = util.GetNextId()
} else {
id = headID
// check if id is already in use
found := false
sameContent := false
for _, t := range storage.Text {
if t.Id == id {
// check if the content is the same
if t.Text == head {
sameContent = true
continue
}
found = true
break
}
div.Attr = append(div.Attr, html.Attribute{Key: constants.DataBobReferenceAttributeKey, Val: id})
}
if sameContent {
log.Info("The ID " + id + " for the head '" + logContent + "' is already in use and the content is the same! Will use this ID for the head.")
} else if found {
originalId := id
id = util.GetNextId()
log.Warn("We want to use the ID " + originalId + " for the head '" + logContent +
"', but this ID is already in use! " +
"This could be a problem if you want to use the head in a translation. " +
"Please change the head or add the attribute '" + constants.DataBobReferenceAttributeKey + "' to the tag. " +
"Will use now use ID: " + id + " instead.")
}
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"})
if sameContent {
continue
}
log.Info("Added head: " + id + " (" + logContent + ") to storage")
storage.Text = append(storage.Text, types.Text{
Id: id,
Text: head,
})
}
}
func PrepareHtmlFile(from, to string, storage types.PageDataStorage) (string, error) {
......
......@@ -68,3 +68,20 @@ func NewPageDataStorage() PageDataStorage {
return storage
}
func (p *PageDataStorage) ResetContent() {
for _, pageData := range *p {
pageData.ResetContent()
}
}
func (p *PageData) ResetContent() {
p.Title = ""
p.Meta = make(map[string]string)
p.Images = make([]Image, 0)
p.Anchors = make([]Anchor, 0)
p.Text = make([]Text, 0)
p.Translations = make([]Translations, 0)
}
......@@ -20,6 +20,16 @@ func LoadHtml(p string) (*html.Node, error) {
}
func HasAttribute(attrs []html.Attribute, key string) bool {
for _, attr := range attrs {
if attr.Key == key {
return true
}
}
return false
}
func GetAttribute(attrs []html.Attribute, key string) string {
for _, attr := range attrs {
if attr.Key == key {
......
......@@ -67,6 +67,10 @@ func BuildTextKey(txt string) (string, error) {
}
txt = strings.TrimSpace(txt)
re := regexp.MustCompile(`([a-z])([A-Z])`)
txt = re.ReplaceAllString(txt, "${1}-${2}")
txt = strings.ToLower(txt)
txt = strings.ReplaceAll(txt, keyDelimiter, tempStringMask)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment