chore: Änderungen commited

This commit is contained in:
2025-05-10 23:20:10 +02:00
parent 02d1801fc9
commit d0f32a8355
2 changed files with 297 additions and 11 deletions

69
app.py
View File

@@ -861,11 +861,17 @@ def api_get_user_mindmaps():
@app.route('/api/mindmaps/<int:mindmap_id>', methods=['GET'])
@login_required
@handle_api_exception
def api_get_user_mindmap_detail(mindmap_id):
mindmap = UserMindmap.query.filter_by(id=mindmap_id, user_id=current_user.id).first_or_404()
# Bestehende Logik von get_user_mindmap kann hier wiederverwendet oder angepasst werden
# Für eine einfache Detailansicht:
return jsonify({
# Berechtigungsprüfung (mindestens READ-Zugriff)
has_permission, error_msg = check_mindmap_permission(mindmap_id, "READ")
if not has_permission:
return ErrorHandler.api_error(error_msg, 403)
mindmap = UserMindmap.query.get_or_404(mindmap_id)
# Logik für eine detaillierte Ansicht
result = {
'id': mindmap.id,
'name': mindmap.name,
'description': mindmap.description,
@@ -873,18 +879,44 @@ def api_get_user_mindmap_detail(mindmap_id):
'user_id': mindmap.user_id,
'created_at': mindmap.created_at.isoformat(),
'last_modified': mindmap.last_modified.isoformat(),
# Hier könnten auch Knoten und Notizen hinzugefügt werden, ähnlich wie in get_user_mindmap
})
'is_owner': mindmap.user_id == current_user.id
}
# Berechtigungsinformationen hinzufügen, falls nicht der Eigentümer
if mindmap.user_id != current_user.id:
share = MindmapShare.query.filter_by(
mindmap_id=mindmap_id,
shared_with_id=current_user.id
).first()
if share:
result['permission'] = share.permission_type.name
result['owner'] = {
'id': mindmap.user_id,
'username': User.query.get(mindmap.user_id).username
}
return jsonify(result)
@app.route('/api/mindmaps/<int:mindmap_id>', methods=['PUT'])
@login_required
@handle_api_exception
def api_update_user_mindmap(mindmap_id):
mindmap = UserMindmap.query.filter_by(id=mindmap_id, user_id=current_user.id).first_or_404()
# Berechtigungsprüfung (mindestens EDIT-Zugriff)
has_permission, error_msg = check_mindmap_permission(mindmap_id, "EDIT")
if not has_permission:
return ErrorHandler.api_error(error_msg, 403)
mindmap = UserMindmap.query.get_or_404(mindmap_id)
data = request.get_json()
# Grundlegende Informationen aktualisieren
mindmap.name = data.get('name', mindmap.name)
mindmap.description = data.get('description', mindmap.description)
mindmap.is_private = data.get('is_private', mindmap.is_private)
# is_private kann nur vom Eigentümer geändert werden
if mindmap.user_id == current_user.id and 'is_private' in data:
mindmap.is_private = data.get('is_private')
db.session.commit()
return jsonify({
@@ -892,16 +924,31 @@ def api_update_user_mindmap(mindmap_id):
'name': mindmap.name,
'description': mindmap.description,
'is_private': mindmap.is_private,
'last_modified': mindmap.last_modified.isoformat()
'last_modified': mindmap.last_modified.isoformat(),
'success': True
})
@app.route('/api/mindmaps/<int:mindmap_id>', methods=['DELETE'])
@login_required
@handle_api_exception
def api_delete_user_mindmap(mindmap_id):
mindmap = UserMindmap.query.filter_by(id=mindmap_id, user_id=current_user.id).first_or_404()
# Nur der Eigentümer kann eine Mindmap löschen
mindmap = UserMindmap.query.get_or_404(mindmap_id)
if mindmap.user_id != current_user.id:
return ErrorHandler.api_error("Nur der Eigentümer kann eine Mindmap löschen.", 403)
# Alle Freigaben löschen
MindmapShare.query.filter_by(mindmap_id=mindmap_id).delete()
# Mindmap löschen
db.session.delete(mindmap)
db.session.commit()
return jsonify({'message': 'Mindmap erfolgreich gelöscht'}), 200
return jsonify({
'success': True,
'message': 'Mindmap erfolgreich gelöscht'
}), 200
# API-Endpunkte für Mindmap-Daten (öffentlich und benutzerspezifisch)
@app.route('/api/mindmap/public')

