<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" href="data:;base64,iVBORw0KGgo="> <title>Mail4one Web config</title> <script type="application/json" id="m41config"> { "matches": [ { "name": "mydomain", "addr_rexs": [ ".*@mydomain.com", ".*@m.mydomain.com" ] }, { "name": "personal", "addrs": [ "first.last@mydomain.com", "secret.name@mydomain.com" ] } ], "boxes": [ { "name": "spam", "rules": [ { "match_name": "mydomain", "negate": true, "stop_check": true } ] }, { "name": "important", "rules": [ { "match_name": "personal" } ] }, { "name": "all", "rules": [ { "match_name": "default_match_all" } ] } ], "users": [ { "username": "mymobile", "password_hash": "AFTY5EVN7AX47ZL7UMH3BETYWFBTAV3XHR73CEFAJBPN2NIHPWDZHV2UQSMSPHSQQ2A2BFQBNC77VL7F2UKATQNJZGYLCSU6C43UQDAQXWXSWNGAEPGIMG2F3QDKBXL3MRHY6K2BPID64ZR6LABLPVSF", "mbox": "important" }, { "username": "mydesk", "password_hash": "AFTY5EVN7AX47ZL7UMH3BETYWFBTAV3XHR73CEFAJBPN2NIHPWDZHV2UQSMSPHSQQ2A2BFQBNC77VL7F2UKATQNJZGYLCSU6C43UQDAQXWXSWNGAEPGIMG2F3QDKBXL3MRHY6K2BPID64ZR6LABLPVSF", "mbox": "all" } ] } </script> <style> td { padding: 5px; } table { border: 2px solid; } thead { background-color: orange; } h1.page-title { background-color: orange; } h3 { text-align: center; } .outer { display: flex; justify-content: center; background: grey; } .inner { display: flex; flex-direction: column; align-items: center; border: 2px solid; background-color: lightyellow; padding: 10px; justify-content: space-around; margin: 10px; width: 1200px; } .m41-box { display: grid; grid-template-columns: 1fr 3fr; margin: 10px; // background-color: lightblue; } .multiline { text-align: left; padding: 5px; padding-left: 15px; } #web-cfg-matches { width: 90%; text-align: center; } #web-cfg-matches tbody tr:nth-of-type(odd) { background: lightblue; } #web-cfg-matches tbody tr:nth-of-type(even) { background: lightgrey; } #web-cfg-boxes { text-align: center; width: 90%; // display: grid; // grid-template-columns: 1fr ; //justify-content: space-around; // justify-content: flex-start; // background-color: blue; } #web-cfg-boxes tbody:nth-of-type(odd) { background: lightblue; } #web-cfg-boxes tbody:nth-of-type(even) { background: lightgrey; } #web-cfg-boxes tbody tr td div.dummy{ display: none; } #web-cfg-boxes tbody tr td div button.dummy { display: none; } #web-cfg-boxes tbody:first-of-type tr td.box div button.up.dummy { display: block; } #web-cfg-boxes tbody:first-of-type tr td.box div button.up.real { display: none; } #web-cfg-boxes tbody:last-of-type tr td.box div button.down.dummy { display: block; } #web-cfg-boxes tbody:last-of-type tr td.box div button.down.real { display: none; } #web-cfg-boxes tbody:only-of-type tr td.box div.real { display: none; } #web-cfg-boxes tbody:only-of-type tr td.box div.dummy { display: flex; } td.box { display: flex; justify-content: center; } .button-group { display: flex; justify-content: center; } </style> <script type="application/javascript"> "use strict" // Globals let server_config let matches_table let match_row_template let boxes_table let box_template // let rule_template function initGlobals() { server_config = JSON.parse(document.getElementById('m41config').text) matches_table = document.getElementById("web-cfg-matches") match_row_template = document.getElementById("web-cfg-matches-row") boxes_table = document.getElementById("web-cfg-boxes") box_template = document.getElementById("web-cfg-box") // rule_template = document.getElementById("web-cfg-boxes-rule-li") } function populate_match_table(matches_config) { for (const { name: match_name, addrs, addr_rexs } of matches_config) { const [match_type, match_values] = (() => { if (addrs != undefined) { return ["addrs", addrs] } else { return ["addr_rexs", addr_rexs] } })() addMatchRow() const last_row = matches_table.tBodies[0].lastElementChild const [ ,name_cell, {firstElementChild: type_select}, value_cell ] = last_row.cells name_cell.innerText = match_name type_select.value = match_type value_cell.innerText = match_values.join("\n") } } function extract_match_table() { let matches = [] for (let row of matches_table.tBodies[0].rows) { const [ ,name_cell, {firstElementChild: type_select}, value_cell ] = row.cells let m = {"name" : name_cell.innerText} switch (type_select.value) { case "addrs": m["addrs"] = value_cell.innerText.split("\n") break case "addr_rexs": m["addr_rexs"] = value_cell.innerText.split("\n") break } matches.push(m) } return matches } function addMatchRow() { let row_clone = match_row_template.content.cloneNode(true) matches_table.tBodies[0].appendChild(row_clone) } function populate_boxes_list(boxes_config) { for (const {name:box_name, rules} of boxes_config) { addBox() const tbody = boxes_table.lastElementChild const box = tbody.firstElementChild // console.log(box) const [, {children: [box_text, ]}, , {firstElementChild: match_select}, {firstElementChild: negate_check}, {firstElementChild: stop_check}, ] = box.children box_text.value = box_name const [first_rule, ...rest] = rules const {match_name, negate = false, stop = false} = first_rule match_select.value = match_name negate_check.checked = negate stop_check.checked = stop for (const {match_name, negate = false, stop_check = false} of rest ) { addRule(box) const rule = tbody.lastElementChild const [, {firstElementChild: match_select}, {firstElementChild: negateCheck}, {firstElementChild: stopCheck} ] = rule.children match_select.value = match_name negate_check.checked = negate stop_check.checked = stop } } } function addRule(box) { box.parentElement.appendChild(box.cloneNode(true)) box.children[0].rowSpan++; box.children[1].rowSpan++; const newrule = box.parentElement.lastElementChild newrule.removeClass("fist-tr") newrule.firstElementChild.remove() newrule.firstElementChild.remove() } function addBox() { let box_clone = box_template.content.cloneNode(true) boxes_table.appendChild(box_clone) } function moveUp(button) { const li = button.parentElement.parentElement.parentElement if (li.previousElementSibling != null) { li.parentNode.insertBefore(li, li.previousElementSibling) } } function moveDown(button) { const li = button.parentElement.parentElement.parentElement if (li.nextElementSibling != null) { li.parentNode.insertBefore(li.nextElementSibling, li) } } function main() { initGlobals() populate_match_table(server_config["matches"]) save() document.getElementById("before").innerText = JSON.stringify(server_config["matches"], null, 2) populate_boxes_list(server_config["boxes"]) } function save() { const matches = extract_match_table() document.getElementById("after").innerText = JSON.stringify(matches, null, 2) } </script> </head> <body onload="main()"> <template id="web-cfg-matches-row"> <tr> <td><button onClick="this.parentElement.parentElement.remove()">✗</button></td> <td contentEditable></td> <td> <select> <option value="addrs">List of addresses</option> <option value="addr_rexs">List of regexes for addresses</option> </select> </td> <td contentEditable class="multiline"></td> </tr> </template> <template id="web-cfg-box"> <tbody> <tr> <td class="box"> <div class="button-group dummy"> <button disabled>✗</button> <button disabled>↑</button> <button disabled>↓</button> </div> <div class="button-group real"> <button onClick="this.parentElement.parentElement.parentElement.parentElement.remove()">✗</button> <button onClick="moveBoxUp(this)" class="real up">↑</button> <button disabled class="dummy up">↑</button> <button onClick="moveBoxDown(this)" class="real down">↓</button> <button disabled class="dummy down">↓</button> </div> <button onClick="addRule(this.parentElement.parentElement)">+</button> </td> <td contentEditable> <input type="text" contentEditable> </td> <td class="rule"> <div class="button-group dummy"> <button disabled>✗</button> <button disabled>↑</button> <button disabled>↓</button> </div> <div class="button-group real"> <button onClick="this.parentElement.parentElement.parentElement.remove()">✗</button> <button onClick="moveRuleUp(this)" class="real up">↑</button> <button disabled class="dummy up">↑</button> <button onClick="moveRuleDown(this)" class="real down">↓</button> <button disabled class="dummy down">↓</button> </div> </td> <td> <select></select> </td> <td> <input type=checkbox> </td> <td> <input type=checkbox> </td> </tr> </tbody> </template> <div class="outer"> <div class="inner"> <h1 class="page-title">Mail4one Web config</h1> <div id="top-menu"> <button onClick="save()">Previous</button> <button onClick="save()">Matches</button> <button onClick="save()">Boxes</button> <button onClick="save()">Users</button> <button onClick="save()">JSON</button> <button onClick="save()">Next</button> </div> <div id="match-page"> <h3> Matches </h3> <table id="web-cfg-matches"> <thead> <tr> <th> <button onClick="addMatchRow()">+</button> </th> <th>Match</th> <th>Type</th> <th>Values</th> </tr> </thead> <tbody> </tbody> </table> </div> <div id=box-page"> <h3>Boxes</h3> <table id="web-cfg-boxes"> <thead> <tr> <th><button onClick="addBox()">+</button></th> <th>Mailbox</th> <th></th> <th>Match</th> <th>Invert</th> <th>Stop</th> </tr> </thead> </table> </div> <hr> <h3>Before</h3> <pre id="before"></pre> <hr> <h3>After</h3> <pre id="after"></pre> <hr> </div> </div> </body> </html>