Community erstellt

This commit is contained in:
2025-05-01 21:04:37 +01:00
parent d80c4c9aec
commit 2e1c3ce8b0
9 changed files with 1818 additions and 86 deletions

379
app.py
View File

@@ -24,7 +24,7 @@ from flask_migrate import Migrate
from models import (
db, User, Thought, Comment, MindMapNode, ThoughtRelation, ThoughtRating,
RelationType, Category, UserMindmap, UserMindmapNode, MindmapNote,
node_thought_association, user_thought_bookmark, node_relationship
node_thought_association, user_thought_bookmark, node_relationship, ForumCategory, ForumPost
)
# Lade .env-Datei
@@ -190,6 +190,31 @@ def create_default_categories():
db.session.commit()
print("Standard-Kategorien wurden erstellt!")
def create_forum_categories():
"""Erstellt Forum-Kategorien basierend auf Hauptknotenpunkten der Mindmap"""
# Hauptknotenpunkte abrufen (nur die, die keine Elternknoten haben)
main_nodes = MindMapNode.query.filter(~MindMapNode.id.in_(
db.session.query(node_relationship.c.child_id)
)).all()
for node in main_nodes:
# Prüfen, ob eine Forum-Kategorie für diesen Knoten bereits existiert
existing_category = ForumCategory.query.filter_by(node_id=node.id).first()
if existing_category:
continue
# Neue Kategorie erstellen
forum_category = ForumCategory(
node_id=node.id,
title=node.name,
description=node.description,
is_active=True
)
db.session.add(forum_category)
db.session.commit()
print("Forum-Kategorien wurden für alle Hauptknotenpunkte erstellt!")
def initialize_database():
"""Initialisiert die Datenbank mit Grunddaten, falls diese leer ist"""
try:
@@ -198,97 +223,34 @@ def initialize_database():
# Erstelle alle Tabellen
db.create_all()
# Prüfe, ob bereits Benutzer existieren
if User.query.count() == 0:
print("Erstelle Admin-Benutzer...")
admin = User(
username="admin",
email="admin@example.com",
is_admin=True
)
admin.set_password("admin123") # In echter Umgebung ein sicheres Passwort verwenden!
db.session.add(admin)
# Prüfen, ob bereits Kategorien existieren
categories_count = Category.query.count()
users_count = User.query.count()
# Prüfe, ob bereits Kategorien existieren
if Category.query.count() == 0:
print("Erstelle Standard-Kategorien...")
# Erstelle Standarddaten, wenn es keine Kategorien gibt
if categories_count == 0:
create_default_categories()
# Stelle sicher, dass die Standard-Knoten für die öffentliche Mindmap existieren
if MindMapNode.query.count() == 0:
print("Erstelle Standard-Knoten für die Mindmap...")
# Hauptknoten: Wissen
root_node = MindMapNode(
name="Wissen",
description="Zentrale Wissensbasis",
color_code="#4299E1",
is_public=True
# Admin-Benutzer erstellen, wenn keine Benutzer vorhanden sind
if users_count == 0:
admin_user = User(
username="admin",
email="admin@example.com",
role="admin",
is_active=True
)
db.session.add(root_node)
db.session.flush() # Um die ID zu generieren
# Verwandte Kategorien finden
philosophy = Category.query.filter_by(name="Philosophie").first()
science = Category.query.filter_by(name="Wissenschaft").first()
technology = Category.query.filter_by(name="Technologie").first()
arts = Category.query.filter_by(name="Künste").first()
# Erstelle Hauptthemenknoten
nodes = [
MindMapNode(
name="Philosophie",
description="Philosophisches Denken",
color_code="#9F7AEA",
category=philosophy,
is_public=True
),
MindMapNode(
name="Wissenschaft",
description="Wissenschaftliche Erkenntnisse",
color_code="#48BB78",
category=science,
is_public=True
),
MindMapNode(
name="Technologie",
description="Technologische Entwicklungen",
color_code="#ED8936",
category=technology,
is_public=True
),
MindMapNode(
name="Künste",
description="Künstlerische Ausdrucksformen",
color_code="#ED64A6",
category=arts,
is_public=True
)
]
# Füge Knoten zur Datenbank hinzu
for node in nodes:
db.session.add(node)
admin_user.set_password("admin123") # Sicheres Passwort in der Produktion verwenden!
db.session.add(admin_user)
db.session.commit()
# Nachdem wir die IDs haben, füge die Verbindungen hinzu
all_nodes = MindMapNode.query.all()
root = MindMapNode.query.filter_by(name="Wissen").first()
if root:
for node in all_nodes:
if node.id != root.id:
root.children.append(node)
# Speichere die Änderungen
db.session.commit()
print("Datenbankinitialisierung abgeschlossen.")
print("Admin-Benutzer wurde erstellt!")
# Forum-Kategorien erstellen
create_forum_categories()
return True
except Exception as e:
print(f"Fehler bei der Datenbankinitialisierung: {str(e)}")
db.session.rollback()
raise
print(f"Fehler bei Datenbank-Initialisierung: {e}")
return False
# Instead of before_first_request, which is deprecated in newer Flask versions
# Use a function to initialize the database that will be called during app creation
@@ -1521,6 +1483,251 @@ def refresh_mindmap():
def mindmap_page():
return render_template('mindmap.html')
# Community-Forum-Routen
@app.route('/community')
@login_required
def community():
"""Hauptseite des Community-Forums"""
forum_categories = ForumCategory.query.filter_by(is_active=True).all()
# Statistiken für jede Kategorie berechnen
categories_data = []
for category in forum_categories:
total_posts = ForumPost.query.filter_by(category_id=category.id, parent_id=None).count()
total_replies = ForumPost.query.filter(
ForumPost.category_id == category.id,
ForumPost.parent_id != None
).count()
latest_post = ForumPost.query.filter_by(category_id=category.id)\
.order_by(ForumPost.created_at.desc()).first()
categories_data.append({
'category': category,
'total_posts': total_posts,
'total_replies': total_replies,
'latest_post': latest_post
})
return render_template('community/index.html', categories_data=categories_data)
@app.route('/community/category/<int:category_id>')
@login_required
def forum_category(category_id):
"""Zeigt alle Themen in einer bestimmten Kategorie an"""
category = ForumCategory.query.get_or_404(category_id)
# Haupt-Beiträge (Threads) in dieser Kategorie
threads = ForumPost.query.filter_by(
category_id=category_id,
parent_id=None
).order_by(ForumPost.is_pinned.desc(), ForumPost.created_at.desc()).all()
# Zähle Antworten und hole den neuesten Beitrag für jeden Thread
threads_data = []
for thread in threads:
reply_count = ForumPost.query.filter_by(parent_id=thread.id).count()
latest_reply = ForumPost.query.filter_by(parent_id=thread.id)\
.order_by(ForumPost.created_at.desc()).first()
threads_data.append({
'thread': thread,
'reply_count': reply_count,
'latest_reply': latest_reply
})
return render_template('community/category.html',
category=category,
threads_data=threads_data,
node=category.node)
@app.route('/community/post/<int:post_id>')
@login_required
def forum_post(post_id):
"""Zeigt einen Beitrag und alle seine Antworten an"""
post = ForumPost.query.get_or_404(post_id)
# Wenn es sich um eine Antwort handelt, leite zur übergeordneten Beitragsseite weiter
if post.parent_id:
return redirect(url_for('forum_post', post_id=post.parent_id))
# Erhöhe die Ansichtszahl
post.view_count += 1
db.session.commit()
# Hole alle Antworten zu diesem Beitrag
replies = ForumPost.query.filter_by(parent_id=post_id)\
.order_by(ForumPost.created_at).all()
return render_template('community/post.html',
post=post,
replies=replies,
category=post.category)
@app.route('/community/new-post/<int:category_id>', methods=['GET', 'POST'])
@login_required
def new_post(category_id):
"""Erstellt einen neuen Beitrag in einer Kategorie"""
category = ForumCategory.query.get_or_404(category_id)
if request.method == 'POST':
title = request.form.get('title')
content = request.form.get('content')
if not title or not content:
flash('Bitte fülle alle Pflichtfelder aus.', 'error')
return redirect(url_for('new_post', category_id=category_id))
# Neuen Beitrag erstellen
post = ForumPost(
title=title,
content=content,
user_id=current_user.id,
category_id=category_id
)
db.session.add(post)
db.session.commit()
flash('Dein Beitrag wurde erfolgreich erstellt.', 'success')
return redirect(url_for('forum_post', post_id=post.id))
return render_template('community/new_post.html', category=category)
@app.route('/community/reply/<int:post_id>', methods=['POST'])
@login_required
def reply_to_post(post_id):
"""Antwortet auf einen bestehenden Beitrag"""
parent_post = ForumPost.query.get_or_404(post_id)
# Stelle sicher, dass der Beitrag nicht gesperrt ist
if parent_post.is_locked:
flash('Dieser Beitrag ist gesperrt und kann keine Antworten mehr erhalten.', 'error')
return redirect(url_for('forum_post', post_id=post_id))
content = request.form.get('content')
if not content:
flash('Die Antwort darf nicht leer sein.', 'error')
return redirect(url_for('forum_post', post_id=post_id))
# Erstelle eine Antwort
reply = ForumPost(
title=f"Re: {parent_post.title}",
content=content,
user_id=current_user.id,
category_id=parent_post.category_id,
parent_id=post_id
)
db.session.add(reply)
db.session.commit()
flash('Deine Antwort wurde erfolgreich hinzugefügt.', 'success')
return redirect(url_for('forum_post', post_id=post_id))
@app.route('/community/edit-post/<int:post_id>', methods=['GET', 'POST'])
@login_required
def edit_post(post_id):
"""Bearbeitet einen bestehenden Beitrag"""
post = ForumPost.query.get_or_404(post_id)
# Überprüfe, ob der Benutzer der Autor ist oder Admin-Rechte hat
if post.user_id != current_user.id and current_user.role != 'admin':
flash('Du hast keine Berechtigung, diesen Beitrag zu bearbeiten.', 'error')
return redirect(url_for('forum_post', post_id=post.parent_id or post.id))
if request.method == 'POST':
title = request.form.get('title')
content = request.form.get('content')
if not title or not content:
flash('Bitte fülle alle Pflichtfelder aus.', 'error')
return redirect(url_for('edit_post', post_id=post_id))
# Aktualisiere den Beitrag
post.title = title
post.content = content
post.updated_at = datetime.utcnow()
db.session.commit()
flash('Dein Beitrag wurde erfolgreich aktualisiert.', 'success')
return redirect(url_for('forum_post', post_id=post.parent_id or post.id))
return render_template('community/edit_post.html', post=post)
@app.route('/community/delete-post/<int:post_id>', methods=['POST'])
@login_required
def delete_post(post_id):
"""Löscht einen Beitrag"""
post = ForumPost.query.get_or_404(post_id)
# Überprüfe, ob der Benutzer der Autor ist oder Admin-Rechte hat
if post.user_id != current_user.id and current_user.role != 'admin':
flash('Du hast keine Berechtigung, diesen Beitrag zu löschen.', 'error')
return redirect(url_for('forum_post', post_id=post.parent_id or post.id))
# Bestimme, wohin nach dem Löschen weitergeleitet wird
if post.parent_id:
redirect_url = url_for('forum_post', post_id=post.parent_id)
else:
redirect_url = url_for('forum_category', category_id=post.category_id)
# Lösche den Beitrag und seine Antworten
if not post.parent_id: # Wenn es ein Hauptbeitrag ist, lösche auch alle Antworten
ForumPost.query.filter_by(parent_id=post_id).delete()
db.session.delete(post)
db.session.commit()
flash('Der Beitrag wurde erfolgreich gelöscht.', 'success')
return redirect(redirect_url)
@app.route('/community/toggle-pin/<int:post_id>', methods=['POST'])
@login_required
def toggle_pin_post(post_id):
"""Fixiert oder löst einen Beitrag von der Fixierung"""
# Nur Admins und Moderatoren können Beiträge fixieren
if current_user.role not in ['admin', 'moderator']:
flash('Du hast keine Berechtigung, Beiträge zu fixieren.', 'error')
return redirect(url_for('forum_post', post_id=post_id))
post = ForumPost.query.get_or_404(post_id)
# Nur Hauptbeiträge können fixiert werden
if post.parent_id:
flash('Nur Hauptbeiträge können fixiert werden.', 'error')
return redirect(url_for('forum_post', post_id=post.parent_id))
# Ändere den Status
post.is_pinned = not post.is_pinned
db.session.commit()
status = 'fixiert' if post.is_pinned else 'nicht mehr fixiert'
flash(f'Der Beitrag ist jetzt {status}.', 'success')
return redirect(url_for('forum_post', post_id=post_id))
@app.route('/community/toggle-lock/<int:post_id>', methods=['POST'])
@login_required
def toggle_lock_post(post_id):
"""Sperrt oder entsperrt einen Beitrag für weitere Antworten"""
# Nur Admins und Moderatoren können Beiträge sperren
if current_user.role not in ['admin', 'moderator']:
flash('Du hast keine Berechtigung, Beiträge zu sperren.', 'error')
return redirect(url_for('forum_post', post_id=post_id))
post = ForumPost.query.get_or_404(post_id)
# Nur Hauptbeiträge können gesperrt werden
if post.parent_id:
flash('Nur Hauptbeiträge können gesperrt werden.', 'error')
return redirect(url_for('forum_post', post_id=post.parent_id))
# Ändere den Status
post.is_locked = not post.is_locked
db.session.commit()
status = 'gesperrt' if post.is_locked else 'entsperrt'
flash(f'Der Beitrag ist jetzt {status}.', 'success')
return redirect(url_for('forum_post', post_id=post_id))
# Fehlerbehandlung
@app.errorhandler(404)
def not_found(e):