Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d07577220 | |||
| f3c7b6cd55 | |||
| c2bfed710d | |||
| 5d90eab154 | |||
| 1eadfff9e1 | |||
| 18f096b3f2 | |||
| 4b895a52ab | |||
| 3a504afe37 | |||
| fb2ba86140 | |||
| 72e8a8cf5d | |||
| a158d16f62 | |||
| b8347b80f2 | |||
| 31356e7178 |
+17
-4
@@ -2301,7 +2301,12 @@ def _upload_student_cards_excel():
|
||||
row_errors.append('Vorname und Nachname fehlen')
|
||||
|
||||
if not ausweis_id and student_name:
|
||||
ausweis_id = generate_ausweis_id(existing_ids)
|
||||
ausweis_id = generate_ausweis_id()
|
||||
if ausweis_id in existing_ids:
|
||||
row_errors.append(f'Automatisch erzeugte Ausweis-ID {ausweis_id} existiert bereits')
|
||||
else:
|
||||
while ausweis_id in existing_ids:
|
||||
ausweis_id = generate_ausweis_id()
|
||||
validation_warnings.append((row_number, f'Ausweis-ID wurde automatisch erzeugt: {ausweis_id}'))
|
||||
existing_ids.add(ausweis_id.upper())
|
||||
elif ausweis_id and not rollover_mode:
|
||||
@@ -4850,7 +4855,8 @@ def student_cards_admin():
|
||||
if ausweis_id:
|
||||
existing = student_cards.find_one({'AusweisId': ausweis_id})
|
||||
else:
|
||||
ausweis_id = generate_ausweis_id()
|
||||
generated_ausweis_id = generate_ausweis_id()
|
||||
ausweis_id = generated_ausweis_id
|
||||
existing = False
|
||||
if not ausweis_ident:
|
||||
ausweis_ident = ausweis_id
|
||||
@@ -4890,10 +4896,10 @@ def student_cards_admin():
|
||||
|
||||
return render_template(
|
||||
'student_cards_admin.html',
|
||||
student_cards=all_cards, # Hier all_cards übergeben
|
||||
student_cards=all_cards,
|
||||
edit_mode=edit_mode,
|
||||
form_data=form_data,
|
||||
available_classes=available_classes, # Enthält nun die lesbaren Klassen
|
||||
available_classes=available_classes,
|
||||
config=cfg.get_school_info(),
|
||||
email_service_enabled=cfg.MODULES.is_enabled('mail')
|
||||
)
|
||||
@@ -13769,6 +13775,13 @@ def upload_csv_batch():
|
||||
"images_failed": error_count
|
||||
}), 200
|
||||
|
||||
@app.route('/get/asv_export', methods=['GET'])
|
||||
def get_asv_export():
|
||||
if 'username' not in session:
|
||||
flash('Bitte melden Sie sich an.', 'error')
|
||||
return redirect(url_for('login'))
|
||||
return send_from_directory(app.static_folder, 'img/asv_export_invario_2026.exfj')
|
||||
|
||||
"""
|
||||
@app.route('/test_email')
|
||||
def test_email():
|
||||
|
||||
Binary file not shown.
@@ -78,11 +78,11 @@
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="filter_name_1">Name Filter 1</label>
|
||||
<input type="text" id="filter_name_1" name="filter_name_1" value="{{ filter_names.get('1', 'Klassenstufe') }}" placeholder="z. B. Klassenstufe">
|
||||
<input type="text" id="filter_name_1" name="filter_name_1" value="{{ filter_names.get('1', 'Fachgebiet') }}" placeholder="z. B. Fachgebiet">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="filter_name_2">Name Filter 2</label>
|
||||
<input type="text" id="filter_name_2" name="filter_name_2" value="{{ filter_names.get('2', 'Fach') }}" placeholder="z. B. Fach">
|
||||
<input type="text" id="filter_name_2" name="filter_name_2" value="{{ filter_names.get('2', 'Klassenstufe') }}" placeholder="z. B. Klassenstufe">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="filter_name_3">Name Filter 3</label>
|
||||
|
||||
+23
-19
@@ -1135,12 +1135,7 @@
|
||||
<a class="nav-link quick-link-pill {% if current_path == url_for('terminplaner.configure') %}nav-active{% endif %}" href="{{ url_for('terminplaner.configure') }}" data-tutorial-tip="Neuen Terminplan erstellen und an Ihr Team versenden.">Neue Planung</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item" data-nav-fixed="true">
|
||||
<button type="button" class="btn btn-link nav-link px-3" data-theme-toggle aria-label="Dark Mode umschalten" aria-pressed="false" title="Theme umschalten">
|
||||
<span class="theme-icon-light" style="display: none;">☀️</span>
|
||||
<span class="theme-icon-dark" style="display: none;">🌙</span>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<li class="nav-item dropdown ms-lg-auto">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="termMoreDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false" title="Weitere Optionen">Mehr Optionen</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="termMoreDropdown">
|
||||
@@ -1199,6 +1194,13 @@
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('change_password') }}">Passwort ändern</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li class="dropdown-item" data-nav-fixed="true">
|
||||
<button type="button" class="btn btn-link nav-link px-3" data-theme-toggle aria-label="Dark Mode umschalten" aria-pressed="false" title="Theme umschalten">
|
||||
<span class="theme-icon-light" style="display: none;">☀️</span>
|
||||
<span class="theme-icon-dark" style="display: none;">🌙</span>
|
||||
</button>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('logout') }}">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -1228,12 +1230,6 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li class="nav-item" data-nav-fixed="true">
|
||||
<button type="button" class="btn btn-link nav-link px-3" data-theme-toggle aria-label="Dark Mode umschalten" aria-pressed="false" title="Theme umschalten">
|
||||
<span class="theme-icon-light" style="display: none;">☀️</span>
|
||||
<span class="theme-icon-dark" style="display: none;">🌙</span>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<li class="nav-item dropdown ms-lg-auto">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="invMoreDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false" title="Weitere Optionen">
|
||||
@@ -1321,6 +1317,13 @@
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('change_password') }}">Passwort ändern</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li class="dropdown-item" data-nav-fixed="true">
|
||||
<button type="button" class="btn btn-link nav-link px-3" data-theme-toggle aria-label="Dark Mode umschalten" aria-pressed="false" title="Theme umschalten">
|
||||
<span class="theme-icon-light" style="display: none;">☀️</span>
|
||||
<span class="theme-icon-dark" style="display: none;">🌙</span>
|
||||
</button>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('logout') }}">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -1342,7 +1345,7 @@
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0" id="libraryNavList">
|
||||
{% if current_permissions.pages.get('library_view', False) %}
|
||||
<li class="nav-item" data-nav-fixed="true">
|
||||
<a class="nav-link {% if current_path == url_for('library_view') %}nav-active{% endif %}" href="{{ url_for('library_view') }}" data-tutorial-tip="Die Bibliothek zeigt Ihnen alle Medien und verfügbaren Bücher.">Medien/Ausleihe</a>
|
||||
<a class="nav-link {% if current_path == url_for('library_view') %}nav-active{% endif %}" href="{{ url_for('library_view') }}" data-tutorial-tip="Die Bibliothek zeigt Ihnen alle Medien und verfügbaren Bücher.">Ausleihe/Rückgabe</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if 'username' in session %}
|
||||
@@ -1359,12 +1362,6 @@
|
||||
<a class="nav-link nav-priority-link {% if current_path == url_for('library_admin') %}nav-active{% endif %}" href="{{ url_for('library_admin') }}" data-tutorial-tip="Neue Bibliotheksmedien können hier aufgenommen werden.">📖 Hochladen</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item" data-nav-fixed="true">
|
||||
<button type="button" class="btn btn-link nav-link px-3" data-theme-toggle aria-label="Dark Mode umschalten" aria-pressed="false" title="Theme umschalten">
|
||||
<span class="theme-icon-light" style="display: none;">☀️</span>
|
||||
<span class="theme-icon-dark" style="display: none;">🌙</span>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<li class="nav-item dropdown ms-lg-auto">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="libMoreDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false" title="Weitere Optionen">
|
||||
@@ -1431,6 +1428,13 @@
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('change_password') }}">Passwort ändern</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li class="dropdown-item" data-nav-fixed="true">
|
||||
<button type="button" class="btn btn-link nav-link px-3" data-theme-toggle aria-label="Dark Mode umschalten" aria-pressed="false" title="Theme umschalten">
|
||||
<span class="theme-icon-light" style="display: none;">☀️</span>
|
||||
<span class="theme-icon-dark" style="display: none;">🌙</span>
|
||||
</button>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('logout') }}">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -26,18 +26,27 @@
|
||||
<form method="POST" action="{{ url_for('change_password') }}">
|
||||
<div class="form-group">
|
||||
<label for="current_password">Aktuelles Passwort:</label>
|
||||
<input type="password" id="current_password" name="current_password" required>
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control" id="current_password" name="current_password" required>
|
||||
<button class="btn btn-outline-secondary" type="button" onclick="togglePasswordVisibility('current_password', this)">Anzeigen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="new_password">Neues Passwort:</label>
|
||||
<input type="password" id="new_password" name="new_password" required>
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control" id="new_password" name="new_password" required>
|
||||
<button class="btn btn-outline-secondary" type="button" onclick="togglePasswordVisibility('new_password', this)">Anzeigen</button>
|
||||
</div>
|
||||
<small class="form-text text-muted">Das Passwort sollte mindestens 6 Zeichen lang sein.</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="confirm_password">Neues Passwort bestätigen:</label>
|
||||
<input type="password" id="confirm_password" name="confirm_password" required>
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control" id="confirm_password" name="confirm_password" required>
|
||||
<button class="btn btn-outline-secondary" type="button" onclick="togglePasswordVisibility('confirm_password', this)">Anzeigen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
@@ -48,6 +57,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function togglePasswordVisibility(fieldId, button) {
|
||||
var passwordInput = document.getElementById(fieldId);
|
||||
|
||||
if (passwordInput.type === 'password') {
|
||||
passwordInput.type = 'text';
|
||||
button.textContent = 'Verbergen';
|
||||
} else {
|
||||
passwordInput.type = 'password';
|
||||
button.textContent = 'Anzeigen';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.password-change-form {
|
||||
max-width: 500px;
|
||||
@@ -74,7 +97,7 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input[type="password"] {
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ced4da;
|
||||
@@ -122,6 +145,16 @@
|
||||
.btn-secondary:hover {
|
||||
background-color: #5a6268;
|
||||
}
|
||||
|
||||
.btn-outline-secondary {
|
||||
border: 1px solid #ced4da;
|
||||
background-color: #f8f9fa;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.btn-outline-secondary:hover {
|
||||
background-color: #e2e6ea;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 12px;
|
||||
@@ -141,4 +174,4 @@
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -278,8 +278,8 @@
|
||||
<div class="multi-filter">
|
||||
{% for idx in range(4) %}
|
||||
<div class="form-group">
|
||||
<label for="filter1-{{ idx + 1 }}">Wert {{ idx + 1 }}:</label>
|
||||
<select id="filter1-{{ idx + 1 }}" name="filter" class="filter-dropdown-select" data-selected="{{ item.Filter[idx] if item.Filter and item.Filter|length > idx else '' }}">
|
||||
<label for="filter2-{{ idx + 1 }}">Wert {{ idx + 1 }}:</label>
|
||||
<select id="filter2-{{ idx + 1 }}" name="filter" class="filter-dropdown-select" data-selected="{{ item.Filter[idx] if item.Filter and item.Filter|length > idx else '' }}">
|
||||
<option value="">-- Optional --</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -290,8 +290,8 @@
|
||||
<div class="multi-filter">
|
||||
{% for idx in range(4) %}
|
||||
<div class="form-group">
|
||||
<label for="filter2-{{ idx + 1 }}">Wert {{ idx + 1 }}:</label>
|
||||
<select id="filter2-{{ idx + 1 }}" name="filter2" class="filter-dropdown-select" data-selected="{{ item.Filter2[idx] if item.Filter2 and item.Filter2|length > idx else '' }}">
|
||||
<label for="filter1-{{ idx + 1 }}">Wert {{ idx + 1 }}:</label>
|
||||
<select id="filter1-{{ idx + 1 }}" name="filter2" class="filter-dropdown-select" data-selected="{{ item.Filter2[idx] if item.Filter2 and item.Filter2|length > idx else '' }}">
|
||||
<option value="">-- Optional --</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -2343,7 +2343,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
||||
<div class="filter-container">
|
||||
<div class="filter-group">
|
||||
<div class="filter-header">
|
||||
<label>{{ filter_names.get('2', 'Fach') }}</label>
|
||||
<label>{{ filter_names.get('1', 'Fach') }}</label>
|
||||
<button type="button" class="filter-toggle" onclick="toggleFilterDropdown('filter1-dropdown')">▼</button>
|
||||
<button type="button" class="clear-filter" onclick="clearFilter(1)">Clear</button>
|
||||
</div>
|
||||
@@ -2357,7 +2357,7 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
||||
|
||||
<div class="filter-group">
|
||||
<div class="filter-header">
|
||||
<label>{{ filter_names.get('1', 'Klassenstufe') }}</label>
|
||||
<label>{{ filter_names.get('2', 'Klassenstufe') }}</label>
|
||||
<button type="button" class="filter-toggle" onclick="toggleFilterDropdown('filter2-dropdown')">▼</button>
|
||||
<button type="button" class="clear-filter" onclick="clearFilter(2)">Clear</button>
|
||||
</div>
|
||||
@@ -4112,17 +4112,17 @@ document.addEventListener('DOMContentLoaded', ()=>{
|
||||
</div>
|
||||
|
||||
<div class="detail-group">
|
||||
<div class="detail-label">Unterrichtsfach:</div>
|
||||
<div class="detail-label">{{ filter_names.get('1', 'Unterrichtsfach') }}:</div>
|
||||
<div class="detail-value">${escapeHtml(filter1Array.join(', ') || '-')}</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-group">
|
||||
<div class="detail-label">Jahrgangsstufe:</div>
|
||||
<div class="detail-label">{{ filter_names.get('2', 'Jahrgangsstufe') }}:</div>
|
||||
<div class="detail-value">${escapeHtml(filter2Array.join(', ') || '-')}</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-group">
|
||||
<div class="detail-label">Schlagwort:</div>
|
||||
<div class="detail-label">{{ filter_names.get('3', 'Schlagwort') }}:</div>
|
||||
<div class="detail-value">${escapeHtml(filter3Array.join(', ') || '-')}</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -514,6 +514,7 @@
|
||||
<p style="margin:0 0 12px 0; color:#555; font-size:13px; line-height:1.4;">
|
||||
Laden Sie eine <strong>.xlsx</strong>- oder <strong>.csv</strong>-Datei hoch (z. B. aus <strong>ASV</strong>). Erkannt werden Rufame, Nachname, Klasse*, Ausweis-ID*, lokales Differenzierungsmerkmal*, Notizen* & Ausleihdauer*.
|
||||
Hinweis: Die Daten werden Validiert und müssen dann noch einmal aus sicherheits gründen hochgeladen werden!
|
||||
Im folgenden ist eine export vorlage für ihre ASV-Software hinterlegt: <a href="{{ url_for('get_asv_export') }}" target="_blank" style="color:#007bff; text-decoration:underline;">ASV Export Vorlage</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -865,38 +865,6 @@
|
||||
<!-- Normal Item Mode: Standard Filters -->
|
||||
<div class="filter-inputs">
|
||||
<h3>{{ filter_names.get('1', 'Jahrgangsstufe') }}</h3>
|
||||
<div class="multi-filter">
|
||||
<div class="form-group">
|
||||
<label for="filter1-1">Wert 1:</label>
|
||||
<select id="filter1-1" name="filter" class="filter-dropdown-select" required>
|
||||
<option value="">-- Bitte auswählen --</option>
|
||||
<!-- Options will be loaded by JavaScript -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="filter1-2">Wert 2:</label>
|
||||
<select id="filter1-2" name="filter" class="filter-dropdown-select">
|
||||
<option value="">-- Optional --</option>
|
||||
<!-- Options will be loaded by JavaScript -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="filter1-3">Wert 3:</label>
|
||||
<select id="filter1-3" name="filter" class="filter-dropdown-select">
|
||||
<option value="">-- Optional --</option>
|
||||
<!-- Options will be loaded by JavaScript -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="filter1-4">Wert 4:</label>
|
||||
<select id="filter1-4" name="filter" class="filter-dropdown-select">
|
||||
<option value="">-- Optional --</option>
|
||||
<!-- Options will be loaded by JavaScript -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>{{ filter_names.get('2', 'Fachgebiet') }}</h3>
|
||||
<div class="multi-filter">
|
||||
<div class="form-group">
|
||||
<label for="filter2-1">Wert 1:</label>
|
||||
@@ -928,6 +896,38 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>{{ filter_names.get('2', 'Fachgebiet') }}</h3>
|
||||
<div class="multi-filter">
|
||||
<div class="form-group">
|
||||
<label for="filter1-1">Wert 1:</label>
|
||||
<select id="filter1-1" name="filter" class="filter-dropdown-select" required>
|
||||
<option value="">-- Bitte auswählen --</option>
|
||||
<!-- Options will be loaded by JavaScript -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="filter1-2">Wert 2:</label>
|
||||
<select id="filter1-2" name="filter" class="filter-dropdown-select">
|
||||
<option value="">-- Optional --</option>
|
||||
<!-- Options will be loaded by JavaScript -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="filter1-3">Wert 3:</label>
|
||||
<select id="filter1-3" name="filter" class="filter-dropdown-select">
|
||||
<option value="">-- Optional --</option>
|
||||
<!-- Options will be loaded by JavaScript -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="filter1-4">Wert 4:</label>
|
||||
<select id="filter1-4" name="filter" class="filter-dropdown-select">
|
||||
<option value="">-- Optional --</option>
|
||||
<!-- Options will be loaded by JavaScript -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>{{ filter_names.get('3', 'Schlagwort') }}</h3>
|
||||
<div class="multi-filter">
|
||||
<div class="form-group">
|
||||
|
||||
Reference in New Issue
Block a user