Move on to a fully pre-compiled setup

See #14
This commit is contained in:
jrenslin committed 2022-11-07 02:57:34 +01:00
1 parent 014523c17d
commit 3e4554f759
17 files changed
+321 -179

No files matched your search

+4
View File
@@ -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
+2
View File
@@ -10,6 +10,8 @@
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/",
+18 -1
View File
@@ -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
|============ */ |============ */
+19 -16
View File
@@ -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}
+75 -11
View File
@@ -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', const allowedLangs = document.documentElement.getAttribute("data-allowed-langs").split(',');
}).then(function(response) {
return response.json(); if (navigator.language === undefined) return 'en';
}).then(function(elements) {
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"); document.body.classList.remove("loading");
const page = new CsvxmlPage(elements); const page = new CsvxmlPage(fieldList, tls);
page.renderHeader();
page.renderUploader(); page.renderUploader();
page.renderMain(); 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
View 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
View 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>
-85
View File
@@ -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
View File
@@ -0,0 +1 @@
{"Objekt-Basisdaten":{"inventory_number":{"required":true,"allowedValues":[],"dependsOn":[],"remark":"Only unique values allowed","name_human_readable":"Inventarnummer","explica":"Falls es f\u00fcr dieses Objekt keine Inventarnummer gibt, dann tragen Sie bitte \"o. Inv.\" oder \u00c4hnliches ein. Handelt es sich um eine Dauerleihgabe k\u00f6nnen Sie auch ein K\u00fcrzel f\u00fcr die leihgebende Einrichtung, die dortige Inventarnummer und den Vermerk \"(Leihgabe)\" eintragen.<br><br>Beispiel 1: \"G0904\"<br>Beispiel 2: \"A4711 (neu), SMH12 (alt)\"<br>Beispiel 3: \"KHM 1252 (Leihgabe)\""},"object_type":{"required":true,"allowedValues":[],"dependsOn":[],"remark":"Keep it short! Field is needed to create valid LIDO from museum-digital entries","name_human_readable":"Objektart","explica":"Von welcher Art ist das Objekt? Die Kategorie, zu der das Objekt gez\u00e4hlt werden kann ...<br><br>Beispiel 1: \"Gem\u00e4lde\"<br>Beispiel 2: \"Tierfalle\"<br><br>(Terminologische Kontrolle der Begriffe in diesem Feld durch Einbindung von Terminologien ist in Arbeit)"},"object_title":{"required":true,"allowedValues":[],"dependsOn":[],"remark":"Best is not to repeat the title again and again for many objects, e.g. vase, vase, vase ... better: Green vase, Blue vase, Yellow vase, ...","name_human_readable":"Objektname","explica":"Bezeichnung des einzelnen Objektes selbst. Objektnamen sind auf das jeweilige Objekt gem\u00fcnzt. Sie dienen auch dazu \"Vase\" von \"Vase\" unterscheiden zu k\u00f6nnen!<br><br>Beispiel 1: \"Selbstportr\u00e4t Adolf Senff\"<br>Beispiel 2: Brakteat (Ludolf von Kroppenstedt)<br><br>Der Objektname sollte in eine Bildschirmzeile passen. Bitte nur in Ausnahmef\u00e4llen mehr als 100 Zeichen."},"object_description":{"required":true,"allowedValues":[],"dependsOn":[],"remark":"A good description of the objects is fundamental to make the objects searchable in the internet","name_human_readable":"Beschreibung","explica":"Allgemein verst\u00e4ndliche Beschreibung des Objektes in zwei bis drei Abs\u00e4tzen. Bitte verwenden Sie m\u00f6glichst wenige Abk\u00fcrzungen und f\u00fcgen an unbekanntere Abk\u00fcrzungen deren Aufl\u00f6sung in Klammern an.<br><br>Redundanzen sind erlaubt! D.h. auch wenn \u00fcber Ereignisse Herstellungszeiten, -orte oder \u00c4hnliches angegeben werden: Es darf hier noch einmal genannt werden. Wenn m\u00f6glich sollte die \"deskriptive Form\" gew\u00e4hlt werden, d.h. S\u00e4tze statt Stichworten."},"object_material_technique":{"required":false,"allowedValues":[],"dependsOn":[],"remark":"This is a cummulative field for material and technique. It is also possible to additionally save the information separate in the fields: <i>material_separate<\/i> and <i>technique_separate<\/i>","name_human_readable":"Material \/ Technik","explica":"Angaben zum Material aus dem das Objekt besteht und zur Technik auf deren Anwendung es basiert.<br\/><br\/>Getrennte Angaben (etwa zur Inventarisierung) k\u00f6nnen auf der Registerkarte \"Zusatz\" eingetragen und automatisch in dieses Feld \u00fcbertragen werden."},"object_dimensions":{"required":false,"allowedValues":[],"dependsOn":[],"remark":"This is a cummulative field for dimensions (values and units). It is also possible to additionally save the information separate. See the block: <i>separate dimensions<\/i>","name_human_readable":"Ma\u00dfe","explica":"Keine exakte Schreibvorschrift. Aber: Bitte geben Sie immer auch eine Ma\u00dfeinheit an. Halten Sie die Eintr\u00e4ge bitte kurz. Abk\u00fcrzungen sind an dieser Stelle erlaubt<br><br>Beispiel 1: \"LxBxH 95 x 15 x 150 mm\"<br>Beispiel 2: \"Innendurchm.: 4,8 mm; Wand.: 2 mm; H: 5,2 cm\".<br\/><br\/>Getrennte Angaben (etwa zur Inventarisierung) k\u00f6nnen auf der Registerkarte \"Zusatz\" eingetragen und automatisch in dieses Feld \u00fcbertragen werden."},"object_publication":{"required":false,"allowedValues":["y","n"],"dependsOn":[],"remark":"Shall the object be visible directly after import?","name_human_readable":"Sichtbarkeit","explica":""},"institution_name":{"required":false,"allowedValues":[],"dependsOn":[],"remark":"If name of institution is given it has to be exactly the same as already known in museum-digital","name_human_readable":"Name des Museums","explica":"Der Name wird in Tabellen aufgef\u00fchrt, deshalb bitte m\u00f6glichst kurz halten"},"collection_name1":{"required":false,"allowedValues":[],"dependsOn":[],"remark":"Use if object belongs to a collection. If collection is already in museum-digital, please use exactly the same name","name_human_readable":"Name der Sammlung","explica":"Kurzen Namen verwenden - dieser Name erscheint in Tabellen.<br><br>Beispiel 1: \"Gem\u00e4lde\"<br>Beispiel 2: \"Brauereisammlung\"<br>Beispiel 3: \"Nachlass Illies\""},"collection_name2":{"required":false,"allowedValues":[],"dependsOn":[],"remark":"Use if object belongs to a collection. If collection is already in museum-digital, please use exactly the same name","name_human_readable":"Name der SammluLine truncated
+1
View File
@@ -0,0 +1 @@
{"Basic Object Fields":{"inventory_number":{"required":true,"allowedValues":[],"dependsOn":[],"remark":"Only unique values allowed","name_human_readable":"Inventory number","explica":"If the object has no inventory number, please enter something like \"No Inv. No.\" If it is a permanent loan, you can add the signature of the organziation lending, followed by the object's inventory number at the given institution and finally a note: \"(permanent loan)\".<br><br>Example 1: \"G0904\"<br>Example 2: \"A4711 (new), SMH12 (old)\"<br>Example 3: \"KHM 1252 (Permanent loan)\""},"object_type":{"required":true,"allowedValues":[],"dependsOn":[],"remark":"Keep it short! Field is needed to create valid LIDO from museum-digital entries","name_human_readable":"Object type","explica":"What kind of object is this? To which category does it belong?<br><br>Example 1: \"Painting\"<br>Example 2: \"Animal Trap\"<br><br>(A terminological control function for these terms will be available in the future)"},"object_title":{"required":true,"allowedValues":[],"dependsOn":[],"remark":"Best is not to repeat the title again and again for many objects, e.g. vase, vase, vase ... better: Green vase, Blue vase, Yellow vase, ...","name_human_readable":"Object title","explica":"Title of the object. Titles belong to each object on their own. They also help differentiate between one \"jar\" and another \"jar\"!<br><br>Example 1: \"Portrait Adolf Senff\"<br>Example 2: Bracteate (Ludolf von Kroppenstedt)<br><br>The title should not be longer than a line on your screen. If possible, do not use more than 100 characters."},"object_description":{"required":true,"allowedValues":[],"dependsOn":[],"remark":"A good description of the objects is fundamental to make the objects searchable in the internet","name_human_readable":"Description","explica":"Easily understandable (to anybody!) description of the object. Please avoid using abbreviations if possible. If avoiding them is impossible and it's not an entirely common abbreviation with the general public, provide an explanation in brackets.<br><br>Repetitions are allowed! That means: Even if you have already entered events, places, times etc. related to your object, you can still mention them in your description.<br>Please do not use keywords: Use full sentences."},"object_material_technique":{"required":false,"allowedValues":[],"dependsOn":[],"remark":"This is a cummulative field for material and technique. It is also possible to additionally save the information separate in the fields: <i>material_separate<\/i> and <i>technique_separate<\/i>","name_human_readable":"Material\/Technique","explica":"Information about the material an object was made with and the methods applied in its creation.<br><br>Just fill in this field, if you have any information about it: This field is optional.<br\/><br\/>The single values can alternatively also be entered separately using the tab \"addendum\" below. Once that's done, you may automatically merge them into this field."},"object_dimensions":{"required":false,"allowedValues":[],"dependsOn":[],"remark":"This is a cummulative field for dimensions (values and units). It is also possible to additionally save the information separate. See the block: <i>separate dimensions<\/i>","name_human_readable":"Dimensions","explica":"No exact rules. But, please always enter units. Keep the entries as short as possible. Abbreviations may be used.<br><br>Example 1: \"LxWxH 95 x 15 x 150 mm\"<br>Example 2: \"Inner diameter.: 4.8 mm; Walls: 2 mm; H: 5.2 cm\"<br\/><br\/>The single values can alternatively also be entered separately using the tab \"addendum\" below. Once that's done, you may automatically merge them into this field."},"object_publication":{"required":false,"allowedValues":["y","n"],"dependsOn":[],"remark":"Shall the object be visible directly after import?","name_human_readable":"Visibility","explica":""},"institution_name":{"required":false,"allowedValues":[],"dependsOn":[],"remark":"If name of institution is given it has to be exactly the same as already known in museum-digital","name_human_readable":"Name of museum","explica":"The name is listed in tables, so please keep it as short as possible."},"collection_name1":{"required":false,"allowedValues":[],"dependsOn":[],"remark":"Use if object belongs to a collection. If collection is already in museum-digital, please use exactly the same name","name_human_readable":"Collection title","explica":"Use a short title - This title will be displayed in tables and lists.<br><br>Example 1: \"Paintings\"<br>Example 2: \"Collection on brewing beer\"<br>Example 3: \"Estate of Illies\""},"collection_name2":{"required":false,"allowedValues":[],"dependsOn":[],"remark":"Use if object belongs to a collection. If collection is already in museum-digital, please use exactly the same name","name_human_readable":"Collection title","explica":"Use a short title - This title will be displayed in tables and lists.<br><br>Example 1: \"Paintings\"<brLine truncated
+1
View File
@@ -0,0 +1 @@
{"Basic Object Fields":{"inventory_number":{"required":true,"allowedValues":[],"dependsOn":[],"remark":"Only unique values allowed","name_human_readable":"Lelt\u00e1ri sz\u00e1m","explica":"Ha a t\u00e1rgynak nincs lelt\u00e1ri sz\u00e1ma, akkor k\u00e9rj\u00fck ezt a &quot;nincs lelt\u00e1ri sz\u00e1m&quot; kifejez\u00e9ssel jel\u00f6lje. Amennyiben tart\u00f3s let\u00e9tr\u0151l van sz\u00f3, akkor adja meg a k\u00f6lcs\u00f6nad\u00f3 int\u00e9zm\u00e9ny\/ szem\u00e9ly r\u00f6vid\u00edt\u00e9s\u00e9t, a t\u00e1rgy eredeti lelt\u00e1ri sz\u00e1m\u00e1t, v\u00e9g\u00fcl a &quot;(tart\u00f3s let\u00e9t) &quot; megjel\u00f6l\u00e9st.<br><br>1. p\u00e9lda: &quot;G0904&quot;<br>2. p\u00e9lda: &quot;A4711 (\u00faj), SMH12 (r\u00e9gi)&quot;<br>3. p\u00e9lda: &quot;KHM 1252 (tart\u00f3s let\u00e9t)&quot;<br><br><b style=&quot;font-weight:normal;color:#660000;&quot;>K\u00e9rj\u00fck, ezt a mez\u0151t mindig t\u00f6ltse ki.<\/b>"},"object_type":{"required":true,"allowedValues":[],"dependsOn":[],"remark":"Keep it short! Field is needed to create valid LIDO from museum-digital entries","name_human_readable":"A t\u00e1rgy t\u00edpusa","explica":"Milyen t\u00edpus\u00fa t\u00e1rgyr\u00f3l van sz\u00f3? Ezen mez\u0151 kit\u00f6lt\u00e9se legink\u00e1bb az Europeana szabv\u00e1ny miatt l\u00e9nyeges.<br><br>. Amennyiben NEM K\u00e9pz\u0151m\u0171v\u00e9szeti, M\u0171szaki- \u00e9s technikat\u00f6rt\u00e9neti szakanyagokr\u00f3l van sz\u00f3, akkor sorolja be a t\u00e1rgyat a a gy\u0171jtem\u00e9nyi besorol\u00e1s szerinti szakanyag megnevez\u00e9se alapj\u00e1n. V\u00e1lasszon a k\u00f6vetkez\u0151 lehet\u0151s\u00e9gekb\u0151l<br><br>Term\u00e9szettudom\u00e1nyi<br>Embertani<br>R\u00e9g\u00e9szeti<br>R\u00e9g\u00e9szeti \u00e1llatcsont-leleti<br> Iparm\u0171v\u00e9szeti<br> N\u00e9prajzi<br>T\u00f6rt\u00e9neti <br>Numizmatikai<br> Irodalomt\u00f6rt\u00e9neti<br> Sz\u00ednh\u00e1zt\u00f6rt\u00e9neti<br> Eredeti f\u00e9nyk\u00e9p<br>Adatt\u00e1ri f\u00e9nyk\u00e9p<br>Adatt\u00e1ri dokument\u00e1ci\u00f3<br>Ki\u00e1ll\u00edt\u00e1si technikai- \u00e9s seg\u00e9deszk\u00f6z <br><br>A felsorolt kateg\u00f3ri\u00e1k k\u00f6z\u00fcl EGY v\u00e1laszthat\u00f3! <br><br><b style=&quot;font-weight:normal;color:#660000;&quot;><br><br> K\u00e9pz\u0151m\u0171v\u00e9szeti szakanyag eset\u00e9n adja meg a m\u0171t\u00e1rgy klasszifik\u00e1ci\u00f3j\u00e1t, mint pl. festm\u00e9ny, szobor, domborm\u0171, grafika, install\u00e1ci\u00f3 stb. M\u0171szaki- \u00e9s technikat\u00f6rt\u00e9neti szakanyag eset\u00e9ben a lelt\u00e1rk\u00f6nyv &quot;T\u00edpusn\u00e9v&quot; mez\u0151j\u00e9be bevitt adatokat vigye be. K\u00e9rj\u00fck, ezt a mez\u0151t mindig t\u00f6ltse ki.<\/b>"},"object_title":{"required":true,"allowedValues":[],"dependsOn":[],"remark":"Best is not to repeat the title again and again for many objects, e.g. vase, vase, vase ... better: Green vase, Blue vase, Yellow vase, ...","name_human_readable":"A t\u00e1rgy megnevez\u00e9se","explica":"A t\u00e1rgy neve, c\u00edme, megnevez\u00e9se. A t\u00e1rgy ezen megjel\u00f6l\u00e9se arra is szolg\u00e1lhat, hogy specifikus n\u00e9v h\u00edj\u00e1n seg\u00edtsen megk\u00fcl\u00f6nb\u00f6ztetni egym\u00e1st\u00f3l k\u00e9t l\u00e1tsz\u00f3lag azonos t\u00e1rgyat, p\u00e9ld\u00e1ul k\u00e9t v\u00e1z\u00e1t. <br><br>1. p\u00e9lda: &quot;Kossuth Lajos portr\u00e9ja&quot;<br>2. p\u00e9lda: Grim\u00e1ni kancs\u00f3 <br><br>A t\u00e1rgy nev\u00e9nek bele kell f\u00e9rnie az erre a c\u00e9lra l\u00e9trehozott mez\u0151be, ez\u00e9rt k\u00e9rj\u00fck, hogy csak kiv\u00e9teles esetben adjon meg 100 kartern\u00e9l hosszabb elnevez\u00e9st. <br><br><b style=&quot;font-weight:normal;color:#660000;&quot;> K\u00e9rj\u00fck, ezt a mez\u0151t mindig t\u00f6ltse ki.<\/b>"},"object_description":{"required":true,"allowedValues":[],"dependsOn":[],"remark":"A good description of the objects is fundamental to make the objects searchable in the internet","name_human_readable":"Le\u00edr\u00e1s","explica":"A t\u00e1rgy k\u00f6z\u00e9rthet\u0151, magyar\u00e1z\u00f3 le\u00edr\u00e1sa k\u00e9t-h\u00e1rom bekezd\u00e9snyi hossz\u00fas\u00e1gban. K\u00e9rj\u00fck, lehet\u0151leg ritk\u00e1n haszn\u00e1ljon r\u00f6vid\u00edt\u00e9seket, valamint a kev\u00e9sb\u00e9 ismert r\u00f6vid\u00edt\u00e9s vagy kifejez\u00e9s jelent\u00e9s\u00e9t z\u00e1r\u00f3jelben fejtse ki.<br><br>Ism\u00e9tl\u00e9s megengedett! Ez azt jelenti, hogy az esem\u00e9nyk\u00e9nt m\u00e1r bevitt le\u00edr\u00f3 inform\u00e1ci\u00f3kat, mint p\u00e9ld\u00e1ul a k\u00e9sz\u00edt\u00e9s idej\u00e9t, hely\u00e9t jel\u00f6l\u0151, vagy egy\u00e9b hasonl\u00f3 adatokat \u00fajra meg lehet itt adni. K\u00e9rj\u00fck, hogy c\u00edmszavak helyett ink\u00e1bb kerek mondatokban fogalmazzon.<br><br><b style=&quot;font-weight:normal;color:#660000;&quot;>K\u00e9rj\u00fck, mindig t\u00f6ltse ki<\/b>"},"object_material_technique":{"required":false,"allowedValues":[],"dependsOn":[],"remark":"This is a cummulative field for material and technique. It is also Line truncated
+1
View 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
View 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
View 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
View 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());
-33
View File
@@ -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();
}
}
-33
View File
@@ -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();
}
}