From c005cbbbd328e4320f4e0ab0b1acdd1802c0576b Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Sun, 8 Dec 2024 09:42:38 +0000 Subject: [PATCH 01/51] bump version number --- CHANGELOG.md | 5 +++++ jinjafx_server.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfc9271..131dfc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## CHANGELOG +### [25.1.0] - In Development +- Updated Pandoc to 3.6 in Dockerfile + ### [24.12.1] - Dec 3, 2024 - Fixed an issue where rows with an incorrect number of fields in `data.csv` weren't being coloured red @@ -351,6 +354,8 @@ ### 21.11.0 - Nov 29, 2021 - Initial release + +[25.1.0]: https://github.com/cmason3/jinjafx_server/compare/24.12.1...25.1.0 [24.12.1]: https://github.com/cmason3/jinjafx_server/compare/24.12.0...24.12.1 [24.12.0]: https://github.com/cmason3/jinjafx_server/compare/24.10.1...24.12.0 [24.10.1]: https://github.com/cmason3/jinjafx_server/compare/24.10.0...24.10.1 diff --git a/jinjafx_server.py b/jinjafx_server.py index b1608a3..542bf46 100755 --- a/jinjafx_server.py +++ b/jinjafx_server.py @@ -28,7 +28,7 @@ import re, argparse, hashlib, traceback, glob, hmac, uuid, struct, binascii, gzip, requests, ctypes, subprocess import cmarkgfm, emoji -__version__ = '24.12.1' +__version__ = '25.1.0' llock = threading.RLock() rlock = threading.RLock() From ad510d0cdc42070c40f97d7e0299d1c8719d6485 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Sun, 8 Dec 2024 09:43:19 +0000 Subject: [PATCH 02/51] bump pandoc version to 3.6 --- kubernetes/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kubernetes/Dockerfile b/kubernetes/Dockerfile index 5af7146..972a9e8 100644 --- a/kubernetes/Dockerfile +++ b/kubernetes/Dockerfile @@ -7,8 +7,8 @@ RUN set -eux; \ apt-get update; \ apt-get install -y --no-install-recommends wget git build-essential; \ -wget -P /tmp https://github.com/jgm/pandoc/releases/download/3.5/pandoc-3.5-1-amd64.deb; \ -dpkg -i /tmp/pandoc-3.5-1-amd64.deb; \ +wget -P /tmp https://github.com/jgm/pandoc/releases/download/3.6/pandoc-3.6-1-amd64.deb; \ +dpkg -i /tmp/pandoc-3.6-1-amd64.deb; \ python3 -m venv /opt/jinjafx; \ /opt/jinjafx/bin/python3 -m pip install --upgrade git+https://github.com/cmason3/jinjafx_server.git@${BRANCH} lxml; \ From ff4b967177023861285b64d3fcd70757fdf8d808 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Sun, 8 Dec 2024 09:58:21 +0000 Subject: [PATCH 03/51] disable mtime changes on access --- CHANGELOG.md | 1 + jinjafx_server.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 131dfc7..29fb268 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## CHANGELOG ### [25.1.0] - In Development +- Don't update modify time of local repository files on access - Updated Pandoc to 3.6 in Dockerfile ### [24.12.1] - Dec 3, 2024 diff --git a/jinjafx_server.py b/jinjafx_server.py index 542bf46..4a13550 100755 --- a/jinjafx_server.py +++ b/jinjafx_server.py @@ -329,7 +329,7 @@ def do_GET(self, head=False, cache=True, versioned=False): r = [ 'application/json', 200, json.dumps({ 'dt': self.e(rr).decode('utf-8') }).encode('utf-8'), sys._getframe().f_lineno ] - os.utime(fpath, None) + # os.utime(fpath, None) else: r = [ 'text/plain', 404, '404 Not Found\r\n'.encode('utf-8'), sys._getframe().f_lineno ] From 0237d46356169aee4b804f7d11d551c358f384fb Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 11 Dec 2024 12:40:39 +0000 Subject: [PATCH 04/51] multi-template support --- setup.py | 2 +- www/index.html | 20 ++++++++++++++++++-- www/jinjafx_m.css | 7 +++++++ www/jinjafx_m.js | 5 +++++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index e1c1b10..be9f843 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ packages=["jinjafx_server"], include_package_data=True, package_data={'': ['www/*', 'pandoc/reference.docx']}, - install_requires=["jinjafx>=1.22.1", "requests", "cmarkgfm>=0.5.0", "emoji"], + install_requires=["jinjafx>=1.23.0", "requests", "cmarkgfm>=0.5.0", "emoji"], entry_points={ "console_scripts": [ "jinjafx_server=jinjafx_server:main", diff --git a/www/index.html b/www/index.html index 09f723f..f718715 100644 --- a/www/index.html +++ b/www/index.html @@ -13,7 +13,7 @@ - + @@ -32,7 +32,7 @@ - +
@@ -167,6 +167,22 @@
T
E
M
P
L
A
T
E
.
J
2
+ +
+
+ + +
+ + +
+

Outputs


