implementation of the user .csv importing
Release Inventarsystem / release-docker (push) Successful in 2m16s
Release Inventarsystem / release-docker (push) Successful in 2m16s
This commit is contained in:
+207
-166
@@ -6,7 +6,7 @@
|
||||
<div class="container">
|
||||
<div class="header-section">
|
||||
<h1>Neuen Benutzer registrieren</h1>
|
||||
<p class="subtitle">Erstellen Sie ein neues Benutzerkonto und legen Sie Zugriffsrechte fest</p>
|
||||
<p class="subtitle">Erstellen Sie ein neues Benutzerkonto oder importieren Sie mehrere Benutzer per CSV</p>
|
||||
</div>
|
||||
|
||||
<div class="flash-container">
|
||||
@@ -23,7 +23,43 @@
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<!-- 1. CSV BULK IMPORT CARD -->
|
||||
<div class="form-card" style="margin-bottom: 2rem; border-top: 4px solid #059669;">
|
||||
<div class="card-header" style="margin-bottom: 1rem;">
|
||||
<h2>Massenregistrierung via CSV</h2>
|
||||
<p class="subtitle" style="color: #4b5563;">
|
||||
Laden Sie eine CSV-Datei hoch (Format: <code>Vorname, Nachname</code>).
|
||||
Benutzernamen und sichere Passwörter werden serverseitig generiert. Nach dem Upload erhalten Sie direkt ein PDF mit Zugangsdaten (2 pro Seite zum Ausschneiden).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ url_for('register_csv') }}" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="permission-preset-csv">Berechtigungs-Preset für alle CSV-Benutzer</label>
|
||||
<select id="permission-preset-csv" name="permission_preset" class="form-select" style="margin-bottom: 1rem;">
|
||||
{% for preset_key, preset in permission_presets.items() %}
|
||||
<option value="{{ preset_key }}">{{ preset.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<label for="csv_file">CSV-Datei auswählen</label>
|
||||
<div class="input-container">
|
||||
<span class="input-icon">📄</span>
|
||||
<input type="file" id="csv_file" name="csv_file" accept=".csv" required style="padding: 10px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group form-actions" style="margin-top: 1.5rem;">
|
||||
<button type="submit" class="action-button" style="background-color: #059669; color: white;">
|
||||
📥 CSV Importieren & Zugangsdaten-PDF Herunterladen
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- 2. SINGLE USER REGISTRATION CARD -->
|
||||
<div class="form-card">
|
||||
<h2>Einzelnen Benutzer registrieren</h2>
|
||||
<form method="POST" action="{{ url_for('register') }}">
|
||||
|
||||
<div class="form-group">
|
||||
@@ -32,11 +68,13 @@
|
||||
<span class="input-icon">👤</span>
|
||||
<input type="text" id="name" name="name" placeholder="Geben Sie den Vornamen ein" required onchange="generateUsername()" oninput="generateUsername()">
|
||||
</div>
|
||||
|
||||
<label for="last-name">Nachname</label>
|
||||
<div class="input-container">
|
||||
<span class="input-icon">👤</span>
|
||||
<input type="text" id="last-name" name="last-name" placeholder="Geben Sie den Nachnamen ein" required onchange="generateUsername()" oninput="generateUsername()">
|
||||
</div>
|
||||
|
||||
<label for="username">Benutzername <span style="color: #9ca3af;">(Vorschau - wird serverseitig finalisiert)</span></label>
|
||||
<div class="input-container">
|
||||
<span class="input-icon">👤</span>
|
||||
@@ -44,7 +82,7 @@
|
||||
</div>
|
||||
<p class="anonymize-hint">Klarnamen werden nur zur Erzeugung des Benutzernamens als Kürzel (z.B. SimFri) verwendet; bei Kollision wird automatisch ein Buchstabe mehr genommen.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Passwort</label>
|
||||
<div class="password-rules" id="password-rules" aria-live="polite">
|
||||
@@ -57,20 +95,21 @@
|
||||
<li id="pw-rule-symbol" class="pw-rule">Mindestens ein Sonderzeichen</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="input-wrapper">
|
||||
<div class="input-container">
|
||||
<span class="input-icon">🔒</span>
|
||||
<!-- HTML5 Pattern blockiert unsichere Passwörter vor dem Absenden -->
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
placeholder="Geben Sie ein sicheres Passwort ein"
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
placeholder="Geben Sie ein sicheres Passwort ein"
|
||||
required
|
||||
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{12,}">
|
||||
<button type="button" id="toggle-pw-btn" class="toggle-pw-btn" onclick="togglePasswordVisibility()" style="background:none; border:none; cursor:pointer; padding-right:10px;">👁️</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pw-actions">
|
||||
<div class="pw-actions" style="margin-top: 8px;">
|
||||
<button type="button" class="btn-secondary" onclick="generateSecurePassword()">Passwort generieren</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -120,6 +159,163 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Hilfsfunktion: Umlaute auflösen und Sonderzeichen entfernen
|
||||
function cleanNameForUsername(text) {
|
||||
if (!text) return '';
|
||||
let cleaned = text.trim().toLowerCase()
|
||||
.replace(/ä/g, 'ae')
|
||||
.replace(/ö/g, 'oe')
|
||||
.replace(/ü/g, 'ue')
|
||||
.replace(/ß/g, 'ss')
|
||||
.replace(/[^a-z]/g, '');
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
// Hilfsfunktion: Erster Buchstabe groß
|
||||
function formatPart(str, len) {
|
||||
if (!str) return '';
|
||||
const part = str.slice(0, len);
|
||||
return part.charAt(0).toUpperCase() + part.slice(1);
|
||||
}
|
||||
|
||||
// Generiert die Vorschau des Benutzernamens (z.B. SimFri)
|
||||
function generateUsername() {
|
||||
const firstName = cleanNameForUsername(document.getElementById('name').value);
|
||||
const lastName = cleanNameForUsername(document.getElementById('last-name').value);
|
||||
let username = '';
|
||||
|
||||
if (firstName && lastName) {
|
||||
username = formatPart(firstName, 3) + formatPart(lastName, 3);
|
||||
} else if (firstName) {
|
||||
username = formatPart(firstName, 6);
|
||||
} else if (lastName) {
|
||||
username = formatPart(lastName, 6);
|
||||
}
|
||||
|
||||
const usernameField = document.getElementById('username');
|
||||
if (usernameField) {
|
||||
usernameField.value = username || '';
|
||||
}
|
||||
}
|
||||
|
||||
// PASSWORT GENERATOR
|
||||
function generateSecurePassword() {
|
||||
const length = 16;
|
||||
const charsetLower = "abcdefghijklmnopqrstuvwxyz";
|
||||
const charsetUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const charsetNum = "0123456789";
|
||||
const charsetSym = "!@#$%^&*()_+~|}{[]:;?><,.-=";
|
||||
|
||||
let password = "";
|
||||
// Garantiert mindestens 1 Zeichen aus jeder Kategorie
|
||||
password += charsetLower[Math.floor(Math.random() * charsetLower.length)];
|
||||
password += charsetUpper[Math.floor(Math.random() * charsetUpper.length)];
|
||||
password += charsetNum[Math.floor(Math.random() * charsetNum.length)];
|
||||
password += charsetSym[Math.floor(Math.random() * charsetSym.length)];
|
||||
|
||||
const allChars = charsetLower + charsetUpper + charsetNum + charsetSym;
|
||||
// Restliche Zeichen auffüllen
|
||||
for (let i = 4; i < length; i++) {
|
||||
password += allChars[Math.floor(Math.random() * allChars.length)];
|
||||
}
|
||||
|
||||
// Passwort durchmischen
|
||||
password = password.split('').sort(() => 0.5 - Math.random()).join('');
|
||||
|
||||
const pwField = document.getElementById('password');
|
||||
pwField.value = password;
|
||||
|
||||
// Automatisch sichtbar machen
|
||||
pwField.type = 'text';
|
||||
const toggleBtn = document.getElementById('toggle-pw-btn');
|
||||
if (toggleBtn) toggleBtn.textContent = '🙈';
|
||||
|
||||
updatePasswordRules();
|
||||
}
|
||||
|
||||
// PASSWORT SICHTBARKEIT UMSCHALTEN
|
||||
function togglePasswordVisibility() {
|
||||
const pwField = document.getElementById('password');
|
||||
const toggleBtn = document.getElementById('toggle-pw-btn');
|
||||
if (pwField.type === "password") {
|
||||
pwField.type = "text";
|
||||
if (toggleBtn) toggleBtn.textContent = '🙈';
|
||||
} else {
|
||||
pwField.type = "password";
|
||||
if (toggleBtn) toggleBtn.textContent = '👁️';
|
||||
}
|
||||
}
|
||||
|
||||
// Globale Funktion für Passwort-Update
|
||||
function updatePasswordRules() {
|
||||
const passwordInput = document.getElementById('password');
|
||||
if (!passwordInput) return;
|
||||
|
||||
const value = String(passwordInput.value || '');
|
||||
|
||||
const setRuleState = (id, ok) => {
|
||||
const node = document.getElementById(id);
|
||||
if (node) node.classList.toggle('ok', !!ok);
|
||||
};
|
||||
|
||||
setRuleState('pw-rule-length', value.length >= 12);
|
||||
setRuleState('pw-rule-lower', /[a-z]/.test(value));
|
||||
setRuleState('pw-rule-upper', /[A-Z]/.test(value));
|
||||
setRuleState('pw-rule-digit', /[0-9]/.test(value));
|
||||
setRuleState('pw-rule-symbol', /[^A-Za-z0-9]/.test(value));
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const permissionPresets = {{ permission_presets | tojson }};
|
||||
const presetSelect = document.getElementById('permission-preset');
|
||||
const useCustomPermissions = document.getElementById('use-custom-permissions');
|
||||
const customPermissions = document.getElementById('custom-permissions');
|
||||
|
||||
function applyPresetToPermissionForm(presetKey) {
|
||||
const preset = permissionPresets[presetKey] || {};
|
||||
const actionDefaults = preset.actions || {};
|
||||
const pageDefaults = preset.pages || {};
|
||||
|
||||
document.querySelectorAll('.permission-action-checkbox').forEach(function (checkbox) {
|
||||
const key = checkbox.name.replace('action_', '');
|
||||
checkbox.checked = !!actionDefaults[key];
|
||||
});
|
||||
|
||||
document.querySelectorAll('.permission-page-checkbox').forEach(function (checkbox) {
|
||||
const key = checkbox.name.replace('page_', '');
|
||||
checkbox.checked = !!pageDefaults[key];
|
||||
});
|
||||
}
|
||||
|
||||
function toggleCustomPermissions() {
|
||||
if (!useCustomPermissions || !customPermissions) {
|
||||
return;
|
||||
}
|
||||
customPermissions.style.display = useCustomPermissions.checked ? 'block' : 'none';
|
||||
}
|
||||
|
||||
if (presetSelect) {
|
||||
presetSelect.addEventListener('change', function () {
|
||||
applyPresetToPermissionForm(this.value);
|
||||
});
|
||||
applyPresetToPermissionForm(presetSelect.value);
|
||||
}
|
||||
|
||||
if (useCustomPermissions) {
|
||||
useCustomPermissions.addEventListener('change', toggleCustomPermissions);
|
||||
toggleCustomPermissions();
|
||||
}
|
||||
|
||||
const passwordInput = document.getElementById('password');
|
||||
if (passwordInput) {
|
||||
passwordInput.addEventListener('input', updatePasswordRules);
|
||||
passwordInput.addEventListener('blur', updatePasswordRules);
|
||||
updatePasswordRules();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #3498db;
|
||||
@@ -310,11 +506,11 @@ input::placeholder {
|
||||
padding: 0 1rem;
|
||||
margin: 1rem auto;
|
||||
}
|
||||
|
||||
|
||||
.content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
|
||||
.header-section h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
@@ -437,159 +633,4 @@ input::placeholder {
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Hilfsfunktion: Umlaute auflösen und Sonderzeichen entfernen
|
||||
function cleanNameForUsername(text) {
|
||||
if (!text) return '';
|
||||
let cleaned = text.trim().toLowerCase()
|
||||
.replace(/ä/g, 'ae')
|
||||
.replace(/ö/g, 'oe')
|
||||
.replace(/ü/g, 'ue')
|
||||
.replace(/ß/g, 'ss')
|
||||
.replace(/[^a-z]/g, '');
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
// Hilfsfunktion: Erster Buchstabe groß
|
||||
function formatPart(str, len) {
|
||||
if (!str) return '';
|
||||
const part = str.slice(0, len);
|
||||
return part.charAt(0).toUpperCase() + part.slice(1);
|
||||
}
|
||||
|
||||
// Generiert die Vorschau des Benutzernamens (z.B. SimFri)
|
||||
function generateUsername() {
|
||||
const firstName = cleanNameForUsername(document.getElementById('name').value);
|
||||
const lastName = cleanNameForUsername(document.getElementById('last-name').value);
|
||||
let username = '';
|
||||
|
||||
if (firstName && lastName) {
|
||||
username = formatPart(firstName, 3) + formatPart(lastName, 3);
|
||||
} else if (firstName) {
|
||||
username = formatPart(firstName, 6);
|
||||
} else if (lastName) {
|
||||
username = formatPart(lastName, 6);
|
||||
}
|
||||
|
||||
const usernameField = document.getElementById('username');
|
||||
if (usernameField) {
|
||||
usernameField.value = username || '';
|
||||
}
|
||||
}
|
||||
|
||||
// PASSWORT GENERATOR
|
||||
function generateSecurePassword() {
|
||||
const length = 16;
|
||||
const charsetLower = "abcdefghijklmnopqrstuvwxyz";
|
||||
const charsetUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const charsetNum = "0123456789";
|
||||
const charsetSym = "!@#$%^&*()_+~|}{[]:;?><,.-=";
|
||||
|
||||
let password = "";
|
||||
// Garantiert mindestens 1 Zeichen aus jeder Kategorie
|
||||
password += charsetLower[Math.floor(Math.random() * charsetLower.length)];
|
||||
password += charsetUpper[Math.floor(Math.random() * charsetUpper.length)];
|
||||
password += charsetNum[Math.floor(Math.random() * charsetNum.length)];
|
||||
password += charsetSym[Math.floor(Math.random() * charsetSym.length)];
|
||||
|
||||
const allChars = charsetLower + charsetUpper + charsetNum + charsetSym;
|
||||
// Restliche Zeichen auffüllen
|
||||
for (let i = 4; i < length; i++) {
|
||||
password += allChars[Math.floor(Math.random() * allChars.length)];
|
||||
}
|
||||
|
||||
// Passwort durchmischen
|
||||
password = password.split('').sort(() => 0.5 - Math.random()).join('');
|
||||
|
||||
const pwField = document.getElementById('password');
|
||||
pwField.value = password;
|
||||
|
||||
// Automatisch sichtbar machen, damit der User es kopieren kann
|
||||
pwField.type = 'text';
|
||||
document.getElementById('toggle-pw-btn').textContent = '🙈';
|
||||
|
||||
updatePasswordRules();
|
||||
}
|
||||
|
||||
// PASSWORT SICHTBARKEIT UMSCHALTEN
|
||||
function togglePasswordVisibility() {
|
||||
const pwField = document.getElementById('password');
|
||||
const toggleBtn = document.getElementById('toggle-pw-btn');
|
||||
if (pwField.type === "password") {
|
||||
pwField.type = "text";
|
||||
toggleBtn.textContent = '🙈';
|
||||
} else {
|
||||
pwField.type = "password";
|
||||
toggleBtn.textContent = '👁️';
|
||||
}
|
||||
}
|
||||
|
||||
// Globale Funktion für Passwort-Update
|
||||
function updatePasswordRules() {
|
||||
const passwordInput = document.getElementById('password');
|
||||
if (!passwordInput) return;
|
||||
|
||||
const value = String(passwordInput.value || '');
|
||||
|
||||
const setRuleState = (id, ok) => {
|
||||
const node = document.getElementById(id);
|
||||
if (node) node.classList.toggle('ok', !!ok);
|
||||
};
|
||||
|
||||
setRuleState('pw-rule-length', value.length >= 12);
|
||||
setRuleState('pw-rule-lower', /[a-z]/.test(value));
|
||||
setRuleState('pw-rule-upper', /[A-Z]/.test(value));
|
||||
setRuleState('pw-rule-digit', /[0-9]/.test(value));
|
||||
setRuleState('pw-rule-symbol', /[^A-Za-z0-9]/.test(value));
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const permissionPresets = {{ permission_presets | tojson }};
|
||||
const presetSelect = document.getElementById('permission-preset');
|
||||
const useCustomPermissions = document.getElementById('use-custom-permissions');
|
||||
const customPermissions = document.getElementById('custom-permissions');
|
||||
|
||||
function applyPresetToPermissionForm(presetKey) {
|
||||
const preset = permissionPresets[presetKey] || {};
|
||||
const actionDefaults = preset.actions || {};
|
||||
const pageDefaults = preset.pages || {};
|
||||
|
||||
document.querySelectorAll('.permission-action-checkbox').forEach(function (checkbox) {
|
||||
const key = checkbox.name.replace('action_', '');
|
||||
checkbox.checked = !!actionDefaults[key];
|
||||
});
|
||||
|
||||
document.querySelectorAll('.permission-page-checkbox').forEach(function (checkbox) {
|
||||
const key = checkbox.name.replace('page_', '');
|
||||
checkbox.checked = !!pageDefaults[key];
|
||||
});
|
||||
}
|
||||
|
||||
function toggleCustomPermissions() {
|
||||
if (!useCustomPermissions || !customPermissions) {
|
||||
return;
|
||||
}
|
||||
customPermissions.style.display = useCustomPermissions.checked ? 'block' : 'none';
|
||||
}
|
||||
|
||||
if (presetSelect) {
|
||||
presetSelect.addEventListener('change', function () {
|
||||
applyPresetToPermissionForm(this.value);
|
||||
});
|
||||
applyPresetToPermissionForm(presetSelect.value);
|
||||
}
|
||||
|
||||
if (useCustomPermissions) {
|
||||
useCustomPermissions.addEventListener('change', toggleCustomPermissions);
|
||||
toggleCustomPermissions();
|
||||
}
|
||||
|
||||
const passwordInput = document.getElementById('password');
|
||||
if (passwordInput) {
|
||||
passwordInput.addEventListener('input', updatePasswordRules);
|
||||
passwordInput.addEventListener('blur', updatePasswordRules);
|
||||
updatePasswordRules();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user