usermanagement/translation/calendar
This commit is contained in:
@@ -10,15 +10,15 @@ class CustomUserCreationForm(UserCreationForm):
|
||||
fields = ('username', 'email', 'password1', 'password2')
|
||||
|
||||
class CustomUserChangeForm(UserChangeForm):
|
||||
password = None # Passwort-Änderung über extra Formular
|
||||
password = None # Password change via separate form
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('email',)
|
||||
widgets = {
|
||||
'email': forms.EmailInput(attrs={'class': 'text-input', 'placeholder': 'E-Mail-Adresse'}),
|
||||
'email': forms.EmailInput(attrs={'class': 'text-input', 'placeholder': 'Email address'}),
|
||||
}
|
||||
|
||||
class JellyfinLoginForm(forms.Form):
|
||||
username = forms.CharField(label='Benutzername', widget=forms.TextInput(attrs={'class': 'form-control'}))
|
||||
password = forms.CharField(label='Passwort', widget=forms.PasswordInput(attrs={'class': 'form-control'}))
|
||||
username = forms.CharField(label='Username', widget=forms.TextInput(attrs={'class': 'form-control'}))
|
||||
password = forms.CharField(label='Password', widget=forms.PasswordInput(attrs={'class': 'form-control'}))
|
||||
|
@@ -4,9 +4,9 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
class User(AbstractUser):
|
||||
"""
|
||||
Custom User Model mit zusätzlichen Feldern und Berechtigungen.
|
||||
Normale User können nur ihre eigenen Daten bearbeiten.
|
||||
Admin-User können alles.
|
||||
Custom User Model with additional fields and permissions.
|
||||
Regular users can only edit their own data.
|
||||
Admin users can edit everything.
|
||||
"""
|
||||
email = models.EmailField(_("email address"), unique=True)
|
||||
bio = models.TextField(max_length=500, blank=True)
|
||||
@@ -26,7 +26,7 @@ class User(AbstractUser):
|
||||
client = JellyfinClient()
|
||||
return client.is_admin(self.jellyfin_user_id, self.jellyfin_token)
|
||||
except:
|
||||
# Im Fehlerfall den lokalen Status verwenden
|
||||
# On error, fall back to local status
|
||||
return self.is_admin
|
||||
|
||||
@property
|
||||
|
@@ -2,14 +2,14 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="auth-container">
|
||||
<h2>Anmelden</h2>
|
||||
<h2>Sign in</h2>
|
||||
<form method="post" class="auth-form">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn-primary">Anmelden</button>
|
||||
<button type="submit" class="btn-primary">Sign in</button>
|
||||
</form>
|
||||
<div class="auth-links">
|
||||
<p>Noch kein Konto? <a href="{% url 'accounts:register' %}">Jetzt registrieren</a></p>
|
||||
<p>Don't have an account? <a href="{% url 'accounts:register' %}">Register now</a></p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@@ -2,11 +2,11 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="auth-container">
|
||||
<h2>Passwort ändern</h2>
|
||||
<h2>Change password</h2>
|
||||
<form method="post" class="auth-form">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn-primary">Passwort ändern</button>
|
||||
<button type="submit" class="btn-primary">Change password</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
@@ -2,8 +2,8 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="auth-container">
|
||||
<h2>Passwort geändert</h2>
|
||||
<p>Ihr Passwort wurde erfolgreich geändert.</p>
|
||||
<p><a href="{% url 'accounts:profile' %}">Zurück zum Profil</a></p>
|
||||
<h2>Password changed</h2>
|
||||
<p>Your password has been changed successfully.</p>
|
||||
<p><a href="{% url 'accounts:profile' %}">Back to profile</a></p>
|
||||
</div>
|
||||
{% endblock %}
|
@@ -7,7 +7,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="profile-container">
|
||||
<h2>Hallo, {{ user.username }}</h2>
|
||||
<h2>Hello, {{ user.username }}</h2>
|
||||
|
||||
{% if messages %}
|
||||
<div class="messages">
|
||||
@@ -18,24 +18,24 @@
|
||||
{% endif %}
|
||||
|
||||
<div class="profile-section">
|
||||
<h3>E-Mail-Adresse</h3>
|
||||
<h3>Email address</h3>
|
||||
<form method="post" class="profile-form compact-form">
|
||||
{% csrf_token %}
|
||||
<div class="form-row">
|
||||
<label for="id_email">E-Mail</label>
|
||||
<label for="id_email">Email</label>
|
||||
{{ form.email }}
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">Speichern</button>
|
||||
<button type="submit" class="btn-primary">Save</button>
|
||||
</form>
|
||||
|
||||
{% if user.jellyfin_server %}
|
||||
<div class="jellyfin-info">
|
||||
<h4>Jellyfin-Verbindung</h4>
|
||||
<h4>Jellyfin connection</h4>
|
||||
<p>
|
||||
Server: {{ user.jellyfin_server }}<br>
|
||||
Status: {% if user.jellyfin_token %}Verbunden{% else %}Nicht verbunden{% endif %}<br>
|
||||
Status: {% if user.jellyfin_token %}Connected{% else %}Not connected{% endif %}<br>
|
||||
{% if user.is_jellyfin_admin %}
|
||||
<span class="badge badge-admin">Jellyfin Administrator</span>
|
||||
<span class="badge badge-admin">Jellyfin administrator</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
@@ -43,9 +43,9 @@
|
||||
</div>
|
||||
|
||||
<div class="profile-section">
|
||||
<h3>Meine Abonnements</h3>
|
||||
<h3>My subscriptions</h3>
|
||||
|
||||
<h4>Serien</h4>
|
||||
<h4>Series</h4>
|
||||
{% if series_subs %}
|
||||
<div class="subscription-list">
|
||||
{% for sub in series_subs %}
|
||||
@@ -53,11 +53,11 @@
|
||||
{% if sub.series_poster %}
|
||||
<img src="{{ sub.series_poster }}" alt="{{ sub.series_title }}" class="subscription-poster">
|
||||
{% else %}
|
||||
<img src="https://via.placeholder.com/80x120?text=Kein+Poster" alt="" class="subscription-poster">
|
||||
<img src="https://via.placeholder.com/80x120?text=No+Poster" alt="" class="subscription-poster">
|
||||
{% endif %}
|
||||
<div class="subscription-info">
|
||||
<div class="subscription-title">{{ sub.series_title }}</div>
|
||||
<div class="subscription-date">Abonniert am {{ sub.created_at|date:"d.m.Y" }}</div>
|
||||
<div class="subscription-date">Subscribed on {{ sub.created_at|date:"d.m.Y" }}</div>
|
||||
{% if sub.series_overview %}
|
||||
<div class="subscription-overview">{{ sub.series_overview|truncatechars:100 }}</div>
|
||||
{% endif %}
|
||||
@@ -66,10 +66,10 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="muted">Keine Serien abonniert.</p>
|
||||
<p class="muted">No series subscribed.</p>
|
||||
{% endif %}
|
||||
|
||||
<h4>Filme</h4>
|
||||
<h4>Movies</h4>
|
||||
{% if movie_subs %}
|
||||
<div class="subscription-list">
|
||||
{% for sub in movie_subs %}
|
||||
@@ -77,11 +77,11 @@
|
||||
{% if sub.poster %}
|
||||
<img src="{{ sub.poster }}" alt="{{ sub.title }}" class="subscription-poster">
|
||||
{% else %}
|
||||
<img src="https://via.placeholder.com/80x120?text=Kein+Poster" alt="" class="subscription-poster">
|
||||
<img src="https://via.placeholder.com/80x120?text=No+Poster" alt="" class="subscription-poster">
|
||||
{% endif %}
|
||||
<div class="subscription-info">
|
||||
<div class="subscription-title">{{ sub.title }}</div>
|
||||
<div class="subscription-date">Abonniert am {{ sub.created_at|date:"d.m.Y" }}</div>
|
||||
<div class="subscription-date">Subscribed on {{ sub.created_at|date:"d.m.Y" }}</div>
|
||||
{% if sub.overview %}
|
||||
<div class="subscription-overview">{{ sub.overview|truncatechars:100 }}</div>
|
||||
{% endif %}
|
||||
@@ -90,7 +90,7 @@
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="muted">Keine Filme abonniert.</p>
|
||||
<p class="muted">No movies subscribed.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2,14 +2,14 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="auth-container">
|
||||
<h2>Registrieren</h2>
|
||||
<h2>Register</h2>
|
||||
<form method="post" class="auth-form">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="btn-primary">Registrieren</button>
|
||||
<button type="submit" class="btn-primary">Register</button>
|
||||
</form>
|
||||
<div class="auth-links">
|
||||
<p>Bereits ein Konto? <a href="{% url 'accounts:login' %}">Jetzt anmelden</a></p>
|
||||
<p>Already have an account? <a href="{% url 'accounts:login' %}">Sign in</a></p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@@ -7,7 +7,7 @@ from django.contrib import messages
|
||||
|
||||
class JellyfinClient:
|
||||
def __init__(self):
|
||||
# Basis-Einstellungen aus den Django-Settings
|
||||
# Base settings from Django settings
|
||||
self.client = settings.JELLYFIN_CLIENT
|
||||
self.version = settings.JELLYFIN_VERSION
|
||||
self.device = settings.JELLYFIN_DEVICE
|
||||
@@ -18,13 +18,13 @@ class JellyfinClient:
|
||||
def authenticate(self, username, password):
|
||||
"""Authenticate with Jellyfin and return user info if successful"""
|
||||
if not self.server_url:
|
||||
raise ValueError("Keine Server-URL angegeben")
|
||||
raise ValueError("No server URL provided")
|
||||
|
||||
# Stelle sicher, dass die URL ein Protokoll hat
|
||||
# Ensure the URL has a protocol
|
||||
if not self.server_url.startswith(('http://', 'https://')):
|
||||
self.server_url = f'http://{self.server_url}'
|
||||
|
||||
# Entferne trailing slashes
|
||||
# Remove trailing slashes
|
||||
self.server_url = self.server_url.rstrip('/')
|
||||
|
||||
headers = {
|
||||
@@ -57,13 +57,13 @@ class JellyfinClient:
|
||||
'is_admin': data['User'].get('Policy', {}).get('IsAdministrator', False)
|
||||
}
|
||||
except requests.exceptions.ConnectionError:
|
||||
raise ValueError("Verbindung zum Server nicht möglich. Bitte überprüfen Sie die Server-URL.")
|
||||
raise ValueError("Unable to connect to the server. Please check the server URL.")
|
||||
except requests.exceptions.Timeout:
|
||||
raise ValueError("Zeitüberschreitung bei der Verbindung zum Server.")
|
||||
raise ValueError("Connection to the server timed out.")
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response.status_code == 401:
|
||||
return None # Authentifizierung fehlgeschlagen
|
||||
raise ValueError(f"HTTP-Fehler: {e.response.status_code}")
|
||||
raise ValueError(f"HTTP error: {e.response.status_code}")
|
||||
except Exception as e:
|
||||
return None
|
||||
|
||||
@@ -71,7 +71,7 @@ class JellyfinClient:
|
||||
"""Check if user is admin in Jellyfin"""
|
||||
cache_key = f'jellyfin_admin_{user_id}'
|
||||
|
||||
# Check cache first
|
||||
# Check cache first
|
||||
cached = cache.get(cache_key)
|
||||
if cached is not None:
|
||||
return cached
|
||||
@@ -106,11 +106,11 @@ def jellyfin_admin_required(view_func):
|
||||
@wraps(view_func)
|
||||
def _wrapped_view(request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
messages.error(request, 'Sie müssen angemeldet sein, um diese Seite zu sehen.')
|
||||
messages.error(request, 'You must be logged in to view this page.')
|
||||
return redirect('accounts:login')
|
||||
|
||||
if not request.user.is_jellyfin_admin:
|
||||
messages.error(request, 'Sie benötigen Admin-Rechte, um diese Seite zu sehen.')
|
||||
messages.error(request, 'You need admin rights to view this page.')
|
||||
return redirect('index')
|
||||
|
||||
return view_func(request, *args, **kwargs)
|
||||
|
@@ -17,7 +17,7 @@ class RegisterView(CreateView):
|
||||
|
||||
def form_valid(self, form):
|
||||
response = super().form_valid(form)
|
||||
messages.success(self.request, 'Registrierung erfolgreich! Sie können sich jetzt anmelden.')
|
||||
messages.success(self.request, 'Registration successful! You can now sign in.')
|
||||
return response
|
||||
|
||||
@login_required
|
||||
@@ -26,12 +26,12 @@ def profile(request):
|
||||
form = CustomUserChangeForm(request.POST, instance=request.user)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, 'E-Mail gespeichert.')
|
||||
messages.success(request, 'Email saved.')
|
||||
return redirect('accounts:profile')
|
||||
else:
|
||||
form = CustomUserChangeForm(instance=request.user)
|
||||
|
||||
# Lade Abonnements
|
||||
# Load subscriptions
|
||||
series_subs = request.user.series_subscriptions.all()
|
||||
movie_subs = request.user.movie_subscriptions.all()
|
||||
|
||||
@@ -40,7 +40,7 @@ def profile(request):
|
||||
from settingspanel.models import AppSettings
|
||||
from arr_api.services import sonarr_get_series, radarr_lookup_movie_by_title
|
||||
cfg = AppSettings.current()
|
||||
# Serien
|
||||
# Series
|
||||
for sub in series_subs:
|
||||
if not sub.series_poster and sub.series_id:
|
||||
details = sonarr_get_series(sub.series_id, base_url=cfg.sonarr_url, api_key=cfg.sonarr_api_key)
|
||||
@@ -51,7 +51,7 @@ def profile(request):
|
||||
if not sub.series_genres:
|
||||
sub.series_genres = details.get('series_genres') or []
|
||||
sub.save(update_fields=['series_poster', 'series_overview', 'series_genres'])
|
||||
# Filme
|
||||
# Movies
|
||||
for sub in movie_subs:
|
||||
if not sub.poster:
|
||||
details = radarr_lookup_movie_by_title(sub.title, base_url=cfg.radarr_url, api_key=cfg.radarr_api_key)
|
||||
@@ -84,7 +84,7 @@ def jellyfin_login(request):
|
||||
app_settings = AppSettings.current()
|
||||
server_url = app_settings.get_jellyfin_url()
|
||||
if not server_url:
|
||||
messages.error(request, 'Jellyfin Server ist nicht konfiguriert. Bitte Setup abschließen.')
|
||||
messages.error(request, 'Jellyfin server is not configured. Please complete setup.')
|
||||
return render(request, 'accounts/login.html', {'form': form})
|
||||
|
||||
try:
|
||||
@@ -93,7 +93,7 @@ def jellyfin_login(request):
|
||||
auth_result = client.authenticate(username, password)
|
||||
|
||||
if not auth_result:
|
||||
messages.error(request, 'Anmeldung fehlgeschlagen. Bitte überprüfen Sie Ihre Anmeldedaten.')
|
||||
messages.error(request, 'Sign in failed. Please check your credentials.')
|
||||
return render(request, 'accounts/login.html', {'form': form})
|
||||
|
||||
# Existierenden User finden oder neu erstellen
|
||||
@@ -116,13 +116,13 @@ def jellyfin_login(request):
|
||||
user.save()
|
||||
|
||||
login(request, user)
|
||||
messages.success(request, f'Willkommen, {username}!')
|
||||
messages.success(request, f'Welcome, {username}!')
|
||||
return redirect('arr_api:index')
|
||||
|
||||
except ValueError as e:
|
||||
messages.error(request, str(e))
|
||||
except Exception as e:
|
||||
messages.error(request, f'Verbindungsfehler: {str(e)}')
|
||||
messages.error(request, f'Connection error: {str(e)}')
|
||||
# invalid form or error path
|
||||
return render(request, 'accounts/login.html', {'form': form})
|
||||
|
||||
|
Reference in New Issue
Block a user