using webp for blogs now
125
scripts/convert-blog
Executable file
@ -0,0 +1,125 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import gzip
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import argparse
|
||||||
|
import tempfile
|
||||||
|
import subprocess
|
||||||
|
from datetime import date
|
||||||
|
from lxml import etree as ET
|
||||||
|
|
||||||
|
def open_blog(path: str) -> ET._Element:
|
||||||
|
with gzip.open(path, 'rt', encoding='utf-8') as f:
|
||||||
|
raw_svg = f.read()
|
||||||
|
|
||||||
|
return ET.fromstring(raw_svg)
|
||||||
|
|
||||||
|
def remove_by_xpath(el: ET._Element, xpath: str):
|
||||||
|
for node in el.xpath(xpath):
|
||||||
|
parent = node.getparent()
|
||||||
|
parent.remove(node)
|
||||||
|
|
||||||
|
def tag_xpath(tag: str):
|
||||||
|
return f".//*[local-name()='{tag}']"
|
||||||
|
|
||||||
|
def class_xpath(class_: str):
|
||||||
|
return f".//*[contains(concat(' ', normalize-space(@class), ' '), '{class_}')]"
|
||||||
|
|
||||||
|
def save_blog(dir: str, root: ET._Element):
|
||||||
|
os.makedirs(dir, exist_ok=True)
|
||||||
|
|
||||||
|
frame: ET._Element = root.xpath(tag_xpath('svg'))[0]
|
||||||
|
width = int(frame.attrib['width'][:-2])
|
||||||
|
height = int(frame.attrib['height'][:-2])
|
||||||
|
root.attrib['viewBox'] = f'0 0 {width} {height}'
|
||||||
|
|
||||||
|
content: ET._Element = frame.getchildren()[0]
|
||||||
|
root.remove(frame)
|
||||||
|
root.append(content)
|
||||||
|
|
||||||
|
remove_by_xpath(root, ".//*[@id='write-document']")
|
||||||
|
remove_by_xpath(root, ".//*[@id='write-defs']")
|
||||||
|
remove_by_xpath(root, ".//*[@id='write-doc-background']")
|
||||||
|
remove_by_xpath(root, class_xpath('ruleline'))
|
||||||
|
|
||||||
|
bookmarks: list[tuple[str, float]] = []
|
||||||
|
for node in root.xpath(class_xpath('bookmark')):
|
||||||
|
match = re.search(r"translate\([^\d\-]*([-+]?\d*\.?\d+),\s*([-+]?\d*\.?\d+)\)", node.attrib['transform'])
|
||||||
|
bookmarks.append((node.attrib['id'], float(match.group(2))))
|
||||||
|
node.getparent().remove(node)
|
||||||
|
|
||||||
|
hyperrefs: list[tuple[ET._Element, str, tuple[float, float], tuple[float, float]]] = []
|
||||||
|
for node in root.xpath(class_xpath('hyperref')):
|
||||||
|
anchor = node.xpath(tag_xpath('a'))[0]
|
||||||
|
rect = anchor.xpath(tag_xpath('rect'))[0]
|
||||||
|
x, y = float(rect.attrib['x']), float(rect.attrib['y'])
|
||||||
|
w, h = float(rect.attrib['width']), float(rect.attrib['height'])
|
||||||
|
url = anchor.attrib['{http://www.w3.org/1999/xlink}href']
|
||||||
|
hyperrefs.append((node, url, (x, y), (w, h)))
|
||||||
|
anchor.getparent().remove(anchor)
|
||||||
|
node.getparent().remove(node)
|
||||||
|
|
||||||
|
tempdir = tempfile.mkdtemp()
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(f'{tempdir}/main.svg', 'wb') as f:
|
||||||
|
f.write(ET.tostring(root, xml_declaration=True, encoding="utf-8"))
|
||||||
|
|
||||||
|
subprocess.call(['magick', '-density', '300', '-background', 'none', f'{tempdir}/main.svg', f'{tempdir}/main.png'])
|
||||||
|
subprocess.call(['magick', f'{tempdir}/main.png', '-crop', 'x16383', '+repage', f'{tempdir}/main_%03d.webp'])
|
||||||
|
|
||||||
|
main_files: list[str] = []
|
||||||
|
for file in os.listdir(tempdir):
|
||||||
|
if file.endswith('webp'): main_files.append(file)
|
||||||
|
|
||||||
|
url_defs: list[tuple[str, str, tuple[float, float], tuple[float, float]]] = []
|
||||||
|
for node, url, (x, y), (w, h) in hyperrefs:
|
||||||
|
name = os.urandom(15).hex()[:7]
|
||||||
|
|
||||||
|
svg = ET.Element('svg', attrib={'viewBox': f'{x} {y} {w} {h}'}, nsmap={None: 'http://www.w3.org/2000/svg'})
|
||||||
|
svg.append(node)
|
||||||
|
|
||||||
|
with open(f'{tempdir}/{name}.svg', 'wb') as f:
|
||||||
|
f.write(ET.tostring(svg, xml_declaration=True, encoding="utf-8"))
|
||||||
|
|
||||||
|
subprocess.call(['magick', '-density', '300', '-background', 'none', f'{tempdir}/{name}.svg',f'{tempdir}/{name}.png'])
|
||||||
|
subprocess.call(['magick', f'{tempdir}/{name}.png', f'{tempdir}/{name}.webp'])
|
||||||
|
|
||||||
|
url_defs.append((f'{name}.webp', url, (x, y), (w, h)))
|
||||||
|
|
||||||
|
|
||||||
|
for file in os.listdir(tempdir):
|
||||||
|
if file.endswith('webp'):
|
||||||
|
shutil.move(f'{tempdir}/{file}', f'{dir}/{file}')
|
||||||
|
|
||||||
|
with open(f'{dir}/_definition.json', 'w') as f:
|
||||||
|
json.dump({
|
||||||
|
'date': date.today().strftime("%Y-%m-%d"),
|
||||||
|
'title': 'My Cool Blog Entry',
|
||||||
|
'keywords': ['Cool', 'Blog'],
|
||||||
|
'bookmarks': list(map(lambda b: {'id': b[0], 'offset': b[1]}, bookmarks)),
|
||||||
|
'urls': list(map(lambda d: {'src': d[0], 'href': d[1], 'offset': d[2], 'dimensions': d[3]}, url_defs)),
|
||||||
|
'files': sorted(main_files),
|
||||||
|
'dimensions': [width, height],
|
||||||
|
}, f, indent=4)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(tempdir, ignore_errors=True)
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument('--svgz', required=True, type=str)
|
||||||
|
parser.add_argument('--dir', required=True, type=str)
|
||||||
|
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
save_blog(args.dir, open_blog(args.svgz))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -31,7 +31,7 @@ RewriteRule ^(typo|admin|login|dashboard).*$ /dummy/kaffee.php [L]
|
|||||||
# Rewrite /lib/* or /blog/* to /error.php?code=404
|
# Rewrite /lib/* or /blog/* to /error.php?code=404
|
||||||
# with 404 response code (internal rewriting)
|
# with 404 response code (internal rewriting)
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
RewriteCond %{REQUEST_URI} ^/(lib|blog)(/.*)?$
|
RewriteCond %{REQUEST_URI} ^/(lib)(/.*)?$
|
||||||
RewriteRule ^ - [R=404,L]
|
RewriteRule ^ - [R=404,L]
|
||||||
ErrorDocument 404 /error.php?code=404
|
ErrorDocument 404 /error.php?code=404
|
||||||
|
|
||||||
|
102
www/blog.php
@ -2,22 +2,25 @@
|
|||||||
|
|
||||||
require_once __DIR__ . '/lib/_index.php';
|
require_once __DIR__ . '/lib/_index.php';
|
||||||
|
|
||||||
$id = (int) $_GET['id'];
|
$id = $_GET['id'];
|
||||||
|
|
||||||
$blog = Blog::get($id);
|
$blog = Blog::get($id);
|
||||||
if ($blog === null) {
|
if ($blog === null) {
|
||||||
http_response_code(404);
|
$_GET['code'] = 404;
|
||||||
require __DIR__ . '/404.php';
|
require __DIR__ . '/error.php';
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$svgs = $blog->getSVGs();
|
|
||||||
|
|
||||||
$rand_imprint = mt_rand() % 6;
|
$rand_imprint = mt_rand() % 6;
|
||||||
$rand_privacy = mt_rand() % 6;
|
$rand_privacy = mt_rand() % 6;
|
||||||
$rand_comments = mt_rand() % 6;
|
$rand_comments = mt_rand() % 6;
|
||||||
$rand_send_comment = mt_rand() % 5;
|
$rand_send_comment = mt_rand() % 5;
|
||||||
|
|
||||||
|
function is_external_url(string $url): bool
|
||||||
|
{
|
||||||
|
return !str_starts_with($url, '#');
|
||||||
|
}
|
||||||
|
|
||||||
?><!DOCTYPE html>
|
?><!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
@ -27,11 +30,9 @@ $rand_send_comment = mt_rand() % 5;
|
|||||||
|
|
||||||
<link rel="shortcut icon" href="/favicon.svg" type="image/x-icon">
|
<link rel="shortcut icon" href="/favicon.svg" type="image/x-icon">
|
||||||
|
|
||||||
<base target="_blank">
|
<meta name="keywords" content="ludwig lehnert,blog,<?= join(",", $blog->keywords()) ?>">
|
||||||
|
|
||||||
<meta name="keywords" content="ludwig lehnert,blog,<?= join(",", $blog->keywords) ?>">
|
<title><?= $blog->title() ?></title>
|
||||||
|
|
||||||
<title><?= $blog->title ?></title>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
@ -40,6 +41,11 @@ $rand_send_comment = mt_rand() % 5;
|
|||||||
background: white;
|
background: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: min(100%, 800px);
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -47,22 +53,47 @@ $rand_send_comment = mt_rand() % 5;
|
|||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
display: flex;
|
position: relative;
|
||||||
flex-direction: column;
|
width: 100%;
|
||||||
align-items: center;
|
aspect-ratio:
|
||||||
|
<?= $blog->width() ?>
|
||||||
|
/
|
||||||
|
<?= $blog->height() ?>
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
main>* {
|
.content img {
|
||||||
display: block;
|
display: block;
|
||||||
width: min(100vw, 1000px);
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg .hyperref {
|
.bookmarks {
|
||||||
cursor: pointer;
|
z-index: -1;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg .hyperref:hover {
|
.bookmarks * {
|
||||||
|
position: absolute;
|
||||||
|
height: 10px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.urls {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.urls a {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.urls a:hover {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,12 +110,12 @@ $rand_send_comment = mt_rand() % 5;
|
|||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-links {
|
.legal-links {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-links svg {
|
.legal-links svg {
|
||||||
max-height: 4rem;
|
max-height: 4rem;
|
||||||
height: 10vw;
|
height: 10vw;
|
||||||
min-height: 2rem;
|
min-height: 2rem;
|
||||||
@ -96,24 +127,47 @@ $rand_send_comment = mt_rand() % 5;
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<?php foreach ($svgs as $svg): ?>
|
<div class="content">
|
||||||
<?= $svg ?>
|
<?php foreach ($blog->files() as $file): ?>
|
||||||
|
<img src="/blog/<?= $blog->id ?>/<?= $file ?>">
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bookmarks">
|
||||||
|
<?php foreach ($blog->bookmarks() as $bm): ?>
|
||||||
|
<div id="<?= $bm['id'] ?>" style="top: <?= 100 * $bm['offset'] / $blog->height() ?>%;"></div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="urls">
|
||||||
|
<?php foreach ($blog->urls() as $url): ?>
|
||||||
|
<a href="<?= $url['href'] ?>" <?php if (is_external_url($url['href']))
|
||||||
|
echo 'target="_blank"'; ?> style="
|
||||||
|
top: <?= 100 * $url['offset'][1] / $blog->height() ?>%;
|
||||||
|
left: <?= 100 * $url['offset'][0] / $blog->width() ?>%;
|
||||||
|
width: <?= 100 * $url['dimensions'][0] / $blog->width() ?>%;">
|
||||||
|
<img src="/blog/<?= $blog->id ?>/<?= $url['src'] ?>" style="width: 100%;">
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div style="height: 100px"></div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
<div class="send-comment">
|
<div class="send-comment">
|
||||||
<?= get_svg(__DIR__ . "/assets/blog/send-comment$rand_send_comment.svg") ?>
|
<?= get_svg(__DIR__ . "/assets/blog/send-comment$rand_send_comment.svg") ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="height: 100px"></div>
|
<div style="height: 100px"></div>
|
||||||
|
|
||||||
<div class="bottom-links">
|
<div class="legal-links">
|
||||||
<?= get_svg(__DIR__ . "/assets/blog/imprint$rand_imprint.svg") ?>
|
<?= get_svg(__DIR__ . "/assets/blog/imprint$rand_imprint.svg") ?>
|
||||||
<?= get_svg(__DIR__ . "/assets/blog/privacy$rand_privacy.svg") ?>
|
<?= get_svg(__DIR__ . "/assets/blog/privacy$rand_privacy.svg") ?>
|
||||||
</div>
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
<div style="height: 100px"></div>
|
<div style="height: 100px"></div>
|
||||||
</main>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "Test: First Blog Entry",
|
|
||||||
"keywords": [
|
|
||||||
"test",
|
|
||||||
"first blog entry"
|
|
||||||
],
|
|
||||||
"date": "2025-04-15",
|
|
||||||
"files": [
|
|
||||||
"0.svg"
|
|
||||||
]
|
|
||||||
}
|
|
Before Width: | Height: | Size: 1.1 MiB |
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "Thoughts about edge computation",
|
|
||||||
"keywords": [
|
|
||||||
"edge",
|
|
||||||
"edge computation",
|
|
||||||
"thoughts about edge computation"
|
|
||||||
],
|
|
||||||
"date": "2025-04-17",
|
|
||||||
"files": [
|
|
||||||
"1.svg"
|
|
||||||
]
|
|
||||||
}
|
|
Before Width: | Height: | Size: 9.3 MiB |
BIN
www/blog/one/07a08c4.webp
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
www/blog/one/09eef40.webp
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
www/blog/one/0aea930.webp
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
www/blog/one/15704c1.webp
Normal file
After Width: | Height: | Size: 678 B |
BIN
www/blog/one/17e65d5.webp
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
www/blog/one/1cc4eb8.webp
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
www/blog/one/2618a28.webp
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
www/blog/one/2f7fe30.webp
Normal file
After Width: | Height: | Size: 608 B |
BIN
www/blog/one/30d5de3.webp
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
www/blog/one/35ca008.webp
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
www/blog/one/4d55914.webp
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
www/blog/one/4e13630.webp
Normal file
After Width: | Height: | Size: 598 B |
BIN
www/blog/one/53145df.webp
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
www/blog/one/590f333.webp
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
www/blog/one/5ad1430.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
www/blog/one/64a799d.webp
Normal file
After Width: | Height: | Size: 606 B |
BIN
www/blog/one/717c078.webp
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
www/blog/one/7e7a7e4.webp
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
www/blog/one/908a399.webp
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
www/blog/one/9c229c9.webp
Normal file
After Width: | Height: | Size: 3.8 KiB |
417
www/blog/one/_definition.json
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
{
|
||||||
|
"date": "2025-04-17",
|
||||||
|
"title": "Thoughts about edge computation",
|
||||||
|
"keywords": [
|
||||||
|
"edge",
|
||||||
|
"edge computation",
|
||||||
|
"thoughts about"
|
||||||
|
],
|
||||||
|
"bookmarks": [
|
||||||
|
{
|
||||||
|
"id": "b-d5I21c9U7J",
|
||||||
|
"offset": 1914.461
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-fLRN0SS32U",
|
||||||
|
"offset": 4834.461
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-sXQ0BxXCpj",
|
||||||
|
"offset": 5754.461
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-yFt7MQSghl",
|
||||||
|
"offset": 6014.461
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-yR35Qi4Msf",
|
||||||
|
"offset": 6774.461
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-qxKx5OdrIT",
|
||||||
|
"offset": 7954.461
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"urls": [
|
||||||
|
{
|
||||||
|
"src": "5ad1430.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/Edge_computing",
|
||||||
|
"offset": [
|
||||||
|
291.575,
|
||||||
|
369.178
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
22.265,
|
||||||
|
17.722
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "908a399.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/Time_to_live",
|
||||||
|
"offset": [
|
||||||
|
163.033,
|
||||||
|
1462.429
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
49.831,
|
||||||
|
17.907
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "b285770.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/Active_users",
|
||||||
|
"offset": [
|
||||||
|
20.623,
|
||||||
|
1579.52
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
42.045,
|
||||||
|
18.93
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "9c229c9.webp",
|
||||||
|
"href": "#b-d5I21c9U7J",
|
||||||
|
"offset": [
|
||||||
|
323.993,
|
||||||
|
2981.505
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
75.638,
|
||||||
|
30.079
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "717c078.webp",
|
||||||
|
"href": "https://cloud.google.com/products/calculator",
|
||||||
|
"offset": [
|
||||||
|
180.869,
|
||||||
|
3060.241
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
184.16,
|
||||||
|
28.221
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "b55185f.webp",
|
||||||
|
"href": "https://cloud.google.com/run/pricing#pricing_for_regions_in_tier_1",
|
||||||
|
"offset": [
|
||||||
|
302.069,
|
||||||
|
3384.668
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
40.145,
|
||||||
|
12.978
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "c5aea0a.webp",
|
||||||
|
"href": "https://cloud.google.com/run/pricing#pricing_for_regions_in_tier_2",
|
||||||
|
"offset": [
|
||||||
|
394.121,
|
||||||
|
3406.002
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
45.343,
|
||||||
|
13.91
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "fe86e6f.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/Virtual_private_server",
|
||||||
|
"offset": [
|
||||||
|
65.769,
|
||||||
|
3762.201
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
33.444,
|
||||||
|
18.997
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "53145df.webp",
|
||||||
|
"href": "https://www.hetzner.com/cloud/",
|
||||||
|
"offset": [
|
||||||
|
153.962,
|
||||||
|
3760.823
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
127.856,
|
||||||
|
20.199
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "17e65d5.webp",
|
||||||
|
"href": "#b-fLRN0SS32U",
|
||||||
|
"offset": [
|
||||||
|
328.668,
|
||||||
|
5220.784
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
99.508,
|
||||||
|
30.447
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "a0be263.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/JSON_Web_Token",
|
||||||
|
"offset": [
|
||||||
|
414.148,
|
||||||
|
5500.374
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
39.779,
|
||||||
|
26.594
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "30d5de3.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/Round-trip_delay",
|
||||||
|
"offset": [
|
||||||
|
78.417,
|
||||||
|
6561.832
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
40.835,
|
||||||
|
16.013
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "2f7fe30.webp",
|
||||||
|
"href": "#b-sXQ0BxXCpj",
|
||||||
|
"offset": [
|
||||||
|
132.554,
|
||||||
|
6562.702
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
9.351,
|
||||||
|
14.592
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "4e13630.webp",
|
||||||
|
"href": "#b-yFt7MQSghl",
|
||||||
|
"offset": [
|
||||||
|
227.85,
|
||||||
|
6564.368
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
13.333,
|
||||||
|
9.814
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "c1e1de3.webp",
|
||||||
|
"href": "#b-sXQ0BxXCpj",
|
||||||
|
"offset": [
|
||||||
|
108.014,
|
||||||
|
6621.458
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
10.852,
|
||||||
|
14.101
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "09eef40.webp",
|
||||||
|
"href": "#b-fLRN0SS32U",
|
||||||
|
"offset": [
|
||||||
|
359.605,
|
||||||
|
7101.638
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
88.394,
|
||||||
|
29.214
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "15704c1.webp",
|
||||||
|
"href": "#b-sXQ0BxXCpj",
|
||||||
|
"offset": [
|
||||||
|
397.962,
|
||||||
|
7261.618
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
8.913,
|
||||||
|
18.387
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "f4b2484.webp",
|
||||||
|
"href": "#b-yFt7MQSghl",
|
||||||
|
"offset": [
|
||||||
|
440.001,
|
||||||
|
7261.85
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
14.139,
|
||||||
|
17.629
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "f44e23c.webp",
|
||||||
|
"href": "#b-sXQ0BxXCpj",
|
||||||
|
"offset": [
|
||||||
|
182.096,
|
||||||
|
7583.017
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
10.694,
|
||||||
|
13.19
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "64a799d.webp",
|
||||||
|
"href": "#b-yFt7MQSghl",
|
||||||
|
"offset": [
|
||||||
|
278.685,
|
||||||
|
7586.278
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
14.377,
|
||||||
|
10.387
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "ea08577.webp",
|
||||||
|
"href": "#b-sXQ0BxXCpj",
|
||||||
|
"offset": [
|
||||||
|
109.759,
|
||||||
|
7643.622
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
9.297,
|
||||||
|
13.103
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "0aea930.webp",
|
||||||
|
"href": "#b-fLRN0SS32U",
|
||||||
|
"offset": [
|
||||||
|
123.703,
|
||||||
|
7736.656
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
115.125,
|
||||||
|
34.642
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "7e7a7e4.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/ACID",
|
||||||
|
"offset": [
|
||||||
|
84.38,
|
||||||
|
8640.77
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
48.438,
|
||||||
|
20.131
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "07a08c4.webp",
|
||||||
|
"href": "#b-fLRN0SS32U",
|
||||||
|
"offset": [
|
||||||
|
361.351,
|
||||||
|
8940.184
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
91.42,
|
||||||
|
33.02
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "35ca008.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/NoSQL",
|
||||||
|
"offset": [
|
||||||
|
216.592,
|
||||||
|
9041.812
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
68.499,
|
||||||
|
18.973
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "1cc4eb8.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/Relational_database",
|
||||||
|
"offset": [
|
||||||
|
381.531,
|
||||||
|
8540.439
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
77.199,
|
||||||
|
20.197
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "590f333.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/Graph_database",
|
||||||
|
"offset": [
|
||||||
|
307.123,
|
||||||
|
9079.422
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
51.318,
|
||||||
|
26.761
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "4d55914.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/Content_delivery_network",
|
||||||
|
"offset": [
|
||||||
|
87.833,
|
||||||
|
9358.795
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
250.808,
|
||||||
|
30.198
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "f072857.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/Denial-of-service_attack#Distributed_DoS",
|
||||||
|
"offset": [
|
||||||
|
82.307,
|
||||||
|
9398.894
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
54.456,
|
||||||
|
19.93
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "f63dffe.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/TLS_termination_proxy",
|
||||||
|
"offset": [
|
||||||
|
80.339,
|
||||||
|
9479.985
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
163.634,
|
||||||
|
19.53
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "2618a28.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/Load_balancing_(computing)",
|
||||||
|
"offset": [
|
||||||
|
169.377,
|
||||||
|
9440.959
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
155.393,
|
||||||
|
32.558
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"main_000.webp",
|
||||||
|
"main_001.webp"
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
480,
|
||||||
|
9700
|
||||||
|
]
|
||||||
|
}
|
BIN
www/blog/one/a0be263.webp
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
www/blog/one/b285770.webp
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
www/blog/one/b55185f.webp
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
www/blog/one/c1e1de3.webp
Normal file
After Width: | Height: | Size: 696 B |
BIN
www/blog/one/c5aea0a.webp
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
www/blog/one/ea08577.webp
Normal file
After Width: | Height: | Size: 536 B |
BIN
www/blog/one/f072857.webp
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
www/blog/one/f44e23c.webp
Normal file
After Width: | Height: | Size: 638 B |
BIN
www/blog/one/f4b2484.webp
Normal file
After Width: | Height: | Size: 912 B |
BIN
www/blog/one/f63dffe.webp
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
www/blog/one/fe86e6f.webp
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
www/blog/one/main_000.webp
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
www/blog/one/main_001.webp
Normal file
After Width: | Height: | Size: 963 KiB |
BIN
www/blog/zero/18bddb5.webp
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
www/blog/zero/2db2951.webp
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
www/blog/zero/60d1274.webp
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
www/blog/zero/6a1748c.webp
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
www/blog/zero/76c4bca.webp
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
www/blog/zero/913fe45.webp
Normal file
After Width: | Height: | Size: 11 KiB |
90
www/blog/zero/_definition.json
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"date": "2025-04-15",
|
||||||
|
"title": "Test: First Blog Entry",
|
||||||
|
"keywords": [
|
||||||
|
"test",
|
||||||
|
"first blog entry"
|
||||||
|
],
|
||||||
|
"bookmarks": [],
|
||||||
|
"urls": [
|
||||||
|
{
|
||||||
|
"src": "18bddb5.webp",
|
||||||
|
"href": "https://google.com",
|
||||||
|
"offset": [
|
||||||
|
176.315,
|
||||||
|
378.853
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
42.536,
|
||||||
|
25.653
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "76c4bca.webp",
|
||||||
|
"href": "https://handwritten.blog",
|
||||||
|
"offset": [
|
||||||
|
271.101,
|
||||||
|
511.231
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
142.634,
|
||||||
|
40.839
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "913fe45.webp",
|
||||||
|
"href": "mailto:blog@lehnert.dev",
|
||||||
|
"offset": [
|
||||||
|
60.28,
|
||||||
|
868.451
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
188.266,
|
||||||
|
44.067
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "60d1274.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/SVG",
|
||||||
|
"offset": [
|
||||||
|
454.485,
|
||||||
|
1012.028
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
35.147,
|
||||||
|
20.576
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "6a1748c.webp",
|
||||||
|
"href": "https://en.wikipedia.org/wiki/PNG",
|
||||||
|
"offset": [
|
||||||
|
320.493,
|
||||||
|
1057.862
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
44.903,
|
||||||
|
19.773
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "2db2951.webp",
|
||||||
|
"href": "https://inkscape.org/",
|
||||||
|
"offset": [
|
||||||
|
64.774,
|
||||||
|
1231.579
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
87.11,
|
||||||
|
37.658
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"main_000.webp"
|
||||||
|
],
|
||||||
|
"dimensions": [
|
||||||
|
650,
|
||||||
|
1400
|
||||||
|
]
|
||||||
|
}
|
BIN
www/blog/zero/main_000.webp
Normal file
After Width: | Height: | Size: 344 KiB |
@ -8,6 +8,8 @@ if (isset($_GET['code'])) {
|
|||||||
$code = (int) $_GET['code'];
|
$code = (int) $_GET['code'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
http_response_code($code);
|
||||||
|
|
||||||
$msg = '🪐 Unknown error! Something went wrong in the fabric of spacetime.';
|
$msg = '🪐 Unknown error! Something went wrong in the fabric of spacetime.';
|
||||||
$action = 'Stabilize systems';
|
$action = 'Stabilize systems';
|
||||||
|
|
||||||
|
@ -136,6 +136,10 @@ usort($blogs, function ($a, $b) {
|
|||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.none {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@keyframes glow {
|
@keyframes glow {
|
||||||
from {
|
from {
|
||||||
@ -188,15 +192,20 @@ usort($blogs, function ($a, $b) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="blog-entries">
|
<div class="blog-entries">
|
||||||
|
<h2 class="none">Handwritten Blog</h2>
|
||||||
|
|
||||||
<?php foreach ($blogs as $blog): ?>
|
<?php foreach ($blogs as $blog): ?>
|
||||||
<div class="blog-entry">
|
<div class="blog-entry">
|
||||||
<h2><a href="/blog/<?= $blog->id ?>"><?= $blog->title ?></a></h2>
|
<h2><a href="/blog/<?= $blog->id ?>"><?= $blog->title() ?></a></h2>
|
||||||
<p><?= $blog->formatDate() ?></p>
|
<p><?= $blog->formatDate() ?></p>
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="height: 350px;"></div>
|
<div style="height: 350px;"></div>
|
||||||
|
|
||||||
|
<!-- SEO Tags -->
|
||||||
|
<h1 class="none">Ludwig Lehnert</h1>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
@ -3,41 +3,68 @@
|
|||||||
class Blog
|
class Blog
|
||||||
{
|
{
|
||||||
public readonly string $id;
|
public readonly string $id;
|
||||||
public readonly string $title;
|
public readonly array $json;
|
||||||
public readonly array $keywords;
|
|
||||||
public readonly string $date;
|
|
||||||
public readonly array $files;
|
|
||||||
|
|
||||||
public function __construct(string $id, string $title, array $keywords, string $date, array $files)
|
public function __construct(string $id, array $json)
|
||||||
{
|
{
|
||||||
$this->id = $id;
|
$this->id = $id;
|
||||||
$this->title = $title;
|
$this->json = $json;
|
||||||
$this->keywords = $keywords;
|
}
|
||||||
$this->date = $date;
|
|
||||||
$this->files = $files;
|
public function date(): string
|
||||||
|
{
|
||||||
|
return $this->json['date'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function title(): string
|
||||||
|
{
|
||||||
|
return $this->json['title'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function keywords(): array
|
||||||
|
{
|
||||||
|
return $this->json['keywords'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bookmarks(): array
|
||||||
|
{
|
||||||
|
return $this->json['bookmarks'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function urls(): array
|
||||||
|
{
|
||||||
|
return $this->json['urls'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function files(): array
|
||||||
|
{
|
||||||
|
return $this->json['files'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function width(): float
|
||||||
|
{
|
||||||
|
return $this->json['dimensions'][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function height(): float
|
||||||
|
{
|
||||||
|
return $this->json['dimensions'][1];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function formatDate(): string
|
public function formatDate(): string
|
||||||
{
|
{
|
||||||
return date("F jS, Y", strtotime($this->date));
|
return date("F jS, Y", strtotime($this->date()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSVGs(): array
|
public static function get(string $id): Blog|null
|
||||||
{
|
{
|
||||||
return array_map(function ($file) {
|
$path = __DIR__ . "/../blog/$id/_definition.json";
|
||||||
return get_svg(__DIR__ . '/../blog/' . $file);
|
|
||||||
}, $this->files);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function get(string|int $id): Blog|null
|
|
||||||
{
|
|
||||||
$path = __DIR__ . "/../blog/$id.json";
|
|
||||||
if (!file_exists($path))
|
if (!file_exists($path))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$json = json_decode(file_get_contents($path), true);
|
$json = json_decode(file_get_contents($path), true);
|
||||||
return new Blog($id, $json['title'], $json['keywords'], $json['date'], $json['files']);
|
return new Blog($id, $json);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -48,18 +75,12 @@ class Blog
|
|||||||
*/
|
*/
|
||||||
public static function getAll()
|
public static function getAll()
|
||||||
{
|
{
|
||||||
$files = scandir(__DIR__ . '/../blog');
|
$dirs = scandir(__DIR__ . '/../blog');
|
||||||
$files = array_filter($files, function ($file) {
|
$dirs = array_filter($dirs, fn($dir) => is_dir(__DIR__ . "/../blog/$dir") && $dir !== '.' && $dir !== '..');
|
||||||
return str_ends_with($file, '.json');
|
|
||||||
});
|
|
||||||
|
|
||||||
$ids = array_map(function ($file) {
|
|
||||||
return str_replace('.json', '', $file);
|
|
||||||
}, $files);
|
|
||||||
|
|
||||||
$blogs = [];
|
$blogs = [];
|
||||||
|
|
||||||
foreach ($ids as $id) {
|
foreach ($dirs as $id) {
|
||||||
$blog = Blog::get($id);
|
$blog = Blog::get($id);
|
||||||
if ($blog !== null)
|
if ($blog !== null)
|
||||||
array_push($blogs, $blog);
|
array_push($blogs, $blog);
|
||||||
|
@ -4,6 +4,8 @@ require_once __DIR__ . "/lib/_index.php";
|
|||||||
|
|
||||||
$blogs = Blog::getAll();
|
$blogs = Blog::getAll();
|
||||||
|
|
||||||
|
header("Content-Type: application/xml");
|
||||||
|
|
||||||
echo '<?';
|
echo '<?';
|
||||||
?>xml version="1.0" encoding="UTF-8"?>
|
?>xml version="1.0" encoding="UTF-8"?>
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
@ -18,20 +20,20 @@ echo '<?';
|
|||||||
<?php foreach ($blogs as $blog): ?>
|
<?php foreach ($blogs as $blog): ?>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://lehnert.dev/blog/<?= $blog->id ?></loc>
|
<loc>https://lehnert.dev/blog/<?= $blog->id ?></loc>
|
||||||
<lastmod><?= $blog->date ?></lastmod>
|
<!-- <lastmod><?= $blog->date() ?></lastmod> -->
|
||||||
<priority>0.8</priority>
|
<priority>0.8</priority>
|
||||||
</url>
|
</url>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
||||||
<url>
|
<url>
|
||||||
<loc>https://lehnert.dev/imprint</loc>
|
<loc>https://lehnert.dev/imprint/</loc>
|
||||||
<lastmod>2025-04-16</lastmod>
|
<lastmod>2025-04-16</lastmod>
|
||||||
<changefreq>yearly</changefreq>
|
<changefreq>yearly</changefreq>
|
||||||
<priority>0.5</priority>
|
<priority>0.5</priority>
|
||||||
</url>
|
</url>
|
||||||
|
|
||||||
<url>
|
<url>
|
||||||
<loc>https://lehnert.dev/privacy</loc>
|
<loc>https://lehnert.dev/privacy/</loc>
|
||||||
<lastmod>2025-04-16</lastmod>
|
<lastmod>2025-04-16</lastmod>
|
||||||
<changefreq>yearly</changefreq>
|
<changefreq>yearly</changefreq>
|
||||||
<priority>0.5</priority>
|
<priority>0.5</priority>
|
||||||
|