new file: .gitignore
new file: README.md new file: database/schema.sql new file: paper-plugin/pom.xml new file: paper-plugin/src/main/java/de/simolzimol/mclogger/paper/PaperLoggerPlugin.java new file: paper-plugin/src/main/java/de/simolzimol/mclogger/paper/commands/MCLoggerCommand.java new file: paper-plugin/src/main/java/de/simolzimol/mclogger/paper/database/DatabaseManager.java new file: paper-plugin/src/main/java/de/simolzimol/mclogger/paper/listeners/BlockListener.java new file: paper-plugin/src/main/java/de/simolzimol/mclogger/paper/listeners/EntityListener.java new file: paper-plugin/src/main/java/de/simolzimol/mclogger/paper/listeners/InventoryListener.java new file: paper-plugin/src/main/java/de/simolzimol/mclogger/paper/listeners/LuckPermsListener.java new file: paper-plugin/src/main/java/de/simolzimol/mclogger/paper/listeners/PlayerChatCommandListener.java new file: paper-plugin/src/main/java/de/simolzimol/mclogger/paper/listeners/PlayerDeathListener.java new file: paper-plugin/src/main/java/de/simolzimol/mclogger/paper/listeners/PlayerMiscListener.java new file: paper-plugin/src/main/java/de/simolzimol/mclogger/paper/listeners/PlayerSessionListener.java new file: paper-plugin/src/main/java/de/simolzimol/mclogger/paper/listeners/WorldListener.java new file: paper-plugin/src/main/resources/config.yml new file: paper-plugin/src/main/resources/plugin.yml new file: paper-plugin/target/classes/config.yml new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/PaperLoggerPlugin.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/commands/MCLoggerCommand$RsConsumer.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/commands/MCLoggerCommand.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/database/DatabaseManager$ThrowingRunnable.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/database/DatabaseManager.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/listeners/BlockListener.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/listeners/EntityListener.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/listeners/InventoryListener.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/listeners/LuckPermsListener.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/listeners/PlayerChatCommandListener.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/listeners/PlayerDeathListener.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/listeners/PlayerMiscListener.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/listeners/PlayerSessionListener.class new file: paper-plugin/target/classes/de/simolzimol/mclogger/paper/listeners/WorldListener.class new file: paper-plugin/target/classes/plugin.yml new file: paper-plugin/target/maven-archiver/pom.properties new file: paper-plugin/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file: paper-plugin/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file: paper-plugin/target/mclogger-paper-1.0.0.jar new file: paper-plugin/target/original-mclogger-paper-1.0.0.jar new file: velocity-plugin/pom.xml new file: velocity-plugin/src/main/java/de/simolzimol/mclogger/velocity/VelocityLoggerPlugin.java new file: velocity-plugin/src/main/java/de/simolzimol/mclogger/velocity/database/VelocityDatabaseManager.java new file: velocity-plugin/src/main/java/de/simolzimol/mclogger/velocity/listeners/VelocityEventListener.java new file: velocity-plugin/src/main/resources/velocity-config.yml new file: velocity-plugin/target/classes/de/simolzimol/mclogger/velocity/VelocityLoggerPlugin.class new file: velocity-plugin/target/classes/de/simolzimol/mclogger/velocity/database/VelocityDatabaseManager$ThrowingRunnable.class new file: velocity-plugin/target/classes/de/simolzimol/mclogger/velocity/database/VelocityDatabaseManager.class new file: velocity-plugin/target/classes/de/simolzimol/mclogger/velocity/listeners/VelocityEventListener.class new file: velocity-plugin/target/classes/velocity-config.yml new file: velocity-plugin/target/classes/velocity-plugin.json new file: velocity-plugin/target/maven-archiver/pom.properties new file: velocity-plugin/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file: velocity-plugin/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file: velocity-plugin/target/mclogger-velocity-1.0.0.jar new file: velocity-plugin/target/original-mclogger-velocity-1.0.0.jar new file: web/Dockerfile new file: web/app.py new file: web/blueprints/__init__.py new file: web/blueprints/auth.py new file: web/blueprints/group_admin.py new file: web/blueprints/panel.py new file: web/blueprints/site_admin.py new file: web/config.py new file: web/crypto.py new file: web/docker-compose.yml new file: web/panel_db.py new file: web/requirements.txt new file: web/static/css/style.css new file: web/static/js/main.js new file: web/templates/_pagination.html new file: web/templates/admin/base.html new file: web/templates/admin/dashboard.html new file: web/templates/admin/group_edit.html new file: web/templates/admin/group_members.html new file: web/templates/admin/groups.html new file: web/templates/admin/user_edit.html new file: web/templates/admin/users.html new file: web/templates/auth/admin_login.html new file: web/templates/auth/login.html new file: web/templates/base.html new file: web/templates/blocks.html new file: web/templates/chat.html new file: web/templates/commands.html new file: web/templates/dashboard.html new file: web/templates/deaths.html new file: web/templates/group_admin/base.html new file: web/templates/group_admin/dashboard.html new file: web/templates/group_admin/database.html new file: web/templates/group_admin/member_edit.html new file: web/templates/group_admin/members.html new file: web/templates/login.html new file: web/templates/panel/blocks.html new file: web/templates/panel/chat.html new file: web/templates/panel/commands.html new file: web/templates/panel/dashboard.html new file: web/templates/panel/deaths.html new file: web/templates/panel/no_db.html new file: web/templates/panel/perms.html new file: web/templates/panel/player_detail.html new file: web/templates/panel/players.html new file: web/templates/panel/proxy.html new file: web/templates/panel/server_events.html new file: web/templates/panel/sessions.html new file: web/templates/perms.html new file: web/templates/player_detail.html new file: web/templates/players.html new file: web/templates/proxy.html new file: web/templates/server_events.html new file: web/templates/sessions.html
This commit is contained in:
46
web/templates/group_admin/base.html
Normal file
46
web/templates/group_admin/base.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" data-bs-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Gruppen Admin{% endblock %} — {{ session.get('group_name','') }}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-dark bg-warning bg-opacity-75">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand fw-bold text-dark" href="{{ url_for('group_admin.dashboard') }}">
|
||||
<i class="bi bi-gear-fill me-2"></i>{{ session.get('group_name', 'Gruppe') }} — Admin
|
||||
</a>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<a href="{{ url_for('group_admin.dashboard') }}" class="nav-link text-dark {{ 'fw-bold' if request.endpoint == 'group_admin.dashboard' }}">Dashboard</a>
|
||||
<a href="{{ url_for('group_admin.members') }}" class="nav-link text-dark {{ 'fw-bold' if request.endpoint == 'group_admin.members' }}">Mitglieder</a>
|
||||
<a href="{{ url_for('group_admin.database') }}" class="nav-link text-dark {{ 'fw-bold' if request.endpoint == 'group_admin.database' }}">Datenbank</a>
|
||||
<a href="{{ url_for('panel.dashboard') }}" class="btn btn-outline-dark btn-sm">
|
||||
<i class="bi bi-grid me-1"></i>Panel
|
||||
</a>
|
||||
<a href="{{ url_for('auth.logout') }}" class="btn btn-dark btn-sm">
|
||||
<i class="bi bi-box-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid py-4">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for cat, msg in messages %}
|
||||
<div class="alert alert-{{ cat }} alert-dismissible fade show" role="alert">
|
||||
{{ msg }}<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
77
web/templates/group_admin/dashboard.html
Normal file
77
web/templates/group_admin/dashboard.html
Normal file
@@ -0,0 +1,77 @@
|
||||
{% extends "group_admin/base.html" %}
|
||||
{% block title %}Dashboard{% endblock %}
|
||||
{% block content %}
|
||||
<h2 class="mb-4"><i class="bi bi-gear-fill text-warning me-2"></i>Gruppenadmin: {{ session.get('group_name') }}</h2>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card border-0 bg-secondary bg-opacity-25">
|
||||
<div class="card-body text-center">
|
||||
<div class="fs-2 fw-bold text-warning">{{ stats.member_count }}</div>
|
||||
<div class="text-muted">Mitglieder</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card border-0 bg-secondary bg-opacity-25">
|
||||
<div class="card-body text-center">
|
||||
<div class="fs-2 fw-bold {{ 'text-success' if stats.db_configured else 'text-danger' }}">
|
||||
{{ 'Ja' if stats.db_configured else 'Nein' }}
|
||||
</div>
|
||||
<div class="text-muted">DB konfiguriert</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card border-0 bg-secondary bg-opacity-25">
|
||||
<div class="card-body text-center">
|
||||
<div class="fs-2 fw-bold text-info">{{ stats.admin_count }}</div>
|
||||
<div class="text-muted">Admins</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="card border-secondary h-100">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-people-fill me-2"></i>Schnellzugriff
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column gap-2">
|
||||
<a href="{{ url_for('group_admin.members') }}" class="btn btn-outline-warning">
|
||||
<i class="bi bi-people-fill me-2"></i>Mitglieder verwalten
|
||||
</a>
|
||||
<a href="{{ url_for('group_admin.database') }}" class="btn btn-outline-info">
|
||||
<i class="bi bi-database-fill-gear me-2"></i>Datenbank konfigurieren
|
||||
</a>
|
||||
<a href="{{ url_for('panel.dashboard') }}" class="btn btn-outline-success">
|
||||
<i class="bi bi-speedometer2 me-2"></i>Panel öffnen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card border-secondary h-100">
|
||||
<div class="card-header"><i class="bi bi-info-circle me-2"></i>Gruppeninfo</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-5">Name</dt>
|
||||
<dd class="col-sm-7">{{ session.get('group_name') }}</dd>
|
||||
<dt class="col-sm-5">Deine Rolle</dt>
|
||||
<dd class="col-sm-7"><span class="badge bg-warning text-dark">Admin</span></dd>
|
||||
<dt class="col-sm-5">Datenbank</dt>
|
||||
<dd class="col-sm-7">
|
||||
{% if stats.db_configured %}
|
||||
<span class="text-success"><i class="bi bi-check-circle-fill me-1"></i>Verbunden</span>
|
||||
{% else %}
|
||||
<span class="text-danger"><i class="bi bi-x-circle-fill me-1"></i>Nicht konfiguriert</span>
|
||||
{% endif %}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
98
web/templates/group_admin/database.html
Normal file
98
web/templates/group_admin/database.html
Normal file
@@ -0,0 +1,98 @@
|
||||
{% extends "group_admin/base.html" %}
|
||||
{% block title %}Datenbank{% endblock %}
|
||||
{% block content %}
|
||||
<h2 class="mb-4"><i class="bi bi-database-fill-gear me-2"></i>MC Datenbank konfigurieren</h2>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-7">
|
||||
<div class="card border-secondary">
|
||||
<div class="card-header">Verbindungsdaten</div>
|
||||
<div class="card-body">
|
||||
{% if test_result is defined %}
|
||||
<div class="alert {{ 'alert-success' if test_result else 'alert-danger' }}">
|
||||
{% if test_result %}
|
||||
<i class="bi bi-check-circle-fill me-2"></i>Verbindung erfolgreich! Daten wurden gespeichert.
|
||||
{% else %}
|
||||
<i class="bi bi-x-circle-fill me-2"></i>Verbindung fehlgeschlagen: {{ test_error }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-8">
|
||||
<label class="form-label">Host *</label>
|
||||
<input type="text" name="host" class="form-control" required
|
||||
placeholder="localhost"
|
||||
value="{{ creds.host if creds else request.form.get('host', '') }}">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Port *</label>
|
||||
<input type="number" name="port" class="form-control" required
|
||||
value="{{ creds.port if creds else request.form.get('port', '3306') }}">
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Datenbank *</label>
|
||||
<input type="text" name="database" class="form-control" required
|
||||
placeholder="mclogger"
|
||||
value="{{ creds.database if creds else request.form.get('database', '') }}">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Benutzer *</label>
|
||||
<input type="text" name="user" class="form-control" required
|
||||
value="{{ creds.user if creds else request.form.get('user', '') }}">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Passwort</label>
|
||||
<input type="password" name="password" class="form-control"
|
||||
placeholder="{{ '(unverändert)' if creds else '' }}">
|
||||
{% if creds %}
|
||||
<div class="form-text">Leer lassen um das bestehende Passwort beizubehalten.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-4">
|
||||
<button type="submit" name="action" value="test_save" class="btn btn-success">
|
||||
<i class="bi bi-plug-fill me-1"></i>Testen & Speichern
|
||||
</button>
|
||||
{% if creds %}
|
||||
<button type="submit" name="action" value="delete" class="btn btn-outline-danger"
|
||||
onclick="return confirm('DB-Konfiguration löschen?')">
|
||||
<i class="bi bi-trash3 me-1"></i>Entfernen
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-5">
|
||||
<div class="card border-secondary">
|
||||
<div class="card-header"><i class="bi bi-info-circle me-2"></i>Info</div>
|
||||
<div class="card-body">
|
||||
<p class="small text-muted">
|
||||
Gib hier die Verbindungsdaten zu deiner <strong>MCLogger MySQL-Datenbank</strong> ein.
|
||||
Das Panel liest nur Daten (SELECT) — schreibender Zugriff ist nicht nötig.
|
||||
</p>
|
||||
<p class="small text-muted">
|
||||
Die Zugangsdaten werden <strong>verschlüsselt</strong> gespeichert und sind nur für deine Gruppe sichtbar.
|
||||
</p>
|
||||
<hr>
|
||||
<p class="small text-muted mb-1"><strong>Benötigte Tabellen:</strong></p>
|
||||
<ul class="small text-muted">
|
||||
<li>player_sessions</li>
|
||||
<li>chat_messages</li>
|
||||
<li>player_commands</li>
|
||||
<li>block_events</li>
|
||||
<li>player_deaths</li>
|
||||
<li>proxy_events</li>
|
||||
<li>server_events</li>
|
||||
<li>permission_changes</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
54
web/templates/group_admin/member_edit.html
Normal file
54
web/templates/group_admin/member_edit.html
Normal file
@@ -0,0 +1,54 @@
|
||||
{% extends "group_admin/base.html" %}
|
||||
{% block title %}Berechtigungen – {{ member.username }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="d-flex align-items-center gap-2 mb-4">
|
||||
<a href="{{ url_for('group_admin.members') }}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left"></i>
|
||||
</a>
|
||||
<h2 class="mb-0">Berechtigungen: <span class="text-warning">{{ member.username }}</span></h2>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<div class="card border-secondary">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-shield-lock-fill me-2"></i>Panel-Berechtigungen
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Rolle</label>
|
||||
<select name="role" class="form-select">
|
||||
<option value="member" {{ 'selected' if member.role == 'member' }}>Member</option>
|
||||
<option value="admin" {{ 'selected' if member.role == 'admin' }}>Admin</option>
|
||||
</select>
|
||||
<div class="form-text">Admins können Mitglieder und die DB-Verbindung verwalten.</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<p class="form-label mb-2">Panel-Zugriff</p>
|
||||
<div class="row g-2">
|
||||
{% for key, label in all_permissions %}
|
||||
<div class="col-md-6">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch"
|
||||
name="perm_{{ key }}" id="perm_{{ key }}"
|
||||
{{ 'checked' if perms.get(key, True) }}>
|
||||
<label class="form-check-label" for="perm_{{ key }}">{{ label }}</label>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-4">
|
||||
<button type="submit" class="btn btn-warning">
|
||||
<i class="bi bi-check-lg me-1"></i>Speichern
|
||||
</button>
|
||||
<a href="{{ url_for('group_admin.members') }}" class="btn btn-outline-secondary">Abbrechen</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
65
web/templates/group_admin/members.html
Normal file
65
web/templates/group_admin/members.html
Normal file
@@ -0,0 +1,65 @@
|
||||
{% extends "group_admin/base.html" %}
|
||||
{% block title %}Mitglieder{% endblock %}
|
||||
{% block content %}
|
||||
<h2 class="mb-4"><i class="bi bi-people-fill me-2"></i>Mitglieder</h2>
|
||||
|
||||
<div class="row g-3">
|
||||
<!-- Mitgliederliste -->
|
||||
<div class="col-md-8">
|
||||
<div class="card border-secondary">
|
||||
<div class="card-header">Aktuelle Mitglieder ({{ members|length }})</div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead><tr><th>Benutzer</th><th>Rolle</th><th class="text-end">Aktionen</th></tr></thead>
|
||||
<tbody>
|
||||
{% for m in members %}
|
||||
<tr>
|
||||
<td>{{ m.username }}</td>
|
||||
<td>
|
||||
{% if m.role == 'admin' %}
|
||||
<span class="badge bg-warning text-dark"><i class="bi bi-star-fill me-1"></i>Admin</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Member</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
{% if m.id != session.get('user_id') %}
|
||||
<a href="{{ url_for('group_admin.member_edit', user_id=m.id) }}" class="btn btn-sm btn-outline-warning" title="Berechtigungen">
|
||||
<i class="bi bi-shield-lock"></i>
|
||||
</a>
|
||||
<form method="post" action="{{ url_for('group_admin.member_remove', user_id=m.id) }}" class="d-inline"
|
||||
onsubmit="return confirm('{{ m.username }} entfernen?')">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger" title="Entfernen">
|
||||
<i class="bi bi-person-dash"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<span class="text-muted small">Du</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="3" class="text-muted text-center py-3">Keine Mitglieder</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Benutzer einladen (nur via Benutzername - Site Admin fügt Benutzer hinzu, Gruppen admin kann nur bestehende Mitglieder verwalten) -->
|
||||
<div class="col-md-4">
|
||||
<div class="card border-secondary">
|
||||
<div class="card-header"><i class="bi bi-info-circle me-2"></i>Hinweis</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small">
|
||||
Neue Mitglieder müssen vom <strong>Site Admin</strong> zur Gruppe hinzugefügt werden.
|
||||
</p>
|
||||
<p class="text-muted small">
|
||||
Als Gruppenadmin kannst du Berechtigungen bestehender Mitglieder verwalten und Mitglieder entfernen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user