diff --git a/www/jinjafx_m.css b/www/jinjafx_m.css index 077bc9e..35e3e86 100644 --- a/www/jinjafx_m.css +++ b/www/jinjafx_m.css @@ -71,6 +71,13 @@ textarea { border-radius: 0.5rem; padding: 10px; } +.templates { + position: absolute; + z-index: 2; + right: 5px; + top: 5px; + visibility: hidden; +} .info { position: absolute; z-index: 2; diff --git a/www/jinjafx_m.js b/www/jinjafx_m.js index ca75860..265e417 100644 --- a/www/jinjafx_m.js +++ b/www/jinjafx_m.js @@ -1554,6 +1554,7 @@ function getStatusText(code) { else { set_status("darkred", "HTTP ERROR 503", "Service Unavailable"); reset_location(''); + document.getElementById('templates').style.visibility = 'visible'; loaded = true; } } @@ -1578,11 +1579,13 @@ function getStatusText(code) { else { set_status("darkred", "HTTP ERROR 503", "Service Unavailable"); reset_location(''); + document.getElementById('templates').style.visibility = 'visible'; loaded = true; } } else { reset_location(''); + document.getElementById('templates').style.visibility = 'visible'; loaded = true; } } @@ -1607,6 +1610,7 @@ function getStatusText(code) { function remove_info() { document.getElementById('template_info').classList.add('fade-out'); document.getElementById('template_info').style.zIndex = -1000; + document.getElementById('templates').style.visibility = 'visible'; } function set_wait() { @@ -1786,6 +1790,7 @@ function getStatusText(code) { if (editor == window.cmTemplate) { document.getElementById('template_info').classList.add('fade-out'); document.getElementById('template_info').style.zIndex = -1000; + document.getElementById('templates').style.visibility = 'visible'; tinfo = false; } } From fd284470bf32144f92debcca807f2981eade91e3 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 11 Dec 2024 14:04:06 +0000 Subject: [PATCH 05/51] datasets are now sorted alphabetically --- www/dt.html | 2 +- www/index.html | 2 +- www/jinjafx_dt.js | 2 +- www/jinjafx_m.js | 12 +++++++++++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/www/dt.html b/www/dt.html index dfdf901..3a73abc 100644 --- a/www/dt.html +++ b/www/dt.html @@ -8,7 +8,7 @@ - +
diff --git a/www/index.html b/www/index.html index f718715..9b3f4e5 100644 --- a/www/index.html +++ b/www/index.html @@ -32,7 +32,7 @@ - +
diff --git a/www/jinjafx_dt.js b/www/jinjafx_dt.js index f4654e8..c6b9d71 100644 --- a/www/jinjafx_dt.js +++ b/www/jinjafx_dt.js @@ -33,7 +33,7 @@ dtx += ' datasets:\n'; - Object.keys(dt.datasets).forEach(function(ds) { + Object.keys(dt.datasets).sort(window.opener.default_on_top).forEach(function(ds) { var data = dt.datasets[ds].data.match(/\S/) ? window.opener.d(dt.datasets[ds].data).replace(/\s+$/g, '') : ""; var vars = dt.datasets[ds].vars.match(/\S/) ? window.opener.d(dt.datasets[ds].vars).replace(/\s+$/g, '') : ""; diff --git a/www/jinjafx_m.js b/www/jinjafx_m.js index 265e417..56db9cb 100644 --- a/www/jinjafx_m.js +++ b/www/jinjafx_m.js @@ -16,6 +16,16 @@ function rot47(data) { }); } +function default_on_top(a, b) { + if (a == 'Default') { + return -1; + } + else if (b == 'Default') { + return 1; + } + return a.localeCompare(b); +} + var _fromCC = String.fromCharCode.bind(String); function _utob(c) { @@ -153,7 +163,7 @@ function getStatusText(code) { function rebuild_datasets() { document.getElementById('datasets').innerHTML = ''; - Object.keys(datasets).forEach(function(ds) { + Object.keys(datasets).sort(default_on_top).forEach(function(ds) { var a = document.createElement('a'); a.classList.add('dropdown-item', 'text-decoration-none'); a.addEventListener('click', select_dataset, false); From 48f19c368b5a8241594a2f8607c8c06ae68dd264 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 11 Dec 2024 14:26:27 +0000 Subject: [PATCH 06/51] multi-template support --- www/index.html | 19 ++++++++++ www/jinjafx_m.js | 90 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/www/index.html b/www/index.html index 9b3f4e5..a044f4a 100644 --- a/www/index.html +++ b/www/index.html @@ -401,5 +401,24 @@

Add DataSet

+ diff --git a/www/jinjafx_m.js b/www/jinjafx_m.js index 56db9cb..5239a4b 100644 --- a/www/jinjafx_m.js +++ b/www/jinjafx_m.js @@ -107,7 +107,11 @@ function getStatusText(code) { var datasets = { 'Default': [CodeMirror.Doc('', 'data'), CodeMirror.Doc('', 'yaml')] }; + var templates = { + 'Default': CodeMirror.Doc('', 'template') + }; var current_ds = 'Default'; + var current_t = 'Default'; var pending_dt = ''; var dt_password = null; var dt_opassword = null; @@ -160,6 +164,32 @@ function getStatusText(code) { fe.focus(); } + function select_template(e) { + switch_template(e.currentTarget.t_name, true, false); + } + + function switch_template(t, sflag, dflag) { + if (sflag) { + templates[current_t] = window.cmTemplate.swapDoc(templates[t]); + } + else { + window.cmTemplate.swapDoc(templates[t]); + } + if (t != current_t) { + if (dflag) { + window.addEventListener('beforeunload', onBeforeUnload); + if (document.getElementById('get_link').value != 'false') { + document.title = 'JinjaFx [unsaved]'; + } + } + dirty = true; + document.getElementById('selected_t').innerHTML = t; + current_t = t; + onDataBlur(); + } + fe.focus(); + } + function rebuild_datasets() { document.getElementById('datasets').innerHTML = ''; @@ -202,7 +232,7 @@ function getStatusText(code) { xsplit = null; if (window.cmgVars.getValue().match(/\S/)) { - var ds = Object.keys(datasets)[0]; + var ds = Object.keys(datasets).sort(default_on_top)[0]; datasets[ds][1].setValue(window.cmgVars.getValue().trimEnd() + "\n\n" + datasets[ds][1].getValue()); } @@ -213,10 +243,41 @@ function getStatusText(code) { document.getElementById('selected_ds').innerHTML = current_ds; } + function rebuild_templates() { + document.getElementById('templates').innerHTML = ''; + + Object.keys(templates).sort(default_on_top).forEach(function(t) { + var a = document.createElement('a'); + a.classList.add('dropdown-item', 'text-decoration-none'); + a.addEventListener('click', select_template, false); + a.href = '#'; + a.t_name = t; + a.innerHTML = t; + document.getElementById('templates').appendChild(a); + }); + + if (Object.keys(templates).length > 1) { + document.getElementById('select_t').disabled = false; + document.getElementById('delete_t').disabled = false; + } + else { + document.getElementById('select_t').disabled = true; + document.getElementById('delete_t').disabled = true; + } + document.getElementById('selected_t').innerHTML = current_t; + } + function delete_dataset(ds) { delete datasets[ds]; rebuild_datasets(); - switch_dataset(Object.keys(datasets)[0], false, true); + switch_dataset(Object.keys(datasets).sort(default_on_top)[0], false, true); + fe.focus(); + } + + function delete_template(t) { + delete templates[t]; + rebuild_templates(); + switch_template(Object.keys(templates).sort(default_on_top)[0], false, true); fe.focus(); } @@ -304,6 +365,24 @@ function getStatusText(code) { }).show(); return false; } + else if (method == "delete_dataset") { + if (window.cmTemplate.getValue().match(/\S/) { + if (confirm("Are You Sure?") === true) { + delete_template(current_t); + } + } + else { + delete_template(current_t); + } + return false; + } + else if (method == "add_template") { + document.getElementById("t_name").value = ''; + new bootstrap.Modal(document.getElementById('template_input'), { + keyboard: true + }).show(); + return false; + } if (method == "protect") { document.getElementById('password_open2').classList.remove('is-invalid'); @@ -812,6 +891,8 @@ function getStatusText(code) { document.getElementById('delete_ds').onclick = function() { jinjafx('delete_dataset'); }; document.getElementById('add_ds').onclick = function() { jinjafx('add_dataset'); }; + document.getElementById('delete_t').onclick = function() { jinjafx('delete_template'); }; + document.getElementById('add_t').onclick = function() { jinjafx('add_template'); }; document.getElementById('get').onclick = function() { jinjafx('get_link'); }; document.getElementById('get2').onclick = function() { jinjafx('get_link'); }; document.getElementById('update').onclick = function() { jinjafx('update_link'); }; @@ -1810,6 +1891,7 @@ function getStatusText(code) { function load_datatemplate(_dt, _qs, _ds) { try { current_ds = 'Default'; + current_t = 'Default'; window.cmgVars.setValue(""); @@ -1823,7 +1905,7 @@ function getStatusText(code) { }); if ((_ds == null) || !datasets.hasOwnProperty(_ds)) { - current_ds = Object.keys(datasets)[0]; + current_ds = Object.keys(datasets).sort(default_on_top)[0]; } else { current_ds = _ds; @@ -1845,6 +1927,8 @@ function getStatusText(code) { datasets['Default'][1].setValue(_dt.hasOwnProperty("vars") ? _dt.vars : ""); window.cmVars.swapDoc(datasets['Default'][1]); } + + // FIXME: NEED SOME WORK HERE! window.cmTemplate.setValue(_dt.hasOwnProperty("template") ? _dt.template : ""); window.cmData.getDoc().clearHistory(); From e408301cfdb51a6e44d19d122cec9c59dae01f5f Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 11 Dec 2024 15:40:28 +0000 Subject: [PATCH 07/51] multi-template support --- www/dt.html | 2 +- www/index.html | 8 ++--- www/jinjafx_dt.js | 27 +++++++++++++---- www/jinjafx_m.js | 76 ++++++++++++++++++++++++++++++++++------------- 4 files changed, 83 insertions(+), 30 deletions(-) diff --git a/www/dt.html b/www/dt.html index 3a73abc..93332bc 100644 --- a/www/dt.html +++ b/www/dt.html @@ -8,7 +8,7 @@ - +
diff --git a/www/index.html b/www/index.html index a044f4a..ac9c752 100644 --- a/www/index.html +++ b/www/index.html @@ -32,7 +32,7 @@ - +
@@ -167,15 +167,15 @@
T
E
M
P
L
A
T
E
.
J
2
- +
-
diff --git a/www/jinjafx_dt.js b/www/jinjafx_dt.js index c6b9d71..7ad7f1d 100644 --- a/www/jinjafx_dt.js +++ b/www/jinjafx_dt.js @@ -77,14 +77,31 @@ } } - var template = dt.template.match(/\S/) ? window.opener.d(dt.template).replace(/\s+$/g, '') : ""; + if (typeof dt.template == "object") { + dtx += ' template:\n'; - if (template == '') { - dtx += ' template: ""\n'; + Object.keys(dt.template).sort(window.opener.default_on_top).forEach(function(t) { + var template = dt.template[t].match(/\S/) ? window.opener.d(dt.template[t]).replace(/\s+$/g, '') : ""; + + if (template == '') { + dtx += ' "' + t + '": ""\n\n'; + } + else { + dtx += ' "' + t + '": |2\n'; + dtx += window.opener.quote(template.replace(/^/gm, ' ')) + '\n\n'; + } + }); } else { - dtx += ' template: |2\n'; - dtx += window.opener.quote(template.replace(/^/gm, ' ')) + '\n'; + var template = dt.template.match(/\S/) ? window.opener.d(dt.template).replace(/\s+$/g, '') : ""; + + if (template == '') { + dtx += ' template: ""\n'; + } + else { + dtx += ' template: |2\n'; + dtx += window.opener.quote(template.replace(/^/gm, ' ')) + '\n\n'; + } } document.getElementById('container').innerHTML = dtx; diff --git a/www/jinjafx_m.js b/www/jinjafx_m.js index 5239a4b..1791679 100644 --- a/www/jinjafx_m.js +++ b/www/jinjafx_m.js @@ -142,13 +142,12 @@ function getStatusText(code) { function switch_dataset(ds, sflag, dflag) { if (sflag) { - datasets[current_ds][0] = window.cmData.swapDoc(datasets[ds][0]); - datasets[current_ds][1] = window.cmVars.swapDoc(datasets[ds][1]); - } - else { - window.cmData.swapDoc(datasets[ds][0]); - window.cmVars.swapDoc(datasets[ds][1]); + datasets[current_ds][0] = window.cmData.getDoc(); //swapDoc(datasets[ds][0]); + datasets[current_ds][1] = window.cmVars.getDoc(); //swapDoc(datasets[ds][1]); } + window.cmData.swapDoc(datasets[ds][0]); + window.cmVars.swapDoc(datasets[ds][1]); + if (ds != current_ds) { if (dflag) { window.addEventListener('beforeunload', onBeforeUnload); @@ -170,11 +169,10 @@ function getStatusText(code) { function switch_template(t, sflag, dflag) { if (sflag) { - templates[current_t] = window.cmTemplate.swapDoc(templates[t]); - } - else { - window.cmTemplate.swapDoc(templates[t]); + templates[current_t] = window.cmTemplate.getDoc(); } + window.cmTemplate.swapDoc(templates[t]); + if (t != current_t) { if (dflag) { window.addEventListener('beforeunload', onBeforeUnload); @@ -183,6 +181,7 @@ function getStatusText(code) { } } dirty = true; + document.getElementById('delete_t').disabled = (t == 'Default'); document.getElementById('selected_t').innerHTML = t; current_t = t; onDataBlur(); @@ -319,6 +318,7 @@ function getStatusText(code) { function jinjafx_generate() { var vaulted_vars = dt.vars.indexOf('$ANSIBLE_VAULT;') > -1; dt.vars = e(dt.vars); + // FIXME - NEED SOME WORK! dt.template = e(window.cmTemplate.getValue().replace(/\t/g, " ")); dt.id = dt_id; dt.dataset = current_ds; @@ -365,8 +365,8 @@ function getStatusText(code) { }).show(); return false; } - else if (method == "delete_dataset") { - if (window.cmTemplate.getValue().match(/\S/) { + else if (method == "delete_template") { + if (window.cmTemplate.getValue().match(/\S/)) { if (confirm("Are You Sure?") === true) { delete_template(current_t); } @@ -395,7 +395,8 @@ function getStatusText(code) { return false; } - if (window.cmTemplate.getValue().length === 0) { + switch_template(current_t, true, false); + if (templates['Default'].getValue().length === 0) { window.cmTemplate.focus(); set_status("darkred", "ERROR", "No Template"); return false; @@ -631,7 +632,17 @@ function getStatusText(code) { } dt.dataset = current_ds; - dt.template = e(window.cmTemplate.getValue().replace(/\t/g, " ")); + + if (Object.keys(templates).length === 1) { + dt.template = e(window.cmTemplate.getValue().replace(/\t/g, " ")); + } + else { + dt.template = {}; + + Object.keys(templates).sort(default_on_top).forEach(function(t) { + dt.template[t] = e(templates[t].getValue()); + }); + } if ((current_ds === 'Default') && (Object.keys(datasets).length === 1)) { dt.vars = e(window.cmVars.getValue().replace(/\t/g, " ")); @@ -645,7 +656,7 @@ function getStatusText(code) { } switch_dataset(current_ds, true, false); - Object.keys(datasets).forEach(function(ds) { + Object.keys(datasets).sort(default_on_top).forEach(function(ds) { dt.datasets[ds] = {}; dt.datasets[ds].data = e(datasets[ds][0].getValue()); dt.datasets[ds].vars = e(datasets[ds][1].getValue().replace(/\t/g, " ")); @@ -1488,6 +1499,10 @@ function getStatusText(code) { document.getElementById('dataset_input').addEventListener('shown.bs.modal', function (e) { document.getElementById("ds_name").focus(); }); + + document.getElementById('template_input').addEventListener('shown.bs.modal', function (e) { + document.getElementById("t_name").focus(); + }); document.getElementById('ml-dataset-ok').onclick = function() { var new_ds = document.getElementById("ds_name").value; @@ -1504,11 +1519,32 @@ function getStatusText(code) { } }; + document.getElementById('ml-template-ok').onclick = function() { + var new_t = document.getElementById("t_name").value; + + if (new_t.match(/^[A-Z][A-Z0-9_ -]*$/i)) { + if (!templates.hasOwnProperty(new_t)) { + templates[new_t] = CodeMirror.Doc('', 'template'); + rebuild_templates(); + } + switch_template(new_t, true, true); + } + else { + set_status("darkred", "ERROR", "Invalid Template Name"); + } + }; + document.getElementById('ds_name').onkeyup = function(e) { if (e.which == 13) { document.getElementById('ml-dataset-ok').click(); } }; + + document.getElementById('t_name').onkeyup = function(e) { + if (e.which == 13) { + document.getElementById('ml-template-ok').click(); + } + }; function check_open() { if (document.getElementById('password_open1').value == document.getElementById('password_open2').value) { @@ -1645,7 +1681,7 @@ function getStatusText(code) { else { set_status("darkred", "HTTP ERROR 503", "Service Unavailable"); reset_location(''); - document.getElementById('templates').style.visibility = 'visible'; + document.getElementById('stemplates').style.visibility = 'visible'; loaded = true; } } @@ -1670,13 +1706,13 @@ function getStatusText(code) { else { set_status("darkred", "HTTP ERROR 503", "Service Unavailable"); reset_location(''); - document.getElementById('templates').style.visibility = 'visible'; + document.getElementById('stemplates').style.visibility = 'visible'; loaded = true; } } else { reset_location(''); - document.getElementById('templates').style.visibility = 'visible'; + document.getElementById('stemplates').style.visibility = 'visible'; loaded = true; } } @@ -1701,7 +1737,7 @@ function getStatusText(code) { function remove_info() { document.getElementById('template_info').classList.add('fade-out'); document.getElementById('template_info').style.zIndex = -1000; - document.getElementById('templates').style.visibility = 'visible'; + document.getElementById('stemplates').style.visibility = 'visible'; } function set_wait() { @@ -1881,7 +1917,7 @@ function getStatusText(code) { if (editor == window.cmTemplate) { document.getElementById('template_info').classList.add('fade-out'); document.getElementById('template_info').style.zIndex = -1000; - document.getElementById('templates').style.visibility = 'visible'; + document.getElementById('stemplates').style.visibility = 'visible'; tinfo = false; } } From fe1298ceb7e9b5e63f259885bf7a90c18e9c42dc Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 11 Dec 2024 16:44:36 +0000 Subject: [PATCH 08/51] multi-template support --- jinjafx_server.py | 47 ++++++++++++++++++++++++++++++++++------------- www/dt.html | 2 +- www/index.html | 4 ++-- www/jinjafx_dt.js | 6 +++--- www/jinjafx_m.css | 1 - www/jinjafx_m.js | 44 ++++++++++++++++++++++++++++++++++++-------- 6 files changed, 76 insertions(+), 28 deletions(-) diff --git a/jinjafx_server.py b/jinjafx_server.py index 4a13550..144cb68 100755 --- a/jinjafx_server.py +++ b/jinjafx_server.py @@ -329,8 +329,6 @@ def do_GET(self, head=False, cache=True, versioned=False): r = [ 'application/json', 200, json.dumps({ 'dt': self.e(rr).decode('utf-8') }).encode('utf-8'), sys._getframe().f_lineno ] - # os.utime(fpath, None) - else: r = [ 'text/plain', 404, '404 Not Found\r\n'.encode('utf-8'), sys._getframe().f_lineno ] @@ -483,9 +481,17 @@ def do_POST(self): gvars = {} dt = json.loads(postdata.decode('utf-8')) - template = self.d(dt['template']) if 'template' in dt and len(dt['template'].strip()) > 0 else b'' data = self.d(dt['data']) if 'data' in dt and len(dt['data'].strip()) > 0 else b'' + if isinstance(dt['template'], dict): + for t in dt['template']: + dt['template'][t] = self.d(dt['template'][t]).decode('utf-8') if len(dt['template'][t].strip()) > 0 else '' + + else: + dt['template'] = self.d(dt['template']).decode('utf-8') if len(dt['template'].strip()) > 0 else '' + + template = dt['template'] + if 'vars' in dt and len(dt['vars'].strip()) > 0: gyaml = self.d(dt['vars']).decode('utf-8') @@ -511,7 +517,7 @@ def yaml_vault_tag(loader, node): ocount = 0 ret = [0, None] - t = StoppableJinjaFx(jinjafx.JinjaFx().jinjafx, template.decode('utf-8'), data.decode('utf-8'), gvars, ret) + t = StoppableJinjaFx(jinjafx.JinjaFx().jinjafx, template, data.decode('utf-8'), gvars, ret) if timelimit > 0: while t.is_alive() and ((time.time() * 1000) - st) <= (timelimit * 1000): @@ -666,7 +672,7 @@ def html_escape(text): dt_yml += ' "' + ds + '":\n' if vdt['data'] == '': - dt_yml += ' data: ""\n\n' + dt_yml += ' data: ""\n' else: dt_yml += ' data: |2\n' dt_yml += re.sub('^', ' ' * 8, vdt['data'].rstrip(), flags=re.MULTILINE) + '\n\n' @@ -682,7 +688,7 @@ def html_escape(text): vdt['vars'] = self.d(dt['vars']).decode('utf-8') if 'vars' in dt and len(dt['vars'].strip()) > 0 else '' if vdt['data'] == '': - dt_yml += ' data: ""\n\n' + dt_yml += ' data: ""\n' else: dt_yml += ' data: |2\n' dt_yml += re.sub('^', ' ' * 4, vdt['data'].rstrip(), flags=re.MULTILINE) + '\n\n' @@ -693,15 +699,31 @@ def html_escape(text): dt_yml += ' vars: |2\n' dt_yml += re.sub('^', ' ' * 4, vdt['vars'].rstrip(), flags=re.MULTILINE) + '\n\n' - vdt['template'] = self.d(dt['template']).decode('utf-8') if 'template' in dt and len(dt['template'].strip()) > 0 else '' + if isinstance(dt['template'], dict): + dt_yml += ' template:\n' - if vdt['template'] == '': - dt_yml += ' template: ""\n' + for t in dt['template']: + te = self.d(dt['template'][t]).decode('utf-8') if len(dt['template'][t].strip()) > 0 else '' + + if te == '': + dt_yml += ' "' + t + '": ""\n' + else: + dt_yml += ' "' + t + '": |2\n' + dt_yml += re.sub('^', ' ' * 6, te, flags=re.MULTILINE) + '\n\n' + else: - dt_yml += ' template: |2\n' - dt_yml += re.sub('^', ' ' * 4, vdt['template'], flags=re.MULTILINE) + '\n' + te = self.d(dt['template']).decode('utf-8') if len(dt['template'].strip()) > 0 else '' + + if te == '': + dt_yml += ' template: ""\n' + else: + dt_yml += ' template: |2\n' + dt_yml += re.sub('^', ' ' * 4, te, flags=re.MULTILINE) + '\n\n' + + if not dt_yml.endswith('\n\n'): + dt_yml += '\n' - dt_yml += '\nrevision: ' + str(dt_revision) + '\n' + dt_yml += 'revision: ' + str(dt_revision) + '\n' dt_yml += 'dataset: "' + dt['dataset'] + '"\n' dt_hash = hashlib.sha256(dt_yml.encode('utf-8')).hexdigest() @@ -1035,7 +1057,6 @@ def signal_handler(*args): finally: if rflag[0] > 0: -# s.shutdown(1) s.close() diff --git a/www/dt.html b/www/dt.html index 93332bc..958feec 100644 --- a/www/dt.html +++ b/www/dt.html @@ -8,7 +8,7 @@ - +
diff --git a/www/index.html b/www/index.html index ac9c752..53ca980 100644 --- a/www/index.html +++ b/www/index.html @@ -13,7 +13,7 @@ - + @@ -32,7 +32,7 @@ - +
diff --git a/www/jinjafx_dt.js b/www/jinjafx_dt.js index 7ad7f1d..e27a860 100644 --- a/www/jinjafx_dt.js +++ b/www/jinjafx_dt.js @@ -40,7 +40,7 @@ dtx += ' "' + ds + '":\n'; if (data == '') { - dtx += ' data: ""\n\n'; + dtx += ' data: ""\n'; } else { dtx += ' data: |2\n'; @@ -61,7 +61,7 @@ var vars = dt.vars.match(/\S/) ? window.opener.d(dt.vars).replace(/\s+$/g, '') : ""; if (data == '') { - dtx += ' data: ""\n\n'; + dtx += ' data: ""\n'; } else { dtx += ' data: |2\n'; @@ -84,7 +84,7 @@ var template = dt.template[t].match(/\S/) ? window.opener.d(dt.template[t]).replace(/\s+$/g, '') : ""; if (template == '') { - dtx += ' "' + t + '": ""\n\n'; + dtx += ' "' + t + '": ""\n'; } else { dtx += ' "' + t + '": |2\n'; diff --git a/www/jinjafx_m.css b/www/jinjafx_m.css index 35e3e86..02b3c90 100644 --- a/www/jinjafx_m.css +++ b/www/jinjafx_m.css @@ -76,7 +76,6 @@ textarea { z-index: 2; right: 5px; top: 5px; - visibility: hidden; } .info { position: absolute; diff --git a/www/jinjafx_m.js b/www/jinjafx_m.js index 1791679..a303940 100644 --- a/www/jinjafx_m.js +++ b/www/jinjafx_m.js @@ -318,8 +318,18 @@ function getStatusText(code) { function jinjafx_generate() { var vaulted_vars = dt.vars.indexOf('$ANSIBLE_VAULT;') > -1; dt.vars = e(dt.vars); - // FIXME - NEED SOME WORK! - dt.template = e(window.cmTemplate.getValue().replace(/\t/g, " ")); + + if (Object.keys(templates).length === 1) { + dt.template = e(window.cmTemplate.getValue().replace(/\t/g, " ")); + } + else { + dt.template = {}; + + Object.keys(templates).sort(default_on_top).forEach(function(t) { + dt.template[t] = e(templates[t].getValue().replace(/\t/g, " ")); + }); + } + dt.id = dt_id; dt.dataset = current_ds; @@ -640,7 +650,7 @@ function getStatusText(code) { dt.template = {}; Object.keys(templates).sort(default_on_top).forEach(function(t) { - dt.template[t] = e(templates[t].getValue()); + dt.template[t] = e(templates[t].getValue().replace(/\t/g, " ")); }); } @@ -1681,7 +1691,6 @@ function getStatusText(code) { else { set_status("darkred", "HTTP ERROR 503", "Service Unavailable"); reset_location(''); - document.getElementById('stemplates').style.visibility = 'visible'; loaded = true; } } @@ -1706,13 +1715,11 @@ function getStatusText(code) { else { set_status("darkred", "HTTP ERROR 503", "Service Unavailable"); reset_location(''); - document.getElementById('stemplates').style.visibility = 'visible'; loaded = true; } } else { reset_location(''); - document.getElementById('stemplates').style.visibility = 'visible'; loaded = true; } } @@ -1720,6 +1727,7 @@ function getStatusText(code) { if (document.getElementById('get_link').value != 'false') { document.getElementById('lbuttons').classList.remove('d-none'); } + document.getElementById('stemplates').style.visibility = 'hidden'; document.getElementById('template_info').style.visibility = 'visible'; loaded = true; } @@ -1964,8 +1972,27 @@ function getStatusText(code) { window.cmVars.swapDoc(datasets['Default'][1]); } - // FIXME: NEED SOME WORK HERE! - window.cmTemplate.setValue(_dt.hasOwnProperty("template") ? _dt.template : ""); + if (_dt.hasOwnProperty("template")) { + if (typeof _dt['template'] == "object") { + templates = {}; + + Object.keys(_dt['template']).forEach(function(t) { + templates[t] = CodeMirror.Doc(_dt['template'][t], 'template'); + }); + } + else { + templates = { + 'Default': CodeMirror.Doc(_dt['template'], 'template') + }; + } + } + else { + templates = { + 'Default': CodeMirror.Doc('', 'template') + }; + } + + window.cmTemplate.swapDoc(templates[current_t]); window.cmData.getDoc().clearHistory(); window.cmVars.getDoc().clearHistory(); @@ -1973,6 +2000,7 @@ function getStatusText(code) { window.cmTemplate.getDoc().clearHistory(); rebuild_datasets(); + rebuild_templates(); loaded = true; } catch (ex) { From 9c872fb32792611d17e78a1e58fd17feab84d856 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 11 Dec 2024 16:53:56 +0000 Subject: [PATCH 09/51] multi-template support --- www/index.html | 2 +- www/jinjafx_m.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/www/index.html b/www/index.html index 53ca980..3a20205 100644 --- a/www/index.html +++ b/www/index.html @@ -32,7 +32,7 @@ - +
diff --git a/www/jinjafx_m.js b/www/jinjafx_m.js index a303940..d5a2782 100644 --- a/www/jinjafx_m.js +++ b/www/jinjafx_m.js @@ -180,7 +180,6 @@ function getStatusText(code) { document.title = 'JinjaFx [unsaved]'; } } - dirty = true; document.getElementById('delete_t').disabled = (t == 'Default'); document.getElementById('selected_t').innerHTML = t; current_t = t; From b37711d0c533b92cc580224c8c456097ae5a09b8 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 11 Dec 2024 16:58:22 +0000 Subject: [PATCH 10/51] multi-template support --- www/jinjafx_m.js | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/www/jinjafx_m.js b/www/jinjafx_m.js index d5a2782..f0e8efb 100644 --- a/www/jinjafx_m.js +++ b/www/jinjafx_m.js @@ -142,13 +142,14 @@ function getStatusText(code) { function switch_dataset(ds, sflag, dflag) { if (sflag) { - datasets[current_ds][0] = window.cmData.getDoc(); //swapDoc(datasets[ds][0]); - datasets[current_ds][1] = window.cmVars.getDoc(); //swapDoc(datasets[ds][1]); + datasets[current_ds][0] = window.cmData.getDoc(); + datasets[current_ds][1] = window.cmVars.getDoc(); } - window.cmData.swapDoc(datasets[ds][0]); - window.cmVars.swapDoc(datasets[ds][1]); if (ds != current_ds) { + window.cmData.swapDoc(datasets[ds][0]); + window.cmVars.swapDoc(datasets[ds][1]); + if (dflag) { window.addEventListener('beforeunload', onBeforeUnload); if (document.getElementById('get_link').value != 'false') { @@ -164,22 +165,16 @@ function getStatusText(code) { } function select_template(e) { - switch_template(e.currentTarget.t_name, true, false); + switch_template(e.currentTarget.t_name, true); } - function switch_template(t, sflag, dflag) { + function switch_template(t, sflag) { if (sflag) { templates[current_t] = window.cmTemplate.getDoc(); } - window.cmTemplate.swapDoc(templates[t]); if (t != current_t) { - if (dflag) { - window.addEventListener('beforeunload', onBeforeUnload); - if (document.getElementById('get_link').value != 'false') { - document.title = 'JinjaFx [unsaved]'; - } - } + window.cmTemplate.swapDoc(templates[t]); document.getElementById('delete_t').disabled = (t == 'Default'); document.getElementById('selected_t').innerHTML = t; current_t = t; @@ -275,7 +270,7 @@ function getStatusText(code) { function delete_template(t) { delete templates[t]; rebuild_templates(); - switch_template(Object.keys(templates).sort(default_on_top)[0], false, true); + switch_template(Object.keys(templates).sort(default_on_top)[0], false); fe.focus(); } @@ -404,7 +399,7 @@ function getStatusText(code) { return false; } - switch_template(current_t, true, false); + switch_template(current_t, true); if (templates['Default'].getValue().length === 0) { window.cmTemplate.focus(); set_status("darkred", "ERROR", "No Template"); @@ -1536,7 +1531,7 @@ function getStatusText(code) { templates[new_t] = CodeMirror.Doc('', 'template'); rebuild_templates(); } - switch_template(new_t, true, true); + switch_template(new_t, true); } else { set_status("darkred", "ERROR", "Invalid Template Name"); From 65ef6eca26752610cbb33811755393d876180b18 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 11 Dec 2024 16:58:39 +0000 Subject: [PATCH 11/51] multi-template support --- www/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/index.html b/www/index.html index 3a20205..65238d0 100644 --- a/www/index.html +++ b/www/index.html @@ -32,7 +32,7 @@ - +
From 53001beb32489b70c15db6c2253878073428b928 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 11 Dec 2024 17:04:32 +0000 Subject: [PATCH 12/51] multi-template support --- www/index.html | 2 +- www/jinjafx_m.js | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/www/index.html b/www/index.html index 65238d0..7e75865 100644 --- a/www/index.html +++ b/www/index.html @@ -32,7 +32,7 @@ - +
diff --git a/www/jinjafx_m.js b/www/jinjafx_m.js index f0e8efb..30477cb 100644 --- a/www/jinjafx_m.js +++ b/www/jinjafx_m.js @@ -165,16 +165,24 @@ function getStatusText(code) { } function select_template(e) { - switch_template(e.currentTarget.t_name, true); + switch_template(e.currentTarget.t_name, true, false); } - function switch_template(t, sflag) { + function switch_template(t, sflag, dflag) { if (sflag) { templates[current_t] = window.cmTemplate.getDoc(); } if (t != current_t) { window.cmTemplate.swapDoc(templates[t]); + + if (dflag) { + window.addEventListener('beforeunload', onBeforeUnload); + if (document.getElementById('get_link').value != 'false') { + document.title = 'JinjaFx [unsaved]'; + } + dirty = true; + } document.getElementById('delete_t').disabled = (t == 'Default'); document.getElementById('selected_t').innerHTML = t; current_t = t; @@ -270,7 +278,7 @@ function getStatusText(code) { function delete_template(t) { delete templates[t]; rebuild_templates(); - switch_template(Object.keys(templates).sort(default_on_top)[0], false); + switch_template(Object.keys(templates).sort(default_on_top)[0], false, true); fe.focus(); } @@ -399,7 +407,7 @@ function getStatusText(code) { return false; } - switch_template(current_t, true); + switch_template(current_t, true, false); if (templates['Default'].getValue().length === 0) { window.cmTemplate.focus(); set_status("darkred", "ERROR", "No Template"); @@ -1531,7 +1539,7 @@ function getStatusText(code) { templates[new_t] = CodeMirror.Doc('', 'template'); rebuild_templates(); } - switch_template(new_t, true); + switch_template(new_t, true, true); } else { set_status("darkred", "ERROR", "Invalid Template Name"); From bdd78362d6ef18e6b4058ae1fbd63984469e4865 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 11 Dec 2024 23:49:03 +0000 Subject: [PATCH 13/51] multi-template support --- www/dt.html | 2 +- www/index.html | 12 ++++++------ www/jinjafx.css | 1 - www/jinjafx_m.css | 4 ++-- www/jinjafx_m.js | 2 +- www/logs.html | 2 +- www/output.html | 2 +- 7 files changed, 12 insertions(+), 13 deletions(-) diff --git a/www/dt.html b/www/dt.html index 958feec..0c931b7 100644 --- a/www/dt.html +++ b/www/dt.html @@ -6,7 +6,7 @@ JinjaFx DataTemplate - + diff --git a/www/index.html b/www/index.html index 7e75865..4d51e4d 100644 --- a/www/index.html +++ b/www/index.html @@ -12,8 +12,8 @@ - - + + @@ -32,7 +32,7 @@ - +
@@ -170,7 +170,7 @@
-
- - + +
diff --git a/www/jinjafx.css b/www/jinjafx.css index 7d6745c..da3346e 100644 --- a/www/jinjafx.css +++ b/www/jinjafx.css @@ -51,7 +51,6 @@ a:link { a:hover { text-decoration: underline; } - .btn-primary { --bs-btn-color: #fff; --bs-btn-disabled-color: #fff; diff --git a/www/jinjafx_m.css b/www/jinjafx_m.css index 02b3c90..77c7703 100644 --- a/www/jinjafx_m.css +++ b/www/jinjafx_m.css @@ -74,8 +74,8 @@ textarea { .templates { position: absolute; z-index: 2; - right: 5px; - top: 5px; + right: 8px; + top: 8px; } .info { position: absolute; diff --git a/www/jinjafx_m.js b/www/jinjafx_m.js index 30477cb..20d8ec1 100644 --- a/www/jinjafx_m.js +++ b/www/jinjafx_m.js @@ -259,7 +259,7 @@ function getStatusText(code) { if (Object.keys(templates).length > 1) { document.getElementById('select_t').disabled = false; - document.getElementById('delete_t').disabled = false; + document.getElementById('delete_t').disabled = (current_t == 'Default'); } else { document.getElementById('select_t').disabled = true; diff --git a/www/logs.html b/www/logs.html index df43dff..6f6d729 100644 --- a/www/logs.html +++ b/www/logs.html @@ -6,7 +6,7 @@ JinjaFx Logs - + diff --git a/www/output.html b/www/output.html index 75e03f4..b54d6b5 100644 --- a/www/output.html +++ b/www/output.html @@ -6,7 +6,7 @@ Generating... - + From c0c46af1173a6d441267c90d08f4db99737f01ae Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 12 Dec 2024 00:02:37 +0000 Subject: [PATCH 14/51] multi-template support --- www/index.html | 13 ++++++++----- www/jinjafx_m.css | 6 +++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/www/index.html b/www/index.html index 4d51e4d..9e07fbb 100644 --- a/www/index.html +++ b/www/index.html @@ -13,7 +13,7 @@ - + @@ -172,9 +172,10 @@
@@ -194,7 +195,7 @@

Outputs


<output "Output 2"> ... </output> -

By default an output is rendered as text, but you can also tell JinjaFx to render it as HTML or Markdown (GFM), e.g:

+

By default an output is rendered as text, but you can also tell JinjaFx to render it as HTML or Markdown, e.g:

<output:html "Output 1">
 ...
 </output>
@@ -202,6 +203,8 @@ 

Outputs


<output:markdown "Output 2"> ... </output>
+

JinjaFx supports nested templates - if you click on the "+" at the top right of the "template.j2" pane then you can create multiple templates and import them into the "Default" template via the following syntax:

+
{% include "<template>" %}

Dynamic CSV


Within the "data.csv" pane you can specify regular CSV where each row will be treated as a data set using the header row for variable names. However, it also supports something which I call Dynamic CSV where you can use regular expression based static character classes or static groups as values within the data rows using (value1|value2|value3) or [a-f]. These will then be expanded into multiple rows, e.g:

DEVICE, TYPE
diff --git a/www/jinjafx_m.css b/www/jinjafx_m.css
index 77c7703..f1f261f 100644
--- a/www/jinjafx_m.css
+++ b/www/jinjafx_m.css
@@ -80,9 +80,9 @@ textarea {
 .info {
   position: absolute;
   z-index: 2;
-  right: 30px;
-  top: 30px;
-  bottom: 30px;
+  right: 15px;
+  top: 15px;
+  bottom: 15px;
   width: 50%;
   visibility: hidden;
 }

From 64caa91140a780d3c7e4038476c3ab25ec2f82de Mon Sep 17 00:00:00 2001
From: Chris Mason 
Date: Thu, 12 Dec 2024 00:43:17 +0000
Subject: [PATCH 15/51] support for encrypt datatemplate

---
 www/index.html   |  8 ++++++--
 www/jinjafx_m.js | 16 ++++++++++++++++
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/www/index.html b/www/index.html
index 9e07fbb..91cd823 100644
--- a/www/index.html
+++ b/www/index.html
@@ -32,7 +32,7 @@
     
     
     
-    
+    
   
   
     
@@ -54,7 +54,7 @@
-
+
diff --git a/www/jinjafx_m.js b/www/jinjafx_m.js index 20d8ec1..59d64fa 100644 --- a/www/jinjafx_m.js +++ b/www/jinjafx_m.js @@ -864,6 +864,7 @@ function getStatusText(code) { reset_location(''); } document.getElementById('lbuttons').classList.remove('d-none'); + document.getElementById('buttons').classList.remove('d-none'); loaded = true; clear_wait(); }; @@ -872,6 +873,7 @@ function getStatusText(code) { set_status("darkred", "ERROR", "XMLHttpRequest.onError()"); reset_location(''); document.getElementById('lbuttons').classList.remove('d-none'); + document.getElementById('buttons').classList.remove('d-none'); loaded = true; clear_wait(); }; @@ -879,6 +881,7 @@ function getStatusText(code) { set_status("darkred", "ERROR", "XMLHttpRequest.onTimeout()"); reset_location(''); document.getElementById('lbuttons').classList.remove('d-none'); + document.getElementById('buttons').classList.remove('d-none'); loaded = true; clear_wait(); }; @@ -891,6 +894,7 @@ function getStatusText(code) { } else { document.getElementById('lbuttons').classList.remove('d-none'); + document.getElementById('buttons').classList.remove('d-none'); loaded = true; } } @@ -898,6 +902,7 @@ function getStatusText(code) { console.log(ex); set_status("darkred", "ERROR", ex); document.getElementById('lbuttons').classList.remove('d-none'); + document.getElementById('buttons').classList.remove('d-none'); loaded = true; onChange(null, true); } } @@ -1454,6 +1459,8 @@ function getStatusText(code) { document.getElementById("password_modify1").value = ''; document.getElementById("password_modify2").value = ''; document.getElementById("password_modify2").disabled = true; + document.getElementById('encrypt_dt').disabled = true; + document.getElementById('encrypt_dt').checked = false; fe.focus(); }); @@ -1562,10 +1569,12 @@ function getStatusText(code) { if (document.getElementById('password_open1').value == document.getElementById('password_open2').value) { document.getElementById('password_open2').classList.remove('is-invalid'); document.getElementById('password_open2').classList.add('is-valid'); + document.getElementById('encrypt_dt').disabled = false; } else { document.getElementById('password_open2').classList.remove('is-valid'); document.getElementById('password_open2').classList.add('is-invalid'); + document.getElementById('encrypt_dt').disabled = true; } }; @@ -1607,6 +1616,7 @@ function getStatusText(code) { if (document.getElementById('password_open2').disabled == true) { document.getElementById('password_open2').disabled = false; document.getElementById('password_open2').classList.add('is-invalid'); + document.getElementById('encrypt_dt').disabled = true; } else { check_open(); @@ -1617,6 +1627,8 @@ function getStatusText(code) { document.getElementById('password_open2').value = ''; document.getElementById('password_open2').classList.remove('is-valid'); document.getElementById('password_open2').classList.remove('is-invalid'); + document.getElementById('encrypt_dt').disabled = true; + document.getElementById('encrypt_dt').checked = false; } }; @@ -1693,6 +1705,7 @@ function getStatusText(code) { else { set_status("darkred", "HTTP ERROR 503", "Service Unavailable"); reset_location(''); + document.getElementById('buttons').classList.remove('d-none'); loaded = true; } } @@ -1717,11 +1730,13 @@ function getStatusText(code) { else { set_status("darkred", "HTTP ERROR 503", "Service Unavailable"); reset_location(''); + document.getElementById('buttons').classList.remove('d-none'); loaded = true; } } else { reset_location(''); + document.getElementById('buttons').classList.remove('d-none'); loaded = true; } } @@ -1731,6 +1746,7 @@ function getStatusText(code) { } document.getElementById('stemplates').style.visibility = 'hidden'; document.getElementById('template_info').style.visibility = 'visible'; + document.getElementById('buttons').classList.remove('d-none'); loaded = true; } } From c9b0726dbf84a93670df2d3ecea384ea29f0870e Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 12 Dec 2024 16:18:57 +0000 Subject: [PATCH 16/51] encrypt datatemplates --- jinjafx_server.py | 62 ++++++++++++++++++++++++++++++++++++++--------- setup.py | 2 +- www/index.html | 5 ++-- www/jinjafx_m.js | 44 +++++++++++++++++++++++++++++---- 4 files changed, 93 insertions(+), 20 deletions(-) diff --git a/jinjafx_server.py b/jinjafx_server.py index 144cb68..4ca4a49 100755 --- a/jinjafx_server.py +++ b/jinjafx_server.py @@ -332,6 +332,19 @@ def do_GET(self, head=False, cache=True, versioned=False): else: r = [ 'text/plain', 404, '404 Not Found\r\n'.encode('utf-8'), sys._getframe().f_lineno ] + if r[1] == 200: + if dt.startswith('$VAULTY;'): + if 'X-Dt-Password' in self.headers: + try: + dt = jinjafx.Vaulty().decrypt(dt, self.headers['X-Dt-Password']) + r = [ 'application/json', 200, json.dumps({ 'dt': self.e(dt.encode('utf-8')).decode('utf-8') }).encode('utf-8'), sys._getframe().f_lineno ] + + except Exception: + r = [ 'text/plain', 401, '401 Unauthorized\r\n'.encode('utf-8'), sys._getframe().f_lineno ] + + else: + r = [ 'text/plain', 401, '401 Unauthorized\r\n'.encode('utf-8'), sys._getframe().f_lineno ] + if r[1] == 200: mo = re.search(r'dt_password: "(\S+)"', dt) if mo != None: @@ -372,12 +385,15 @@ def do_GET(self, head=False, cache=True, versioned=False): r = [ ctype, 200, f.read(), sys._getframe().f_lineno ] if fpath == '/index.html': + local_repository = 'false' if repository or aws_s3_url or github_url: get_link = 'true' + if repository: + local_repository = 'true' else: get_link = 'false' - r[2] = r[2].decode('utf-8').replace('{{ jinjafx.version }}', jinjafx.__version__ + ' / Jinja2 v' + jinja2_version).replace('{{ get_link }}', get_link).encode('utf-8') + r[2] = r[2].decode('utf-8').replace('{{ jinjafx.version }}', jinjafx.__version__ + ' / Jinja2 v' + jinja2_version).replace('{{ get_link }}', get_link).replace('{{ repository }}', local_repository).encode('utf-8') elif fpath == '/output.html': if pandoc: @@ -634,12 +650,15 @@ def html_escape(text): dt_opassword = '' dt_mpassword = '' dt_revision = 1 + dt_encrypt = 0 if hasattr(self, 'headers'): if 'X-Dt-Password' in self.headers: dt_password = self.headers['X-Dt-Password'] if 'X-Dt-Open-Password' in self.headers: dt_opassword = self.headers['X-Dt-Open-Password'] + if 'X-Dt-Encrypt' in self.headers: + dt_encrypt = int(self.headers['X-Dt-Encrypt']) if 'X-Dt-Modify-Password' in self.headers: dt_mpassword = self.headers['X-Dt-Modify-Password'] if 'X-Dt-Revision' in self.headers: @@ -725,9 +744,9 @@ def html_escape(text): dt_yml += 'revision: ' + str(dt_revision) + '\n' dt_yml += 'dataset: "' + dt['dataset'] + '"\n' - - dt_hash = hashlib.sha256(dt_yml.encode('utf-8')).hexdigest() - dt_yml += 'dt_hash: "' + dt_hash + '"\n' + + if dt_encrypt: + dt_yml += 'encrypted: 1\n' if 'id' in params: if re.search(r'^[A-Za-z0-9_-]{1,24}$', params['id']): @@ -775,7 +794,6 @@ def update_dt(rdt, dt_yml, r): def add_client_fields(dt_yml, remote_addr): dt_yml += 'remote_addr: "' + remote_addr + '"\n' dt_yml += 'updated: "' + str(int(time.time())) + '"\n' - return dt_yml if aws_s3_url: @@ -837,18 +855,38 @@ def add_client_fields(dt_yml, remote_addr): if os.path.isfile(dt_filename): with open(dt_filename, 'rb') as f: - rr = f.read() + rr = f.read().decode('utf-8') - m = re.search(r'revision: (\d+)', rr.decode('utf-8')) - if m != None: - if dt_revision <= int(m.group(1)): - r = [ 'text/plain', 409, '409 Conflict\r\n', sys._getframe().f_lineno ] + if rr.startswith('$VAULTY'): + try: + rr = jinjafx.Vaulty().decrypt(rr, dt_password) - if r[1] != 409: - dt_yml, r = update_dt(rr.decode('utf-8'), dt_yml, r) + except Exception: + r = [ 'text/plain', 401, '401 Unauthorized\r\n'.encode('utf-8'), sys._getframe().f_lineno ] + + if r[1] != 401: + m = re.search(r'revision: (\d+)', rr) + if m != None: + if dt_revision <= int(m.group(1)): + r = [ 'text/plain', 409, '409 Conflict\r\n', sys._getframe().f_lineno ] + + if r[1] != 409: + dt_yml, r = update_dt(rr, dt_yml, r) if r[1] == 500 or r[1] == 200: dt_yml = add_client_fields(dt_yml, remote_addr) + + if dt_encrypt: + if dt_opassword != '': + dt_yml = jinjafx.Vaulty().encrypt(dt_yml, dt_opassword) + '\n' + + elif dt_password != '': + dt_yml = jinjafx.Vaulty().encrypt(dt_yml, dt_password) + '\n' + + else: + r = [ 'text/plain', 400, '400 Bad Request\r\n', sys._getframe().f_lineno ] + + if r[1] == 500 or r[1] == 200: with open(dt_filename, 'w') as f: f.write(dt_yml) diff --git a/setup.py b/setup.py index be9f843..523b109 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ packages=["jinjafx_server"], include_package_data=True, package_data={'': ['www/*', 'pandoc/reference.docx']}, - install_requires=["jinjafx>=1.23.0", "requests", "cmarkgfm>=0.5.0", "emoji"], + install_requires=["jinjafx>=1.23.1", "requests", "cmarkgfm>=0.5.0", "emoji"], entry_points={ "console_scripts": [ "jinjafx_server=jinjafx_server:main", diff --git a/www/index.html b/www/index.html index 91cd823..2329070 100644 --- a/www/index.html +++ b/www/index.html @@ -32,7 +32,7 @@ - +
@@ -251,6 +251,7 @@
JinjaFx v{{ jinjafx.version }}
+