develop #28
@ -166,6 +166,14 @@ declare(strict_types=1);
|
||||
<textarea id="csvpreview" name="csvcontent" rows="12" class="form-control" placeholder="CSV-Inhalt wird hier angezeigt, nachdem Sie eine Datei ausgewählt haben. Sie können den Text bearbeiten, bevor Sie ihn absenden."></textarea>
|
||||
</div>
|
||||
|
||||
<!-- CSV preview table and validation info -->
|
||||
<div id="csvPreviewArea" style="display:none; margin-top:1rem;">
|
||||
<div id="csvPreviewInfo" class="small text-muted mb-2">CSV-Vorschau geladen. Passwörter werden geprüft.</div>
|
||||
<div style="overflow-x:auto; max-height:260px;">
|
||||
<table id="csvPreviewTable" class="table table-sm table-bordered" style="width:100%; border-collapse:collapse;"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group d-flex gap-2">
|
||||
<button type="button" id="loadPreviewBtn" class="btn btn-secondary">In Vorschau laden</button>
|
||||
<button type="submit" class="btn btn-primary">CSV verarbeiten</button>
|
||||
@ -278,14 +286,184 @@ declare(strict_types=1);
|
||||
});
|
||||
}
|
||||
|
||||
// Optional: Intercept submit to ensure csvcontent is present when no file is uploaded.
|
||||
// CSV utilities and validation for passwords in preview
|
||||
function parseCsvText(text, delimiter) {
|
||||
const lines = text.split(/\r?\n/).map(l => l.trim()).filter(l => l !== '');
|
||||
if (lines.length === 0) return { headers: [], rows: [] };
|
||||
const headers = lines[0].split(delimiter).map(h => h.trim().replace(/^"|"$/g, ''));
|
||||
const rows = lines.slice(1).map(l => l.split(delimiter).map(c => c.trim().replace(/^"|"$/g, '')));
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
function validatePasswordJS(password, sam) {
|
||||
const errors = [];
|
||||
if (!password || password.length < 7) {
|
||||
errors.push('Passwort muss mindestens 7 Zeichen lang sein.');
|
||||
}
|
||||
let categories = 0;
|
||||
if (/[A-Z]/.test(password)) categories++;
|
||||
if (/[a-z]/.test(password)) categories++;
|
||||
if (/\d/.test(password)) categories++;
|
||||
if (/[^A-Za-z0-9]/.test(password)) categories++;
|
||||
if (categories < 3) {
|
||||
errors.push('Passwort muss Zeichen aus mindestens 3 von 4 Kategorien enthalten.');
|
||||
}
|
||||
if (sam) {
|
||||
const pwLower = password.toLowerCase();
|
||||
const samLower = sam.toLowerCase();
|
||||
if (pwLower.includes(samLower)) {
|
||||
errors.push('Passwort darf den Benutzernamen nicht enthalten.');
|
||||
} else {
|
||||
const minLen = 4;
|
||||
if (samLower.length >= minLen) {
|
||||
outer: for (let len = minLen; len <= samLower.length; len++) {
|
||||
for (let s = 0; s <= samLower.length - len; s++) {
|
||||
const sub = samLower.substr(s, len);
|
||||
if (pwLower.includes(sub)) { errors.push('Passwort darf keine größeren Teile des Benutzernamens enthalten.'); break outer; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
function renderCsvPreview(text, delimiter) {
|
||||
const parsed = parseCsvText(text, delimiter);
|
||||
const headers = parsed.headers;
|
||||
const rows = parsed.rows;
|
||||
const previewArea = document.getElementById('csvPreviewArea');
|
||||
const previewInfo = document.getElementById('csvPreviewInfo');
|
||||
const table = document.getElementById('csvPreviewTable');
|
||||
table.innerHTML = '';
|
||||
if (headers.length === 0) {
|
||||
previewArea.style.display = 'none';
|
||||
return { invalidCount: 0 };
|
||||
}
|
||||
|
||||
// build header
|
||||
const thead = document.createElement('thead');
|
||||
const trh = document.createElement('tr');
|
||||
headers.forEach(h => { const th = document.createElement('th'); th.textContent = h; trh.appendChild(th); });
|
||||
thead.appendChild(trh);
|
||||
table.appendChild(thead);
|
||||
|
||||
// find indexes
|
||||
const pwdIdx = headers.findIndex(h => /pass(word)?/i.test(h));
|
||||
const samIdx = headers.findIndex(h => /(sam(accountname)?)|samaccountname/i.test(h));
|
||||
|
||||
const tbody = document.createElement('tbody');
|
||||
let invalidCount = 0;
|
||||
rows.forEach((rowArr, rowIndex) => {
|
||||
const tr = document.createElement('tr');
|
||||
rowArr.forEach((cell, colIndex) => {
|
||||
const td = document.createElement('td');
|
||||
const input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.value = cell;
|
||||
input.className = 'form-control form-control-sm';
|
||||
input.addEventListener('input', function () {
|
||||
// re-validate this row when edited
|
||||
const currentPwd = (pwdIdx >= 0) ? tr.querySelectorAll('input')[pwdIdx].value : '';
|
||||
const currentSam = (samIdx >= 0) ? tr.querySelectorAll('input')[samIdx].value : '';
|
||||
const errs = validatePasswordJS(currentPwd, currentSam);
|
||||
if (errs.length > 0) {
|
||||
tr.classList.add('table-danger');
|
||||
} else {
|
||||
tr.classList.remove('table-danger');
|
||||
}
|
||||
});
|
||||
td.appendChild(input);
|
||||
tr.appendChild(td);
|
||||
});
|
||||
|
||||
// validate password for this row if applicable
|
||||
if (pwdIdx >= 0) {
|
||||
const pwd = rowArr[pwdIdx] || '';
|
||||
const sam = (samIdx >= 0) ? (rowArr[samIdx] || '') : '';
|
||||
const errs = validatePasswordJS(pwd, sam);
|
||||
if (errs.length > 0) {
|
||||
tr.classList.add('table-danger');
|
||||
invalidCount++;
|
||||
}
|
||||
}
|
||||
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
table.appendChild(tbody);
|
||||
previewArea.style.display = 'block';
|
||||
previewInfo.textContent = (invalidCount > 0)
|
||||
? `${invalidCount} Zeile(n) haben ungültige Passwörter. Bitte korrigieren Sie diese in der Tabelle bevor Sie die CSV verarbeiten.`
|
||||
: 'CSV-Vorschau geladen. Alle Passwörter entsprechen den Anforderungen.';
|
||||
return { invalidCount };
|
||||
}
|
||||
|
||||
if (loadBtn) {
|
||||
loadBtn.addEventListener('click', function () {
|
||||
const file = fileInput.files && fileInput.files[0];
|
||||
const delim = document.getElementById('csvdelimiter').value || ',';
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
preview.value = e.target.result || '';
|
||||
renderCsvPreview(preview.value, delim);
|
||||
};
|
||||
reader.readAsText(file, 'utf-8');
|
||||
return;
|
||||
}
|
||||
if (preview.value.trim() === '') {
|
||||
alert('Bitte wählen Sie zuerst eine CSV-Datei aus oder fügen Sie CSV-Text direkt in das Feld ein.');
|
||||
return;
|
||||
}
|
||||
renderCsvPreview(preview.value, delim);
|
||||
});
|
||||
}
|
||||
|
||||
if (clearBtn) {
|
||||
clearBtn.addEventListener('click', function () {
|
||||
preview.value = '';
|
||||
fileInput.value = '';
|
||||
document.getElementById('csvPreviewArea').style.display = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
if (fileInput) {
|
||||
fileInput.addEventListener('change', function () {
|
||||
// auto-load content into preview textarea (but do not auto-validate until user clicks 'In Vorschau laden')
|
||||
const file = fileInput.files && fileInput.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
preview.value = e.target.result || '';
|
||||
};
|
||||
reader.readAsText(file, 'utf-8');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Intercept submit: require preview validation or loaded file to have been checked
|
||||
form.addEventListener('submit', function (e) {
|
||||
// If no file was sent, ensure csvcontent isn't empty.
|
||||
const delim = document.getElementById('csvdelimiter').value || ',';
|
||||
const file = fileInput.files && fileInput.files[0];
|
||||
if (!file && preview.value.trim() === '') {
|
||||
// If no preview is present, require user to load preview first to ensure validation
|
||||
if (preview.value.trim() === '') {
|
||||
if (file) {
|
||||
e.preventDefault();
|
||||
alert('Bitte klicken Sie auf "In Vorschau laden" bevor Sie die CSV verarbeiten, damit Passwörter validiert werden.');
|
||||
return false;
|
||||
}
|
||||
e.preventDefault();
|
||||
alert('Die CSV-Vorschau ist leer. Bitte wählen Sie eine Datei oder fügen Sie CSV-Inhalt ein.');
|
||||
return false;
|
||||
}
|
||||
|
||||
const res = renderCsvPreview(preview.value, delim);
|
||||
if (res.invalidCount > 0) {
|
||||
e.preventDefault();
|
||||
alert('Import abgebrochen: Es gibt ungültige Passwörter in der CSV-Vorschau. Bitte korrigieren Sie diese zuerst.');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user