View File

@@ -410,6 +410,12 @@
<i class="fas fa-save"></i>
<span>Layout speichern</span>
</button>
{% if mindmap.user_id == current_user.id %}
<button id="share-btn" class="mindmap-action-btn">
<i class="fas fa-share-alt"></i>
<span>Freigeben</span>
</button>
{% endif %}
<button id="export-btn" class="mindmap-action-btn">
<i class="fas fa-file-export"></i>
<span>Exportieren</span>
@@ -1329,6 +1335,239 @@
});
});
});
// Freigabe-Funktionalität implementieren
if (document.getElementById('share-btn')) {
document.getElementById('share-btn').addEventListener('click', function() {
const mindmapId = parseInt("{{ mindmap.id }}");
// Dialog für Mindmap-Freigabe anzeigen
showShareDialog(mindmapId);
});
}
// Funktion zum Anzeigen des Freigabe-Dialogs
function showShareDialog(mindmapId) {
// Dialog erstellen
const overlay = document.createElement('div');
overlay.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50';
overlay.id = 'share-dialog-overlay';
overlay.innerHTML = `
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 max-w-lg w-full mx-4">
<h3 class="text-xl font-bold mb-4">Mindmap freigeben</h3>
<div class="mb-6">
<div class="flex flex-col space-y-4">
<div>
<label class="block mb-2 text-sm font-medium">E-Mail-Adresse des Benutzers:</label>
<input type="email" id="share-email" class="w-full p-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white"
placeholder="beispiel@mail.com">
</div>
<div>
<label class="block mb-2 text-sm font-medium">Berechtigungen:</label>
<select id="share-permission" class="w-full p-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option value="READ">Nur-Lesen</option>
<option value="EDIT">Bearbeiten</option>
<option value="ADMIN">Administrator</option>
</select>
</div>
<button id="add-share-btn" class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg">
<i class="fas fa-plus mr-1"></i> Hinzufügen
</button>
</div>
</div>
<div class="mb-4">
<h4 class="font-semibold mb-2">Aktuelle Freigaben</h4>
<div id="share-list" class="max-h-60 overflow-y-auto border rounded-lg p-2 dark:border-gray-700">
<div class="text-center text-sm opacity-70 p-4">Lade Freigaben...</div>
</div>
</div>
<div class="flex justify-end gap-2 mt-4">
<button id="close-share-dialog" class="px-4 py-2 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 rounded-lg">Schließen</button>
</div>
</div>
`;
document.body.appendChild(overlay);
// Aktuelle Freigaben laden
loadShares(mindmapId);
// Event-Listener für Buttons
document.getElementById('close-share-dialog').addEventListener('click', function() {
document.getElementById('share-dialog-overlay').remove();
});
document.getElementById('add-share-btn').addEventListener('click', function() {
const email = document.getElementById('share-email').value.trim();
const permission = document.getElementById('share-permission').value;
if (!email) {
showUINotification('Bitte geben Sie eine E-Mail-Adresse ein.', 'error');
return;
}
// Mindmap freigeben
fetch(`/api/mindmap/${mindmapId}/share`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token() }}'
},
body: JSON.stringify({
email: email,
permission: permission
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
showUINotification(data.message, 'success');
document.getElementById('share-email').value = '';
loadShares(mindmapId); // Freigaben neu laden
} else {
showUINotification(data.error, 'error');
}
})
.catch(error => {
console.error('Fehler beim Freigeben der Mindmap:', error);
showUINotification('Fehler beim Freigeben der Mindmap.', 'error');
});
});
}
// Funktion zum Laden der aktuellen Freigaben
function loadShares(mindmapId) {
const shareList = document.getElementById('share-list');
if (!shareList) return;
shareList.innerHTML = '<div class="text-center text-sm opacity-70 p-4">Lade Freigaben...</div>';
fetch(`/api/mindmap/${mindmapId}/shares`)
.then(response => response.json())
.then(data => {
if (data.success) {
const shares = data.shares;
if (shares.length === 0) {
shareList.innerHTML = '<div class="text-center text-sm opacity-70 p-4">Keine Freigaben vorhanden.</div>';
return;
}
shareList.innerHTML = '';
shares.forEach(share => {
const shareItem = document.createElement('div');
shareItem.className = 'flex justify-between items-center p-2 border-b dark:border-gray-700 last:border-0';
const permissionClass = {
'READ': 'text-blue-500 dark:text-blue-400',
'EDIT': 'text-green-500 dark:text-green-400',
'ADMIN': 'text-purple-500 dark:text-purple-400'
}[share.permission] || 'text-gray-500';
shareItem.innerHTML = `
<div>
<div class="font-medium">${share.username}</div>
<div class="text-xs opacity-70">${share.email}</div>
</div>
<div class="flex items-center space-x-2">
<select class="share-permission-select text-sm p-1 rounded ${permissionClass}" data-share-id="${share.id}">
<option value="READ" ${share.permission === 'READ' ? 'selected' : ''}>Nur-Lesen</option>
<option value="EDIT" ${share.permission === 'EDIT' ? 'selected' : ''}>Bearbeiten</option>
<option value="ADMIN" ${share.permission === 'ADMIN' ? 'selected' : ''}>Administrator</option>
</select>
<button class="text-red-500 hover:text-red-700" data-share-id="${share.id}">
<i class="fas fa-times"></i>
</button>
</div>
`;
// Event-Listener zum Aktualisieren der Berechtigungen
const permissionSelect = shareItem.querySelector('.share-permission-select');
permissionSelect.addEventListener('change', function() {
const shareId = this.dataset.shareId;
const newPermission = this.value;
updateSharePermission(shareId, newPermission);
});
// Event-Listener zum Löschen der Freigabe
const deleteButton = shareItem.querySelector('button[data-share-id]');
deleteButton.addEventListener('click', function() {
const shareId = this.dataset.shareId;
if (confirm('Möchten Sie diese Freigabe wirklich widerrufen?')) {
revokeShare(shareId, mindmapId);
}
});
shareList.appendChild(shareItem);
});
} else {
shareList.innerHTML = '<div class="text-center text-sm text-red-500 p-4">Fehler beim Laden der Freigaben.</div>';
}
})
.catch(error => {
console.error('Fehler beim Laden der Freigaben:', error);
shareList.innerHTML = '<div class="text-center text-sm text-red-500 p-4">Fehler beim Laden der Freigaben.</div>';
});
}
// Funktion zum Aktualisieren der Berechtigungen einer Freigabe
function updateSharePermission(shareId, permission) {
fetch(`/api/mindmap/shares/${shareId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token() }}'
},
body: JSON.stringify({
permission: permission
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
showUINotification(data.message, 'success');
} else {
showUINotification(data.error, 'error');
}
})
.catch(error => {
console.error('Fehler beim Aktualisieren der Berechtigungen:', error);
showUINotification('Fehler beim Aktualisieren der Berechtigungen.', 'error');
});
}
// Funktion zum Widerrufen einer Freigabe
function revokeShare(shareId, mindmapId) {
fetch(`/api/mindmap/shares/${shareId}`, {
method: 'DELETE',
headers: {
'X-CSRFToken': '{{ csrf_token() }}'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
showUINotification(data.message, 'success');
loadShares(mindmapId); // Freigaben neu laden
} else {
showUINotification(data.error, 'error');
}
})
.catch(error => {
console.error('Fehler beim Widerrufen der Freigabe:', error);
showUINotification('Fehler beim Widerrufen der Freigabe.', 'error');
});
}
} else {
// Fallback, falls mindmapData null ist