parent
014523c17d
commit
3e4554f759
|
@ -1,5 +1,9 @@
|
||||||
# Validator for the standard csv format for imports to museum-digital
|
# Validator for the standard csv format for imports to museum-digital
|
||||||
|
|
||||||
|
# Development
|
||||||
|
|
||||||
|
To update, run `scripts/compile.php`.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- [JSZip](https://github.com/Stuk/jszip), dual-licensed under MIT & GPLv3
|
- [JSZip](https://github.com/Stuk/jszip), dual-licensed under MIT & GPLv3
|
||||||
|
|
|
@ -10,6 +10,8 @@ declare(strict_types = 1);
|
||||||
|
|
||||||
const CACHE_DIR_PERMS = 0775;
|
const CACHE_DIR_PERMS = 0775;
|
||||||
|
|
||||||
|
const ALLOWED_LANGS = ['de', 'en', 'hu'];
|
||||||
|
|
||||||
const TL_FILE_DIRS = [
|
const TL_FILE_DIRS = [
|
||||||
__DIR__ . "/../l10n/musdb/",
|
__DIR__ . "/../l10n/musdb/",
|
||||||
__DIR__ . "/../l10n/importer/",
|
__DIR__ . "/../l10n/importer/",
|
||||||
|
|
|
@ -38,7 +38,7 @@ a { text-decoration: none; color: inherit; }
|
||||||
|
|
||||||
h1 { display: block; max-width: 600px; margin: 0 auto 1.5em auto; }
|
h1 { display: block; max-width: 600px; margin: 0 auto 1.5em auto; }
|
||||||
h1 > * { display: inline-block; vertical-align: middle; color: var(--color-fg-less); }
|
h1 > * { display: inline-block; vertical-align: middle; color: var(--color-fg-less); }
|
||||||
h1 img { height: 2em; margin-right: .2em; border-radius: .1em; opacity: .7; transition: opacity .4s; }
|
h1 img { height: 2em; margin-right: .5em; border-radius: .1em; opacity: .7; transition: opacity .4s; }
|
||||||
h1 img:hover { opacity: 1; }
|
h1 img:hover { opacity: 1; }
|
||||||
|
|
||||||
main,
|
main,
|
||||||
|
@ -118,6 +118,23 @@ ul.fieldList > li.humanTLToggled { border-color: var(--color-accent-hover); back
|
||||||
.actionList > li a { display: inline-block; padding: .3em; border-radius: .3em; transition: background .4s; }
|
.actionList > li a { display: inline-block; padding: .3em; border-radius: .3em; transition: background .4s; }
|
||||||
.actionList > li a:hover { background: var(--color-accent-hover); }
|
.actionList > li a:hover { background: var(--color-accent-hover); }
|
||||||
|
|
||||||
|
.loading:before,
|
||||||
|
.loading:after { content: " "; display: block;
|
||||||
|
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
|
||||||
|
height: 64px; width: 64px; margin: 0; padding: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 8px solid var(--color-accent-hover);
|
||||||
|
border-color: var(--color-accent-hover) transparent transparent transparent;
|
||||||
|
z-index: 100;
|
||||||
|
animation: rotating 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; animation-delay: -0.15s; }
|
||||||
|
|
||||||
|
.loading:after { animation-delay: -0.45s; }
|
||||||
|
|
||||||
|
@keyframes rotating {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
/* =============
|
/* =============
|
||||||
| Dark mode
|
| Dark mode
|
||||||
|============ */
|
|============ */
|
||||||
|
|
35
public/assets/css/csvxml.min.css
vendored
35
public/assets/css/csvxml.min.css
vendored
|
@ -1,32 +1,35 @@
|
||||||
@import 'editMenu.css';@font-face{font-family:sourceSansPro;src:local('Source-Sans-Pro'),local('Source Sans Pro'),url(../fonts/SourceSansPro-Regular.woff2)format('woff2'),url(../fonts/SourceSansPro-Regular.ttf)format('truetype');font-display:swap}*{box-sizing:border-box;z-index:1}
|
@import 'editMenu.css';@import 'dialogue.css';:root{--color-bg-normal:#FFF;--color-bg-raised:#EEE;--color-bg-raised2:#FAFAFA;--color-fg-normal:#000;--color-fg-less:#212121;--color-borders:#D6D6D6;--color-borders-dark:#646464;--color-accent-normal:#FFCCBC;--color-accent-hover:#CB9B8C}
|
||||||
body{margin:2em;background:#FFF;font-family:sourceSansPro,Arial,Helvetica,Times;font-size:1.2em;line-height:1.5em}
|
@font-face{font-family:sourceSansPro;src:local('Source-Sans-Pro'),local('Source Sans Pro'),url(../fonts/SourceSansPro-Regular.woff2)format('woff2'),url(../fonts/SourceSansPro-Regular.ttf)format('truetype');font-display:swap}*{box-sizing:border-box;z-index:1}
|
||||||
|
body{margin:2em;background:var(--color-bg-normal);font-family:sourceSansPro,Arial,Helvetica,Times;font-size:1.2em;line-height:1.5em}
|
||||||
a{text-decoration:none;color:inherit}
|
a{text-decoration:none;color:inherit}
|
||||||
h1{display:block;max-width:600px;margin:0 auto 1.5em auto}
|
h1{display:block;max-width:600px;margin:0 auto 1.5em auto}
|
||||||
h1>*{display:inline-block;vertical-align:middle;color:#212121}
|
h1>*{display:inline-block;vertical-align:middle;color:var(--color-fg-less)}
|
||||||
h1 img{height:2em;margin-right:.2em;border-radius:.1em;opacity:.7;transition:opacity.4s}
|
h1 img{height:2em;margin-right:.5em;border-radius:.1em;opacity:.7;transition:opacity.4s}
|
||||||
h1 img:hover{opacity:1}
|
h1 img:hover{opacity:1}
|
||||||
body>div,body>form{display:block;max-width:90vw;margin:0 auto 3em auto;padding-bottom:3em}
|
main,body>div,body>form{display:block;max-width:90vw;margin:0 auto 3em auto;padding-bottom:3em}
|
||||||
form>div{margin-bottom:1em}
|
form>div{margin-bottom:1em}
|
||||||
label{display:block;font-weight:bold;margin-bottom:.5em}.invisible{display:none!important;opacity:0}.buttonLike,select,button,textarea,input{display:block;width:100%;padding:.5em.5em;border:2.5px solid #EEE;background:inherit;color:#424242;font-family:roboto;font-size:1em;border-radius:.2em;transition:border.2s}.buttonLike{display:inline-block;text-align:center}
|
label{display:block;font-weight:bold;margin-bottom:.5em}.invisible{display:none!important;opacity:0}.buttonLike,select,button,textarea,input{display:block;width:100%;padding:.5em.5em;border:2.5px solid var(--color-bg-raised);background:inherit;color:var(--color-fg-less);font-family:roboto;font-size:1em;border-radius:.2em;transition:border.2s}.buttonLike{display:inline-block;text-align:center}
|
||||||
input[type="submit"],button{padding:.5em.8em}.buttonLike,input[type="submit"],button{background:#FFCCBC;color:#424242;border:none;font-weight:bold;text-transform:uppercase;transition:background.2s,color.2s}
|
input[type="submit"],button{padding:.5em.8em}.buttonLike,input[type="submit"],button{background:var(--color-accent-normal);color:var(--color-fg-less);border:none;font-weight:bold;text-transform:uppercase;transition:background.2s,color.2s}
|
||||||
textarea{line-height:1.2em}
|
textarea{line-height:1.2em}
|
||||||
select:hover,textarea:hover,input:hover{border-color:#888}.buttonLike:focus,input[type="submit"]:focus,button:focus,.buttonLike:hover,input[type="submit"]:hover,button:hover{background:#CB9B8C;color:#000}
|
select:hover,textarea:hover,input:hover{border-color:#888}.buttonLike:focus,input[type="submit"]:focus,button:focus,.buttonLike:hover,input[type="submit"]:hover,button:hover{background:var(--color-accent-hover);color:#000}
|
||||||
aside.buttonLike+.buttonLike,aside button+button{margin-top:.5em}
|
aside.buttonLike+.buttonLike,aside button+button{margin-top:.5em}
|
||||||
aside>h4:first-child{margin-top:0}
|
aside>h4:first-child{margin-top:0}
|
||||||
select:focus,textarea:focus,input:focus{border-color:#CB9B8C;box-shadow:none}
|
select:focus,textarea:focus,input:focus{border-color:var(--color-accent-hover);box-shadow:none}
|
||||||
textarea:invalid,input:invalid{box-shadow:none}
|
textarea:invalid,input:invalid{box-shadow:none}
|
||||||
textarea:invalid:focus,input:invalid:focus{border-right-width:1em}
|
textarea:invalid:focus,input:invalid:focus{border-right-width:1em}
|
||||||
textarea{min-height:30vh}
|
textarea{min-height:30vh}
|
||||||
table{width:100%;max-height:60vh;margin:2em 0;border-collapse:collapse;overflow:auto}
|
table{width:100%;max-height:60vh;margin:2em 0;border-collapse:collapse;overflow:auto}
|
||||||
th{padding:.3em.5em;text-align:left;border-bottom:2px solid #424242}
|
th{padding:.3em.5em;text-align:left;border-bottom:2px solid var(--color-fg-less)}
|
||||||
tbody>tr:nth-child(2n+1){background:#F2F2F2}
|
tbody>tr:nth-child(2n+1){background:var(--color-bg-raised2)}
|
||||||
td{padding:.3em.5em;border-bottom:1px solid #D6D6D6}
|
td{padding:.3em.5em;border-bottom:1px solid var(--color-borders)}
|
||||||
body>div.uploader{background:#F2F2F2;border:2px solid #EEE;padding:1em 1em}
|
body>div.uploader{background:var(--color-bg-raised2);border:2px solid var(--color-bg-raised);padding:1em 1em}
|
||||||
ul.fieldList{display:block;margin:.5em 0;padding:0 0;list-style:none}
|
ul.fieldList{display:block;margin:.5em 0;padding:0 0;list-style:none}
|
||||||
ul.fieldList>li{display:inline-block;padding:.3em;margin:.1em;border:1px solid #D6D6D6;background:#FAFAFA;cursor:pointer;transition:background.4s,border.4s,box-shadow.4s}
|
ul.fieldList>li{display:inline-block;padding:.3em;margin:.1em;border:1px solid var(--color-borders);background:var(--color-bg-raised2);cursor:pointer;transition:background.4s,border.4s,box-shadow.4s}
|
||||||
ul.fieldList>li:hover{background:#FFF;border-color:#212121}
|
ul.fieldList>li:hover{background:var(--color-bg-normal);border-color:var(--color-fg-less)}
|
||||||
ul.fieldList>li.requiredField:before{display:inline-block;content:" \002612 ";margin-right:.5em}
|
ul.fieldList>li.requiredField:before{display:inline-block;content:" \002612 ";margin-right:.5em}
|
||||||
ul.fieldList>li.humanTLToggled{border-color:#CB9B8C;background:#CB9B8C;box-shadow:0 8px 6px-6px black}.options>a.buttonLike{display:inline-block;width:auto;margin:.15em 0;padding:.4em.5em;text-transform:inherit;cursor:pointer;background:initial;border:2px solid #D6D6D6;opacity:1;transition:background.4s,opacity.4s}.options>a.buttonLike:hover{background:#D6D6D6}.actionList{margin:1em 0 1em 1em;padding:.5em 0}.actionList>li a{display:inline-block;padding:.3em;border-radius:.3em;transition:background.4s}.actionList>li a:hover{background:#CB9B8C}
|
ul.fieldList>li.humanTLToggled{border-color:var(--color-accent-hover);background:var(--color-accent-hover);box-shadow:0 8px 6px-6px black}.options>a.buttonLike{display:inline-block;width:auto;margin:.15em 0;padding:.4em.5em;text-transform:inherit;cursor:pointer;background:initial;border:2px solid var(--color-borders);opacity:1;transition:background.4s,opacity.4s}.options>a.buttonLike:hover{background:var(--color-borders)}.actionList{margin:1em 0 1em 1em;padding:.5em 0}.actionList>li a{display:inline-block;padding:.3em;border-radius:.3em;transition:background.4s}.actionList>li a:hover{background:var(--color-accent-hover)}.loading:before,.loading:after{content:" ";display:block;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);height:64px;width:64px;margin:0;padding:0;border-radius:50%;border:8px solid var(--color-accent-hover);border-color:var(--color-accent-hover)transparent transparent transparent;z-index:100;animation:rotating 1.2s cubic-bezier(0.5,0,0.5,1)infinite;animation-delay:-0.15s}.loading:after{animation-delay:-0.45s}
|
||||||
|
@keyframes rotating{from{transform:rotate(0deg)}
|
||||||
|
to{transform:rotate(360deg)}}
|
||||||
@media(prefers-color-scheme:dark){body{background:#263238;color:#ECEFF1}
|
@media(prefers-color-scheme:dark){body{background:#263238;color:#ECEFF1}
|
||||||
input,select,textarea{background:inherit;color:inherit;border:3px solid #37474F;transition:border.4s,box-shadow.4s}
|
input,select,textarea{background:inherit;color:inherit;border:3px solid #37474F;transition:border.4s,box-shadow.4s}
|
||||||
a.buttonLike:focus,.options>a.buttonLike:focus,button:focus,select:focus,textarea:focus,input:focus,textarea:active,input:active,a.buttonLike:hover,.options>a.buttonLike:hover,ul.fieldList>li:hover,button:hover,select:hover,textarea:hover,input:hover{background:inherit;border-color:#FFF;box-shadow:initial;border-radius:.2em}
|
a.buttonLike:focus,.options>a.buttonLike:focus,button:focus,select:focus,textarea:focus,input:focus,textarea:active,input:active,a.buttonLike:hover,.options>a.buttonLike:hover,ul.fieldList>li:hover,button:hover,select:hover,textarea:hover,input:hover{background:inherit;border-color:#FFF;box-shadow:initial;border-radius:.2em}
|
||||||
|
|
|
@ -380,7 +380,7 @@ class CsvxmlPage {
|
||||||
csvBySelectionButton;
|
csvBySelectionButton;
|
||||||
unsetSelectionButton;
|
unsetSelectionButton;
|
||||||
|
|
||||||
constructor(fieldList) {
|
constructor(fieldList, tls) {
|
||||||
this.fieldList = Object.freeze(fieldList);
|
this.fieldList = Object.freeze(fieldList);
|
||||||
|
|
||||||
let list = {};
|
let list = {};
|
||||||
|
@ -389,7 +389,7 @@ class CsvxmlPage {
|
||||||
}
|
}
|
||||||
this.fieldListFlat = Object.freeze(list);
|
this.fieldListFlat = Object.freeze(list);
|
||||||
|
|
||||||
this.tls = Object.freeze(JSON.parse(document.body.getAttribute("data-tls")));
|
this.tls = Object.freeze(tls);
|
||||||
|
|
||||||
let domUploaderWrapper = document.createElement("div");
|
let domUploaderWrapper = document.createElement("div");
|
||||||
domUploaderWrapper.id = "uploader";
|
domUploaderWrapper.id = "uploader";
|
||||||
|
@ -587,6 +587,29 @@ class CsvxmlPage {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderHeader() {
|
||||||
|
|
||||||
|
const header = document.createElement("header");
|
||||||
|
|
||||||
|
const h1 = document.createElement("h1");
|
||||||
|
|
||||||
|
const img = document.createElement("img");
|
||||||
|
img.width = "70";
|
||||||
|
img.height = "70";
|
||||||
|
img.src = "assets/img/mdlogo-csvxml.svg";
|
||||||
|
img.alt = "";
|
||||||
|
h1.appendChild(img);
|
||||||
|
|
||||||
|
const h1Span = document.createElement("span");
|
||||||
|
h1Span.textContent = "museum-digital:csvxml";
|
||||||
|
h1.appendChild(h1Span);
|
||||||
|
|
||||||
|
header.appendChild(h1);
|
||||||
|
|
||||||
|
document.body.appendChild(header);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
renderUploader() {
|
renderUploader() {
|
||||||
|
|
||||||
let app = this;
|
let app = this;
|
||||||
|
@ -853,19 +876,60 @@ class CsvxmlPage {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.body.classList.add("loading");
|
(async function() {
|
||||||
|
|
||||||
window.fetch('/?output=json', {
|
function getLang() {
|
||||||
method: 'GET', cache: 'no-cache',
|
|
||||||
credentials: 'same-origin',
|
|
||||||
}).then(function(response) {
|
|
||||||
return response.json();
|
|
||||||
}).then(function(elements) {
|
|
||||||
|
|
||||||
document.body.classList.remove("loading");
|
const allowedLangs = document.documentElement.getAttribute("data-allowed-langs").split(',');
|
||||||
|
|
||||||
const page = new CsvxmlPage(elements);
|
if (navigator.language === undefined) return 'en';
|
||||||
page.renderUploader();
|
|
||||||
page.renderMain();
|
|
||||||
|
|
||||||
});
|
const browserLang = navigator.language.toLowerCase().substr(0, 2);
|
||||||
|
console.log(browserLang);
|
||||||
|
|
||||||
|
if (allowedLangs.includes(browserLang)) return browserLang;
|
||||||
|
else return 'en';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const lang = getLang();
|
||||||
|
|
||||||
|
document.body.classList.add("loading");
|
||||||
|
|
||||||
|
let loaded = 0;
|
||||||
|
|
||||||
|
let fieldList;
|
||||||
|
let tls;
|
||||||
|
|
||||||
|
function loadPage() {
|
||||||
|
|
||||||
|
document.body.classList.remove("loading");
|
||||||
|
|
||||||
|
const page = new CsvxmlPage(fieldList, tls);
|
||||||
|
page.renderHeader();
|
||||||
|
page.renderUploader();
|
||||||
|
page.renderMain();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
window.fetch('/json/fields.' + lang + '.json', {
|
||||||
|
method: 'GET', cache: 'no-cache', credentials: 'same-origin',
|
||||||
|
}).then(function(response) {
|
||||||
|
return response.json();
|
||||||
|
}).then(function(elements) {
|
||||||
|
fieldList = elements;
|
||||||
|
loaded++;
|
||||||
|
if (loaded === 2) loadPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.fetch('/json/tls.' + lang + '.json', {
|
||||||
|
method: 'GET', cache: 'no-cache', credentials: 'same-origin',
|
||||||
|
}).then(function(response) {
|
||||||
|
return response.json();
|
||||||
|
}).then(function(elements) {
|
||||||
|
tls = elements;
|
||||||
|
loaded++;
|
||||||
|
if (loaded === 2) loadPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
66
public/assets/js/csvxmlV2.min.js
vendored
Normal file
66
public/assets/js/csvxmlV2.min.js
vendored
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
"use strict";class CsvxmlValidator{fieldList;toValidate;errors;constructor(fieldList,csvRaw){this.errors={parsing:[],mandatoryTags:[],duplicateInvNos:[],dependentColumns:[],controlledLists:[],mainImageResource:[],};this.fieldList=Object.freeze(fieldList);const lines=csvRaw.trim().replace("\r\n","\n").split("\n");let separator;let delimiter;if(csvRaw.substr(0,1)==='"'){separator='";"';delimiter='"'}else{separator=';';delimiter=''}
|
||||||
|
let headersFields=lines.shift();let headers;if(delimiter==="")headers=headersFields.split(separator);else headers=headersFields.substr(1,headersFields.length-2).split(separator);let expectedFieldCount=headers.length;let toValidate=[];let lineCounter=1;for(let line of lines){if(delimiter!==''){line=line.substr(1,line.length-2)}
|
||||||
|
if(line.length<=headers.length)continue;let lineContents={};let fields=line.split(separator);if(fields.length!==headers.length){this.errors.parsing.push("Number of columns in line "+lineCounter+" does not match number of headers")}
|
||||||
|
for(let i=0,max=fields.length;i<max;i++){if(headers[i]===undefined||headers[i]===null){this.errors.parsing.push("ERROR parsing line "+lineCounter+"; column "+i);continue}
|
||||||
|
lineContents[headers[i]]=fields[i]}
|
||||||
|
if(Object.values(lineContents).join("").length===0)continue;toValidate.push(lineContents);lineCounter++}
|
||||||
|
this.toValidate=toValidate;if(toValidate.length===0){alert("Error: No lines of content identified")}
|
||||||
|
this.validate()}
|
||||||
|
validate(){this.validateMandatoryTagsPresent();this.checkDuplicateInvNos();this.checkDependentColumns();this.checkControlledLists()}
|
||||||
|
validateMandatoryTagsPresent(){let mandatoryFields=[];for(let fieldName in this.fieldList){if(this.fieldList[fieldName].required===!0){mandatoryFields.push(fieldName)}}
|
||||||
|
console.log(this.toValidate);let lineCounter=1;for(let line of this.toValidate){for(let mandatoryField of mandatoryFields){if(line[mandatoryField]===undefined||line[mandatoryField]===null||line[mandatoryField]===''){this.errors.mandatoryTags.push("Missing or empty mandatory tag "+mandatoryField+" on line "+lineCounter)}}
|
||||||
|
lineCounter++}}
|
||||||
|
checkDuplicateInvNos(){let invNoEncountered=[];let lineCounter=1;for(let line of this.toValidate){if(invNoEncountered.includes(line.inventory_number)){this.errors.duplicateInvNos.push("Duplicate inventory number "+line.inventory_number+" on line "+lineCounter)}
|
||||||
|
invNoEncountered.push(line.inventory_number);lineCounter++}}
|
||||||
|
checkDependentColumns(){const headers=Object.keys(this.toValidate[0]);for(let header of headers){if(this.fieldList[header]===undefined||this.fieldList[header].dependsOn===undefined||this.fieldList[header].dependsOn===null)continue;let dependencies=this.fieldList[header].dependsOn;for(let dep of dependencies){if(headers.includes(dep)===!1){this.errors.dependentColumns.push("Dependency issue at column "+header+": Corresponding column "+dep+" is missing")}}}}
|
||||||
|
checkControlledLists(){let lineCounter=1;for(let line of this.toValidate){for(let fieldName in line){if(this.fieldList[fieldName]===undefined){console.log("Undefined but requested field "+fieldName);continue}
|
||||||
|
let allowedValues=this.fieldList[fieldName].allowedValues;if(allowedValues===undefined||allowedValues===null)continue;if(Object.values(allowedValues).length===0||Object.values(allowedValues).includes(line[fieldName])){continue}
|
||||||
|
this.errors.controlledLists.push("Disallowed value used for column "+fieldName+" at line "+lineCounter+" (Allowed values are: "+Object.values(allowedValues).join(", ")+")")}
|
||||||
|
lineCounter++}}
|
||||||
|
checkMainImageResource(){}
|
||||||
|
isValid(){for(let errorClass in this.errors){if(this.errors[errorClass].length!==0)return!1}
|
||||||
|
return!0}
|
||||||
|
generateXml(){let output=[];let xmlDoc=document.implementation.createDocument(null,"record");for(let line of this.toValidate){let root=xmlDoc.createElement("record");for(let fieldName in line){const elem=xmlDoc.createElement(fieldName);elem.textContent=line[fieldName];root.appendChild(elem)}
|
||||||
|
output.push(root)}
|
||||||
|
return output}}
|
||||||
|
class CsvxmlTooltip{static getDirection(e,elem){if(window.innerHeight<e.clientY+elem.clientHeight){elem.style.top="";elem.style.bottom=(window.innerHeight-e.clientY)+"px"}else{elem.style.bottom="";elem.style.top=(e.clientY+4)+"px"}
|
||||||
|
if(window.innerWidth<e.clientX+elem.clientWidth){elem.style.left="";elem.style.right=(window.innerWidth-e.clientX)+"px"}else{elem.style.right="";elem.style.left=(e.clientX+3)+"px"}}
|
||||||
|
static positionMobile(newMain){if(window.matchMedia&&window.matchMedia('(max-width:75em)').matches){newMain.style.left="";newMain.style.right="";newMain.style.top="";newMain.style.bottom="";if(newMain.classList.contains("atBottom")===!1){newMain.classList.add("atBottom")}}}
|
||||||
|
static triggerMouseMove(e){const newMain=document.getElementById("newToolTipMain");if(newMain===undefined||newMain===null)return;CsvxmlTooltip.getDirection(e,newMain);CsvxmlTooltip.positionMobile(newMain)}
|
||||||
|
static triggerMouseOut(e){const newMain=document.getElementById("newToolTipMain");if(newMain!==undefined&&newMain!==null){newMain.classList.remove("visible");document.body.removeChild(newMain)}
|
||||||
|
e.target.removeEventListener('mouseout',CsvxmlTooltip.triggerMouseOut)}
|
||||||
|
static bindTooltipToElement(elem,tooltipTitle,tooltipContent){elem.addEventListener('mouseover',function(e){let newMain=document.getElementById("newToolTipMain");if(newMain!==null)return;newMain=document.createElement("div");newMain.classList.add("newToolTip");newMain.id="newToolTipMain";newMain.setAttribute("data-title",tooltipTitle);newMain.appendChild(tooltipContent);document.body.appendChild(newMain);newMain.classList.add("visible");CsvxmlTooltip.getDirection(e,newMain);CsvxmlTooltip.positionMobile(newMain);newMain.addEventListener("mouseout",function(e){const newMain=document.getElementById("newToolTipMain");if(newMain!==undefined&&newMain!==null){newMain.classList.remove("visible");document.body.removeChild(newMain)}});elem.addEventListener("mouseout",CsvxmlTooltip.triggerMouseOut)},{passive:!0});elem.addEventListener("mousemove",CsvxmlTooltip.triggerMouseMove,{passive:!0})}}
|
||||||
|
class CsvxmlDialogue{static closeDialogue(e){if(e!==undefined){e.preventDefault();e.stopPropagation()}
|
||||||
|
let dialogueArea=document.getElementById("dialogueArea");if(dialogueArea!==null&&dialogueArea!==!1){while(dialogueArea.firstChild){dialogueArea.removeChild(dialogueArea.firstChild)}
|
||||||
|
dialogueArea.parentElement.removeChild(dialogueArea);document.removeEventListener('keydown',CsvxmlDialogue.closeDialogueByEscape,!1)}}
|
||||||
|
static closeDialogueByEscape(e){if(e.keyCode===27){CsvxmlDialogue.closeDialogue(e)}}
|
||||||
|
static drawDialogue(contents){let dialogueArea=document.createElement("div");dialogueArea.id="dialogueArea";let dialogue=document.createElement("div");dialogue.id="dialogue";dialogue.appendChild(contents);dialogueArea.appendChild(dialogue);document.body.appendChild(dialogueArea);document.addEventListener('keydown',CsvxmlDialogue.closeDialogueByEscape);return dialogue}}
|
||||||
|
class CsvxmlPage{fieldList;fieldListFlat;tls;domUploaderWrapper;domMainWrapper;selectedFields;csvBySelectionButton;unsetSelectionButton;constructor(fieldList,tls){this.fieldList=Object.freeze(fieldList);let list={};for(let sectionName in fieldList){list=Object.assign(list,fieldList[sectionName])}
|
||||||
|
this.fieldListFlat=Object.freeze(list);this.tls=Object.freeze(tls);let domUploaderWrapper=document.createElement("div");domUploaderWrapper.id="uploader";domUploaderWrapper.classList.add("uploader");this.domUploaderWrapper=domUploaderWrapper;let domMainWrapper=document.createElement("main");this.domMainWrapper=domMainWrapper;this.selectedFields=[]}
|
||||||
|
generateCsv(selectedFields=[]){let line1=[];let line2=[];let line3=[];for(let fieldName in this.fieldListFlat){console.log(fieldName);console.log(selectedFields);if(selectedFields.length!==0&&selectedFields.includes(fieldName)===!1)continue;const field=this.fieldListFlat[fieldName];line1.push(fieldName);line2.push(field.name_human_readable);if(field.allowedValues!==undefined){let values=[];for(let key in field.allowedValues)values.push(field.allowedValues[key]);line3.push(values.join(","))}else line3.push("");}
|
||||||
|
const csvLine1='"'+line1.join('";"')+'"';const csvLine2='"'+line2.join('";"')+'"';const csvLine3='"'+line3.join('";"')+'"';const toStore=csvLine1+"\n"+csvLine2+"\n"+csvLine3;const triggerLink=document.createElement('a');triggerLink.setAttribute('href','data:text/plain;charset=utf-8,'+encodeURIComponent(toStore));triggerLink.setAttribute('download',"csvxml_museum-digital_template.csv");triggerLink.style.display='none';document.body.appendChild(triggerLink);triggerLink.click();document.body.removeChild(triggerLink)}
|
||||||
|
zipUploadToXml(validator){function runZipping(){let zip=new JSZip();let xmlFiles=validator.generateXml();const serializer=new XMLSerializer();let lineCounter=0;for(let xml of xmlFiles){zip.file(lineCounter+".xml",serializer.serializeToString(xml));lineCounter++}
|
||||||
|
zip.generateAsync({type:"blob"}).then(function(content){const triggerLink=document.createElement('a');triggerLink.href=window.URL.createObjectURL(content);triggerLink.setAttribute('download',"csvxml.zip");triggerLink.style.display='none';document.body.appendChild(triggerLink);triggerLink.click();document.body.removeChild(triggerLink)})}
|
||||||
|
if(typeof JSZip==="undefined"){const loadScript=document.createElement("script");loadScript.setAttribute("src","assets/js/jszip/dist/jszip.min.js");loadScript.addEventListener('load',function(){runZipping()},{passive:!0,once:!0});document.body.appendChild(loadScript)}else{runZipping()}}
|
||||||
|
listValidationErrors(validator){const dialogueContent=document.createElement("div");const headline=document.createElement("h3");headline.textContent=this.tls.validation_errors;const cancelB=document.createElement("a");cancelB.classList.add("icons");cancelB.classList.add("iconsClose");cancelB.classList.add("dialogueCloseX");cancelB.id="dialogueClose";cancelB.textContent="X";cancelB.title="Close";cancelB.href="#"+location.href;cancelB.addEventListener('click',CsvxmlDialogue.closeDialogue);headline.appendChild(cancelB);dialogueContent.appendChild(headline);const domErrorsSection=document.createElement("div");for(let errorType in validator.errors){if(validator.errors[errorType].length===0)continue;const ulHl=document.createElement("h4");ulHl.textContent=this.tls['errors_'+errorType]+" ("+validator.errors[errorType].length+")";ulHl.style.cursor="pointer";domErrorsSection.appendChild(ulHl);const ul=document.createElement("ul");for(let error of validator.errors[errorType]){const li=document.createElement("li");li.textContent=error;ul.appendChild(li)}
|
||||||
|
ulHl.addEventListener('click',function(){ul.classList.toggle("minimized")});domErrorsSection.appendChild(ul)}
|
||||||
|
dialogueContent.appendChild(domErrorsSection);const domDlSection=document.createElement("div");const domDlA=document.createElement("span");domDlA.textContent=this.tls.download;domDlA.classList.add("buttonLike");let app=this;domDlA.addEventListener('click',function(){app.zipUploadToXml(validator)});domDlSection.appendChild(domDlA);dialogueContent.appendChild(domDlSection);dialogue=CsvxmlDialogue.drawDialogue(dialogueContent)}
|
||||||
|
uploadFileForValidation(file){const reader=new FileReader();reader.readAsText(file);let app=this;reader.onload=function(){console.log("Read file");let validator=new CsvxmlValidator(app.fieldListFlat,reader.result);if(validator.isValid()===!0){alert("Document is valid. Press ok to download.");app.zipUploadToXml(validator)}else{app.listValidationErrors(validator)}};reader.onerror=function(){alert(reader.error)}}
|
||||||
|
renderHeader(){const header=document.createElement("header");const h1=document.createElement("h1");const img=document.createElement("img");img.width="70";img.height="70";img.src="assets/img/mdlogo-csvxml.svg";img.alt="";h1.appendChild(img);const h1Span=document.createElement("span");h1Span.textContent="museum-digital:csvxml";h1.appendChild(h1Span);header.appendChild(h1);document.body.appendChild(header)}
|
||||||
|
renderUploader(){let app=this;(async function(){const form=document.createElement("form");const label=document.createElement("label");label.textContent=app.tls.select_csv_file_for_upload;label.setAttribute("for","fileToUpload");form.appendChild(label);const input=document.createElement("input");input.type="file";input.id="fileToUpload";input.accept=".csv";input.required="required";input.addEventListener('change',async function(){app.uploadFileForValidation(input.files[0])});form.appendChild(input);app.domUploaderWrapper.appendChild(form)})();document.body.appendChild(this.domUploaderWrapper)}
|
||||||
|
doForFieldList(callback){let fieldLists=document.getElementsByClassName("fieldList");for(let i=0,max=fieldLists.length;i<max;i++){let fields=fieldLists[i].getElementsByTagName("li");for(let j=0,maxj=fields.length;j<maxj;j++){callback(fields[j])}}}
|
||||||
|
toggleListFieldSelectionState(field){let app=this;let newValue=field.getAttribute("data-alt");field.setAttribute("data-alt",field.textContent);field.textContent=newValue;field.classList.toggle("humanTLToggled");if(field.classList.contains("humanTLToggled")===!1)return;let dependencies=this.fieldListFlat[field.id].dependsOn;if(dependencies!==undefined&&dependencies!==null){let linkedFields=this.fieldListFlat[field.id].dependsOn;for(let i=0,max=linkedFields.length;i<max;i++){let linkedField=document.getElementById(linkedFields[i]);if(linkedField.classList.contains("humanTLToggled")===!0)continue;this.toggleListFieldSelectionState(linkedField)}}}
|
||||||
|
checkCSVBySelectionAccessibility(){let selected=document.getElementsByClassName("humanTLToggled");if(selected.length===0){this.csvBySelectionButton.classList.add("invisible");this.unsetSelectionButton.classList.add("invisible")}else{this.csvBySelectionButton.classList.remove("invisible");this.unsetSelectionButton.classList.remove("invisible")}}
|
||||||
|
getOptionsSection(){function genButton(id,text,link=""){const output=document.createElement("a");output.id=id;output.textContent=text;output.classList.add("buttonLike");if(link!=="")output.href=link;return output}
|
||||||
|
const options=document.createElement("div");options.classList.add("options");const app=this;const dlAllButton=genButton("dlAll",this.tls.download_csv_all);dlAllButton.cursor="pointer";dlAllButton.addEventListener('click',function(){app.generateCsv()});options.appendChild(dlAllButton);this.csvBySelectionButton=genButton("csvBySelection",this.tls.download_csv_by_selection);this.csvBySelectionButton.classList.add("invisible");options.appendChild(this.csvBySelectionButton);const optionSelectRequired=genButton("selectRequired",this.tls.select_required_fields);options.appendChild(optionSelectRequired);const optionSelectAll=genButton("selectAll",this.tls.select_all_fields);options.appendChild(optionSelectAll);this.unsetSelectionButton=genButton("unsetSelection",this.tls.unset_selection);this.unsetSelectionButton.classList.add("invisible");options.appendChild(this.unsetSelectionButton);this.csvBySelectionButton.addEventListener('click',function(e){let selected=document.getElementsByClassName("humanTLToggled");let selectedFields=[];for(let i=0,max=selected.length;i<max;i++){selectedFields+=selected[i].getAttribute("data-value")}
|
||||||
|
app.generateCsv(selectedFields)});optionSelectRequired.addEventListener('click',function(e){app.doForFieldList(function(field){if(field.classList.contains("requiredField")===!1)return;if(field.classList.contains("humanTLToggled")===!0)return;app.toggleListFieldSelectionState(field);app.checkCSVBySelectionAccessibility()})});optionSelectAll.addEventListener('click',function(e){app.doForFieldList(function(field){if(field.classList.contains("humanTLToggled")===!0)return;app.toggleListFieldSelectionState(field);app.checkCSVBySelectionAccessibility()})});this.unsetSelectionButton.addEventListener('click',function(e){app.doForFieldList(function(field){if(field.classList.contains("humanTLToggled")===!1)return;app.toggleListFieldSelectionState(field);app.checkCSVBySelectionAccessibility()})});return options}
|
||||||
|
renderMain(){const domH2=document.createElement("h2");domH2.textContent=this.tls.currently_approved_tags;this.domMainWrapper.appendChild(domH2);this.domMainWrapper.appendChild(this.getOptionsSection());for(let sectionName in this.fieldList){const domDiv=document.createElement("div");const domH3=document.createElement("h3");domH3.textContent=sectionName;domDiv.appendChild(domH3);const domUl=document.createElement("ul");domUl.classList.add("fieldList");const sectionFields=this.fieldList[sectionName];for(let fieldName in sectionFields){const field=sectionFields[fieldName];const domLi=document.createElement("li");domLi.textContent=fieldName;domLi.id=fieldName;domLi.setAttribute("data-alt",field.name_human_readable)
|
||||||
|
domLi.setAttribute("data-value",fieldName)
|
||||||
|
if(field.required===!0)domLi.classList.add("requiredField");domUl.appendChild(domLi);const tooltipContent=document.createElement("div");const explicaP=document.createElement("p");explicaP.textContent=field.explica;tooltipContent.appendChild(explicaP);if(field.remarks!==undefined&&field.remarks!==''){const remarkHl=document.createElement("h4");remarkHl.textContent=this.tls.remarks;tooltipContent.appendChild(remarkHl)
|
||||||
|
const remarkCont=document.createElement("p");remarkCont=field.remarks;tooltipContent.appendChild(remarkCont)}
|
||||||
|
if(field.allowedValues!==undefined&&Object.values(field.allowedValues).length!==0){const allowedHl=document.createElement("h4");allowedHl.textContent=this.tls.allowed_values;tooltipContent.appendChild(allowedHl);const allowedList=document.createElement("p");allowedList.textContent=Object.values(field.allowedValues).join(', ');tooltipContent.appendChild(allowedList)}
|
||||||
|
CsvxmlTooltip.bindTooltipToElement(domLi,field.name_human_readable,tooltipContent)}
|
||||||
|
domDiv.appendChild(domUl);this.domMainWrapper.appendChild(domDiv)}
|
||||||
|
document.body.appendChild(this.domMainWrapper);let app=this;this.doForFieldList(function(field){field.addEventListener('click',function(e){app.toggleListFieldSelectionState(field);app.checkCSVBySelectionAccessibility()})})}}(async function(){function getLang(){const allowedLangs=document.documentElement.getAttribute("data-allowed-langs").split(',');if(navigator.language===undefined)return'en';const browserLang=navigator.language.toLowerCase().substr(0,2);console.log(browserLang);if(allowedLangs.includes(browserLang))return browserLang;else return'en'}
|
||||||
|
const lang=getLang();document.body.classList.add("loading");let loaded=0;let fieldList;let tls;function loadPage(){document.body.classList.remove("loading");const page=new CsvxmlPage(fieldList,tls);page.renderHeader();page.renderUploader();page.renderMain()}
|
||||||
|
window.fetch('/json/fields.'+lang+'.json',{method:'GET',cache:'no-cache',credentials:'same-origin',}).then(function(response){return response.json()}).then(function(elements){fieldList=elements;loaded++;if(loaded===2)loadPage();});window.fetch('/json/tls.'+lang+'.json',{method:'GET',cache:'no-cache',credentials:'same-origin',}).then(function(response){return response.json()}).then(function(elements){tls=elements;loaded++;if(loaded===2)loadPage();})})()
|
23
public/index.htm
Normal file
23
public/index.htm
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html data-allowed-langs="de,en,hu">
|
||||||
|
<head>
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
|
||||||
|
<meta name="description" content="Validate import CSV files for museum-digital" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="assets/css/csvxml.css" />
|
||||||
|
<meta name="theme-color" content="#aa4400" />
|
||||||
|
<link rel="shortcut icon" sizes="128x128" href="assets/img/mdlogo-csvxml.svg" />
|
||||||
|
<meta name="robots" content="noindex" />
|
||||||
|
|
||||||
|
<title>CSVXML :: museum-digital</title>
|
||||||
|
|
||||||
|
<meta name="keywords" content="Imports, museum-digital" />
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="loading">
|
||||||
|
|
||||||
|
<script src="assets/js/csvxmlV2.js" type="text/javascript" async></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,85 +0,0 @@
|
||||||
<?PHP
|
|
||||||
/**
|
|
||||||
* New start page for CSVXML.
|
|
||||||
*
|
|
||||||
* @link https://groupit.museum-digital.de/csvxml/
|
|
||||||
*
|
|
||||||
* @author Joshua Ramon Enslin <joshua@museum-digital.de>
|
|
||||||
*/
|
|
||||||
declare(strict_types = 1);
|
|
||||||
require_once __DIR__ . "/../functions/functions.php";
|
|
||||||
|
|
||||||
$allowed_langs = ['ar', 'de', 'en', 'hu', 'id', 'it', 'pl', 'pt'];
|
|
||||||
$lang = MD_STD::get_user_lang($allowed_langs, "en");
|
|
||||||
$tlLoader = new MDTlLoader("csxml_start_v2", $lang);
|
|
||||||
|
|
||||||
$outFormat = MD_STD_IN::get_http_input_text("output", "html", ['html', 'json']);
|
|
||||||
|
|
||||||
if ($outFormat === 'json') {
|
|
||||||
|
|
||||||
$fieldsGetter = new CsvxmlAvailableFields($lang);
|
|
||||||
$availableFields = $fieldsGetter->getFields();
|
|
||||||
|
|
||||||
header('Cache-Control: no-cache, no-store, must-revalidate, max-age=0'); // HTTP/1.1
|
|
||||||
header('Pragma: no-cache'); // HTTP/1.0
|
|
||||||
header("Access-Control-Allow-Origin: *");
|
|
||||||
header("Access-Control-Allow-Methods: GET");
|
|
||||||
header("Access-Control-Allow-Headers: X-PINGOTHER, Content-Type, Accept-Encoding, cache-control");
|
|
||||||
header("Access-Control-Max-Age: 86400");
|
|
||||||
header('content-type: application/json');
|
|
||||||
echo json_encode($availableFields);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
echo '
|
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
|
|
||||||
<meta name="description" content="Validate import CSV files for museum-digital" />
|
|
||||||
<link rel="stylesheet" type="text/css" href="assets/css/csvxml.css" />
|
|
||||||
<meta name="theme-color" content="#aa4400" />
|
|
||||||
<link rel="shortcut icon" sizes="128x128" href="assets/img/mdlogo-csvxml.svg" />
|
|
||||||
<meta name="robots" content="noindex" />
|
|
||||||
|
|
||||||
<title>CSVXML :: museum-digital</title>
|
|
||||||
|
|
||||||
<meta name="keywords" content="Imports, museum-digital" />
|
|
||||||
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body class="loading" data-tls="' . htmlspecialchars(MD_STD::json_encode([
|
|
||||||
'remarks' => $tlLoader->tl('basis', 'basis', 'remarks'),
|
|
||||||
'download' => $tlLoader->tl('export', 'export', 'download'),
|
|
||||||
'upload' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'upload'),
|
|
||||||
'select_csv_file_for_upload' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'select_csv_file_for_upload'),
|
|
||||||
'currently_approved_tags' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'currently_approved_tags'),
|
|
||||||
'download_csv_all' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'download_csv_all'),
|
|
||||||
'download_csv_by_selection' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'download_csv_by_selection'),
|
|
||||||
'select_required_fields' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'select_required_fields'),
|
|
||||||
'select_all_fields' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'select_all_fields'),
|
|
||||||
'unset_selection' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'unset_selection'),
|
|
||||||
'file_format' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'file_format'),
|
|
||||||
'validation_errors' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'validation_errors'),
|
|
||||||
'errors_parsing' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'errors_parsing'),
|
|
||||||
'errors_mandatoryTags' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'errors_mandatoryTags'),
|
|
||||||
'errors_duplicateInvNos' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'errors_duplicateInvNos'),
|
|
||||||
'errors_dependentColumns' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'errors_dependentColumns'),
|
|
||||||
'errors_controlledLists' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'errors_controlledLists'),
|
|
||||||
'errors_mainImageResource' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'errors_mainImageResource'),
|
|
||||||
'allowed_values' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'allowed_values'),
|
|
||||||
])) . '">
|
|
||||||
|
|
||||||
<h1>
|
|
||||||
<img src="assets/img/mdlogo-csvxml.svg" alt="" />
|
|
||||||
<span>museum-digital:csvxml</span>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<script src="assets/js/csvxmlV2.js" type="text/javascript" async></script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>';
|
|
||||||
|
|
1
public/json/fields.de.json
Normal file
1
public/json/fields.de.json
Normal file
File diff suppressed because one or more lines are too long
1
public/json/fields.en.json
Normal file
1
public/json/fields.en.json
Normal file
File diff suppressed because one or more lines are too long
1
public/json/fields.hu.json
Normal file
1
public/json/fields.hu.json
Normal file
File diff suppressed because one or more lines are too long
1
public/json/tls.de.json
Normal file
1
public/json/tls.de.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"remarks":"Notizen","download":"Download","upload":"Hochladen","select_csv_file_for_upload":"Bitte w\u00e4hlen Sie eine CSV Datei als Basis zum Erstellen von XML Dateien","currently_approved_tags":"Derzeit zum Import verf\u00fcgbare Tags \/ Felder","download_csv_all":"CSV-Vorlage mit allen Feldern runterladen","download_csv_by_selection":"CSV-Vorlage auf Basis der Auswahl herunterladen","select_required_fields":"Pflichtfelder ausw\u00e4hlen","select_all_fields":"Alle Felder ausw\u00e4hlen","unset_selection":"Auswahl entfernen","file_format":"Dateiformat","validation_errors":"Validierungsfehler","errors_parsing":"Parsing-Fehler","errors_mandatoryTags":"Fehlende Pflichtfelder","errors_duplicateInvNos":"Doppelte Inventarnummern","errors_dependentColumns":"Spaltenabh\u00e4ngigkeiten nicht ber\u00fccksichtigt","errors_controlledLists":"Kontrollierte Werte","errors_mainImageResource":"Fehlende Haupt-Bilder oder Ressourcen","allowed_values":"Zugelassene Werte"}
|
1
public/json/tls.en.json
Normal file
1
public/json/tls.en.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"remarks":"Remarks","download":"Download","upload":"Upload","select_csv_file_for_upload":"Please select a CSV file to create XML files","currently_approved_tags":"Currently approved tags (column names) for md:import","download_csv_all":"Download CSV template with all fields","download_csv_by_selection":"Download CSV template based on selection","select_required_fields":"Select required fields","select_all_fields":"Select all fields","unset_selection":"Unset selection","file_format":"File format","validation_errors":"Validation errors","errors_parsing":"Parse errors","errors_mandatoryTags":"Missing mandatory tags","errors_duplicateInvNos":"Duplicate inventory numbers","errors_dependentColumns":"Column dependencies unresolved","errors_controlledLists":"Controlled lists","errors_mainImageResource":"Missing main images or resources","allowed_values":"Allowed values"}
|
1
public/json/tls.hu.json
Normal file
1
public/json/tls.hu.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"remarks":"Megjegyz\u00e9sek","download":"Let\u00f6lt\u00e9s","upload":"Felt\u00f6lt\u00e9s","select_csv_file_for_upload":"Please select a CSV file to create XML files","currently_approved_tags":"Jelenleg j\u00f3v\u00e1hagyott c\u00edmk\u00e9k (oszlopnevek) az md:importhoz","download_csv_all":"Download CSV template with all fields","download_csv_by_selection":"Download CSV template based on selection","select_required_fields":"K\u00f6telez\u0151 mez\u0151k kijel\u00f6l\u00e9se","select_all_fields":"\u00d6sszes mez\u0151 kijel\u00f6l\u00e9s","unset_selection":"Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se","file_format":"Kiterjeszt\u00e9s","validation_errors":"Validation errors","errors_parsing":"Parse errors","errors_mandatoryTags":"Missing mandatory tags","errors_duplicateInvNos":"Duplicate inventory numbers","errors_dependentColumns":"Column dependencies unresolved","errors_controlledLists":"Controlled lists","errors_mainImageResource":"Missing main images or resources","allowed_values":"Allowed values"}
|
108
scripts/compile.php
Normal file
108
scripts/compile.php
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
<?PHP
|
||||||
|
/**
|
||||||
|
* Compiles the client-side application from PHP parts.
|
||||||
|
*
|
||||||
|
* @author Joshua Ramon Enslin <joshua@museum-digital.de>
|
||||||
|
*/
|
||||||
|
declare(strict_types = 1);
|
||||||
|
require_once __DIR__ . "/../functions/functions.php";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the json for a translation file.
|
||||||
|
*
|
||||||
|
* @param string $lang Language.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function generateFieldsIndex(string $lang):string {
|
||||||
|
|
||||||
|
$fieldsGetter = new CsvxmlAvailableFields($lang);
|
||||||
|
$availableFields = $fieldsGetter->getFields();
|
||||||
|
return MD_STD::json_encode($availableFields);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the json for a translation file.
|
||||||
|
*
|
||||||
|
* @param string $lang Language.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function generateTranslationFile(string $lang):string {
|
||||||
|
|
||||||
|
$tlLoader = new MDTlLoader("csvxml_tl_cache", $lang);
|
||||||
|
|
||||||
|
return MD_STD::json_encode([
|
||||||
|
'remarks' => $tlLoader->tl('basis', 'basis', 'remarks'),
|
||||||
|
'download' => $tlLoader->tl('export', 'export', 'download'),
|
||||||
|
'upload' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'upload'),
|
||||||
|
'select_csv_file_for_upload' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'select_csv_file_for_upload'),
|
||||||
|
'currently_approved_tags' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'currently_approved_tags'),
|
||||||
|
'download_csv_all' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'download_csv_all'),
|
||||||
|
'download_csv_by_selection' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'download_csv_by_selection'),
|
||||||
|
'select_required_fields' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'select_required_fields'),
|
||||||
|
'select_all_fields' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'select_all_fields'),
|
||||||
|
'unset_selection' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'unset_selection'),
|
||||||
|
'file_format' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'file_format'),
|
||||||
|
'validation_errors' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'validation_errors'),
|
||||||
|
'errors_parsing' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'errors_parsing'),
|
||||||
|
'errors_mandatoryTags' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'errors_mandatoryTags'),
|
||||||
|
'errors_duplicateInvNos' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'errors_duplicateInvNos'),
|
||||||
|
'errors_dependentColumns' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'errors_dependentColumns'),
|
||||||
|
'errors_controlledLists' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'errors_controlledLists'),
|
||||||
|
'errors_mainImageResource' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'errors_mainImageResource'),
|
||||||
|
'allowed_values' => $tlLoader->tl("csvxml-overview", "csvxml_overview", 'allowed_values'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate index.htm.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function generateAppShell():string {
|
||||||
|
|
||||||
|
return '<!DOCTYPE HTML>
|
||||||
|
<html data-allowed-langs="' . htmlspecialchars(implode(',', ALLOWED_LANGS)) . '">
|
||||||
|
<head>
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
|
||||||
|
<meta name="description" content="Validate import CSV files for museum-digital" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="assets/css/csvxml.css" />
|
||||||
|
<meta name="theme-color" content="#aa4400" />
|
||||||
|
<link rel="shortcut icon" sizes="128x128" href="assets/img/mdlogo-csvxml.svg" />
|
||||||
|
<meta name="robots" content="noindex" />
|
||||||
|
|
||||||
|
<title>CSVXML :: museum-digital</title>
|
||||||
|
|
||||||
|
<meta name="keywords" content="Imports, museum-digital" />
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body class="loading">
|
||||||
|
|
||||||
|
<h1>
|
||||||
|
<img src="assets/img/mdlogo-csvxml.svg" alt="" />
|
||||||
|
<span>museum-digital:csvxml</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<script src="assets/js/csvxmlV2.js" type="text/javascript" async></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const SERVED_ROOT = __DIR__ . '/../public/';
|
||||||
|
const SERVED_JSON_ROOT = __DIR__ . '/../public/json/';
|
||||||
|
|
||||||
|
if (!is_dir(SERVED_JSON_ROOT)) mkdir(SERVED_JSON_ROOT);
|
||||||
|
|
||||||
|
foreach (ALLOWED_LANGS as $lang) {
|
||||||
|
file_put_contents(SERVED_JSON_ROOT . 'fields.' . $lang . '.json', generateFieldsIndex($lang));
|
||||||
|
file_put_contents(SERVED_JSON_ROOT . 'tls.' . $lang . '.json', generateTranslationFile($lang));
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents(SERVED_ROOT . 'index.htm', generateAppShell());
|
|
@ -1,33 +0,0 @@
|
||||||
<?PHP
|
|
||||||
/**
|
|
||||||
* This script contains tests for the home page.
|
|
||||||
*
|
|
||||||
* @author Joshua Ramon Enslin <joshua@jrenslin.de>
|
|
||||||
*/
|
|
||||||
declare(strict_types = 1);
|
|
||||||
|
|
||||||
// phpcs:disable
|
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test class for the start page.
|
|
||||||
*/
|
|
||||||
final class CsvTest extends TestCase {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test for HTML output.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function testHTMLOutput():void {
|
|
||||||
|
|
||||||
include __DIR__ . "/../public/csv.php";
|
|
||||||
$output = $this->getActualOutput();
|
|
||||||
|
|
||||||
self::assertIsString($output);
|
|
||||||
ob_clean();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
<?PHP
|
|
||||||
/**
|
|
||||||
* This script contains tests for the home page.
|
|
||||||
*
|
|
||||||
* @author Joshua Ramon Enslin <joshua@jrenslin.de>
|
|
||||||
*/
|
|
||||||
declare(strict_types = 1);
|
|
||||||
|
|
||||||
// phpcs:disable
|
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test class for the start page.
|
|
||||||
*/
|
|
||||||
final class StartPageTest extends TestCase {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test for HTML output.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function testHTMLOutput():void {
|
|
||||||
|
|
||||||
include __DIR__ . "/../public/index.php";
|
|
||||||
$output = $this->getActualOutput();
|
|
||||||
|
|
||||||
self::assertIsString($output);
|
|
||||||
ob_clean();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user