Initial users implementation
This commit is contained in:
parent
62cd31d0f5
commit
277352fe9b
26
__init__.py
Normal file
26
__init__.py
Normal file
@ -0,0 +1,26 @@
|
||||
from django.urls.base import reverse
|
||||
|
||||
|
||||
def url_login(request):
|
||||
nxt = request.GET.get('next', request.get_full_path())
|
||||
return reverse('users-login') + '?next=%s' % nxt
|
||||
|
||||
|
||||
def url_logout(request):
|
||||
nxt = request.GET.get('next', request.get_full_path())
|
||||
return reverse('users-logout') + '?next=%s' % nxt
|
||||
|
||||
|
||||
def url_profile(request):
|
||||
nxt = request.GET.get('next', request.get_full_path())
|
||||
return reverse('users-profile') + '?next=%s' % nxt
|
||||
|
||||
|
||||
def url_register(request):
|
||||
nxt = request.GET.get('next', request.get_full_path())
|
||||
return reverse('users-register') + '?next=%s' % nxt
|
||||
|
||||
|
||||
def url_password_recovery(request):
|
||||
nxt = request.GET.get('next', request.get_full_path())
|
||||
return reverse('users-password-recovery') + '?next=%s' % nxt
|
13
admin.py
Normal file
13
admin.py
Normal file
@ -0,0 +1,13 @@
|
||||
from django.contrib import admin
|
||||
from .models import UserProfile
|
||||
|
||||
|
||||
class UserProfileAdmin(admin.ModelAdmin):
|
||||
list_display = ('user', )
|
||||
search_fields = ('user', 'language_code', 'timezone', 'id', )
|
||||
list_filter = (
|
||||
('language_code', admin.ChoicesFieldListFilter),
|
||||
)
|
||||
|
||||
|
||||
admin.site.register(UserProfile, UserProfileAdmin)
|
8
apps.py
Normal file
8
apps.py
Normal file
@ -0,0 +1,8 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class UsersConfig(AppConfig):
|
||||
name = 'users'
|
||||
|
||||
def ready(self):
|
||||
import users.signals
|
87
context.py
Normal file
87
context.py
Normal file
@ -0,0 +1,87 @@
|
||||
from django.urls.base import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
from themes import empty_entry_parameters, color_icon_url
|
||||
from . import url_login, url_logout, url_register, url_profile
|
||||
|
||||
ADMIN_ENTRY_UID = 'admin-main'
|
||||
LOGIN_ENTRY_UID = 'login-main'
|
||||
LOGOUT_ENTRY_UID = 'logout-main'
|
||||
PROFILE_ENTRY_UID = 'profile-main'
|
||||
REGISTER_ENTRY_UID = 'register-main'
|
||||
|
||||
|
||||
def context_adaption(context, request, title):
|
||||
context.set_additional_title(title)
|
||||
menubar(context[context.MENUBAR], request)
|
||||
context[context.NAVIGATIONBAR].append_entry(*empty_entry_parameters(request))
|
||||
actionbar(context[context.ACTIONBAR], request)
|
||||
|
||||
|
||||
def menubar(bar, request):
|
||||
if not request.user.is_authenticated:
|
||||
bar.append_entry(*login_entry_parameters(request))
|
||||
else:
|
||||
bar.append_entry(*logout_entry_parameters(request))
|
||||
if request.user.is_staff or request.user.is_superuser:
|
||||
bar.append_entry(*admin_entry_parameters(request))
|
||||
bar.append_entry(*profile_entry_parameters(request))
|
||||
|
||||
|
||||
def actionbar(bar, request):
|
||||
bar.append_entry(*login_entry_parameters(request, left=True))
|
||||
bar.append_entry(*register_entry_parameters(request, left=True))
|
||||
|
||||
|
||||
def login_entry_parameters(request, left=False):
|
||||
return (
|
||||
LOGIN_ENTRY_UID, # uid
|
||||
_('Login'), # name
|
||||
color_icon_url(request, 'login.png'), # icon
|
||||
url_login(request), # url
|
||||
left, # left
|
||||
False # active
|
||||
)
|
||||
|
||||
|
||||
def register_entry_parameters(request, left=False):
|
||||
return (
|
||||
REGISTER_ENTRY_UID, # uid
|
||||
_('Register'), # name
|
||||
color_icon_url(request, 'register.png'), # icon
|
||||
url_register(request), # url
|
||||
left, # left
|
||||
False # active
|
||||
)
|
||||
|
||||
|
||||
def logout_entry_parameters(request):
|
||||
return (
|
||||
LOGOUT_ENTRY_UID, # uid
|
||||
_('Logout'), # name
|
||||
color_icon_url(request, 'logout.png'), # icon
|
||||
url_logout(request), # url
|
||||
False, # left
|
||||
False, # active
|
||||
)
|
||||
|
||||
|
||||
def admin_entry_parameters(request):
|
||||
return (
|
||||
ADMIN_ENTRY_UID, # uid
|
||||
_('Administration'), # name
|
||||
color_icon_url(request, 'admin.png'), # icon
|
||||
reverse('admin:index'), # url
|
||||
False, # left
|
||||
False # active
|
||||
)
|
||||
|
||||
|
||||
def profile_entry_parameters(request):
|
||||
return (
|
||||
PROFILE_ENTRY_UID, # uid
|
||||
request.user.username, # name
|
||||
color_icon_url(request, 'user.png'), # icon
|
||||
url_profile(request), # url
|
||||
False, # left
|
||||
False # active
|
||||
)
|
24
forms.py
Normal file
24
forms.py
Normal file
@ -0,0 +1,24 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from .models import UserProfile
|
||||
|
||||
|
||||
class UserRegistrationForm(UserCreationForm):
|
||||
email = forms.EmailField()
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['username', 'email', 'password1', 'password2']
|
||||
|
||||
|
||||
class UserProfileForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
fields = ['timezone', 'language_code']
|
||||
|
||||
|
||||
class UserProfileFormLanguageOnly(forms.ModelForm):
|
||||
class Meta:
|
||||
model = UserProfile
|
||||
fields = ['language_code']
|
BIN
locale/de/LC_MESSAGES/django.mo
Normal file
BIN
locale/de/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
96
locale/de/LC_MESSAGES/django.po
Normal file
96
locale/de/LC_MESSAGES/django.po
Normal file
@ -0,0 +1,96 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-09-22 16:13+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: context.py:38 templates/users/login.html:9 views.py:62
|
||||
msgid "Login"
|
||||
msgstr "Anmelden"
|
||||
|
||||
#: context.py:49 templates/users/register.html:9 views.py:44
|
||||
msgid "Register"
|
||||
msgstr "Registrieren"
|
||||
|
||||
#: context.py:60
|
||||
msgid "Logout"
|
||||
msgstr "Abmelden"
|
||||
|
||||
#: context.py:71
|
||||
msgid "Administration"
|
||||
msgstr "Administration"
|
||||
|
||||
#: templates/users/profile.html:8
|
||||
msgid "Save"
|
||||
msgstr "Speichern"
|
||||
|
||||
#: templates/users/profile_formdata.html:4
|
||||
msgid "Language"
|
||||
msgstr "Sprache"
|
||||
|
||||
#: views.py:27
|
||||
msgid "The language was set to \"%(language)s (%(language_code)s)\"."
|
||||
msgstr "Die Sprache wurde zu \"%(language)s (%(language_code)s)\" gesetzt."
|
||||
|
||||
#: views.py:38
|
||||
#, python-format
|
||||
msgid "Profile for %(username)s"
|
||||
msgstr "Benutzerprofil für %(username)s"
|
||||
|
||||
#: views.py:47
|
||||
#, python-format
|
||||
msgid "If you already have an account, login <a href=\"%(url)s\">here</a>."
|
||||
msgstr ""
|
||||
"Wenn Sie bereits einen Account besitzen, dann können sie sich <a href="
|
||||
"\"%(url)s\">hier</a> anmelden."
|
||||
|
||||
#: views.py:52
|
||||
#, python-format
|
||||
msgid "Your account has been created! You are able to log in as %(username)s."
|
||||
msgstr ""
|
||||
"Ihr Account wurde angelegt! Sie können sich nun als %(username)s anmelden."
|
||||
|
||||
#: views.py:55
|
||||
msgid "Registration failed!"
|
||||
msgstr "Registrierung fehlgeschlagen!"
|
||||
|
||||
#: views.py:65
|
||||
#, python-format
|
||||
msgid "If you don't have an acount, register <a href=\"%(url)s\">here</a>."
|
||||
msgstr ""
|
||||
"Wenn Sie keinen Account haben, können Sie sich <a href=\"%(url)s\">hier</a> "
|
||||
"registrieren."
|
||||
|
||||
#: views.py:72
|
||||
#, python-format
|
||||
msgid "You are now logged in as %(username)s."
|
||||
msgstr "Sie sind nun als %(username)s angemeldet."
|
||||
|
||||
#: views.py:75
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Login failed! You can do a password recorvery <a href=\"%(url_recover)s"
|
||||
"\">here</a> or you can register <a href=\"%(url_register)s\">here</a>."
|
||||
msgstr ""
|
||||
"Anmeldung fehlgeschlagen! Sie können Ihr Passwort hier <a href="
|
||||
"\"%(url_recover)s\">hier</a> wiederherstellen oder Sie können sich <a href="
|
||||
"\"%(url_register)s\">hier</a> registrieren."
|
||||
|
||||
#: views.py:81
|
||||
#, python-format
|
||||
msgid "You are no longer logged in as %(username)s."
|
||||
msgstr "Sie sind nicht mehr als %(username)s angemeldet."
|
42
middleware.py
Normal file
42
middleware.py
Normal file
@ -0,0 +1,42 @@
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils import timezone
|
||||
from django.utils import translation
|
||||
import pytz
|
||||
|
||||
|
||||
class SettingsMiddleware(object):
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
#
|
||||
# Set Timzone
|
||||
#
|
||||
if request.user.is_authenticated:
|
||||
try:
|
||||
timezone.activate(pytz.timezone(request.user.userprofile.timezone))
|
||||
except User.userprofile.RelatedObjectDoesNotExist:
|
||||
pass # No profile
|
||||
except AttributeError:
|
||||
pass # No User logged in
|
||||
else:
|
||||
timezone.deactivate()
|
||||
#
|
||||
# Set Language
|
||||
#
|
||||
try:
|
||||
language = request.user.userprofile.language_code
|
||||
except User.userprofile.RelatedObjectDoesNotExist:
|
||||
pass # No profile
|
||||
except AttributeError:
|
||||
pass # No User logged in
|
||||
else:
|
||||
translation.activate(language)
|
||||
request.LANGUAGE_CODE = translation.get_language()
|
||||
|
||||
response = self.get_response(request)
|
||||
|
||||
# Code to be executed for each request/response after
|
||||
# the view is called.
|
||||
|
||||
return response
|
26
migrations/0001_initial.py
Normal file
26
migrations/0001_initial.py
Normal file
File diff suppressed because one or more lines are too long
0
migrations/__init__.py
Normal file
0
migrations/__init__.py
Normal file
23
models.py
Normal file
23
models.py
Normal file
@ -0,0 +1,23 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
import pytz
|
||||
|
||||
|
||||
# GENERAL Methods and Classes
|
||||
#
|
||||
def get_userprofile(user):
|
||||
try:
|
||||
profile = user.userprofile
|
||||
except UserProfile.DoesNotExist:
|
||||
profile = UserProfile(user=user)
|
||||
profile.save()
|
||||
return profile
|
||||
|
||||
|
||||
# USERPROFILE Model
|
||||
#
|
||||
class UserProfile(models.Model):
|
||||
user = models.OneToOneField(User, unique=True, on_delete=models.CASCADE)
|
||||
timezone = models.CharField(max_length=150, default='UTC', choices=[(t, t) for t in pytz.common_timezones])
|
||||
language_code = models.CharField(max_length=150, default='en', choices=settings.LANGUAGES)
|
37
signals.py
Normal file
37
signals.py
Normal file
@ -0,0 +1,37 @@
|
||||
import logging
|
||||
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
|
||||
from django.dispatch import receiver
|
||||
|
||||
log = logging.getLogger('AUTH')
|
||||
|
||||
|
||||
@receiver(user_logged_in)
|
||||
def user_logged_in_callback(sender, request, user, **kwargs):
|
||||
# to cover more complex cases:
|
||||
# http://stackoverflow.com/questions/4581789/how-do-i-get-user-ip-address-in-django
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
|
||||
log.info('Accepted password for {user} from {ip}'.format(
|
||||
user=user,
|
||||
ip=ip
|
||||
))
|
||||
|
||||
|
||||
@receiver(user_logged_out)
|
||||
def user_logged_out_callback(sender, request, user, **kwargs):
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
|
||||
log.debug('Disconnected from user {user} {ip}'.format(
|
||||
user=user,
|
||||
ip=ip
|
||||
))
|
||||
|
||||
|
||||
@receiver(user_login_failed)
|
||||
def user_login_failed_callback(sender, request, credentials, **kwargs):
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
|
||||
log.warning('Failed password for {user} from {ip}'.format(
|
||||
user=credentials.get('username'),
|
||||
ip=ip
|
||||
))
|
11
templates/users/login.html
Normal file
11
templates/users/login.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends "themes/"|add:settings.page_theme|add:"/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="{% trans "Login" %}" class="button" />
|
||||
</form>
|
||||
{% endblock content %}
|
11
templates/users/profile.html
Normal file
11
templates/users/profile.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends "themes/"|add:settings.page_theme|add:"/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
{% include 'users/profile_formdata.html' %}
|
||||
<input type="submit" value="{% trans "Save" %}" class="button" />
|
||||
</form>
|
||||
|
||||
{% endblock content %}
|
6
templates/users/profile_formdata.html
Normal file
6
templates/users/profile_formdata.html
Normal file
@ -0,0 +1,6 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<h1>{% trans "Language and Timezone" %}</h1>
|
||||
{{ form_userprofile.as_p }}
|
||||
</select>
|
11
templates/users/register.html
Normal file
11
templates/users/register.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends "themes/"|add:settings.page_theme|add:"/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="{% trans "Register" %}" class="button" />
|
||||
</form>
|
||||
{% endblock content %}
|
3
tests.py
Normal file
3
tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
10
urls.py
Normal file
10
urls.py
Normal file
@ -0,0 +1,10 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('login', views.login, name='users-login'),
|
||||
path('logout', views.logout, name='users-logout'),
|
||||
path('password_recovery', views.password_recovery, name='users-password-recovery'),
|
||||
path('profile', views.profile, name='users-profile'),
|
||||
path('register', views.register, name='users-register'),
|
||||
]
|
105
views.py
Normal file
105
views.py
Normal file
@ -0,0 +1,105 @@
|
||||
from .context import context_adaption
|
||||
from django.shortcuts import render, redirect
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import authenticate
|
||||
from django.contrib.auth import login as django_login
|
||||
from django.contrib.auth import logout as django_logout
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.utils.translation import gettext as _
|
||||
from .forms import UserRegistrationForm, UserProfileForm
|
||||
from .models import get_userprofile
|
||||
from themes import Context
|
||||
import users
|
||||
|
||||
|
||||
def password_recovery(request):
|
||||
return redirect(request.GET.get('next') or '/')
|
||||
|
||||
|
||||
def profile_post_actions(request, context):
|
||||
if request.POST:
|
||||
form = context.get('form_userprofile')
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return redirect(request.GET.get('next') or '/')
|
||||
|
||||
|
||||
def profile_pre_actions(request, context, form_to_be_used=UserProfileForm):
|
||||
profile = get_userprofile(request.user)
|
||||
if request.POST:
|
||||
form = form_to_be_used(request.POST, instance=profile)
|
||||
else:
|
||||
form = form_to_be_used(instance=profile)
|
||||
context['form_userprofile'] = form
|
||||
|
||||
|
||||
@login_required
|
||||
def profile(request):
|
||||
context = Context(request) # needs to be executed first because of time mesurement
|
||||
profile_pre_actions(request, context)
|
||||
response = profile_post_actions(request, context)
|
||||
if response is not None:
|
||||
return response
|
||||
else:
|
||||
context_adaption(
|
||||
context,
|
||||
request,
|
||||
_('Profile for %(username)s') % {'username': request.user.username},
|
||||
)
|
||||
return render(request, 'users/profile.html', context=context)
|
||||
|
||||
|
||||
def register(request):
|
||||
context = Context(request) # needs to be executed first because of time mesurement
|
||||
context_adaption(context, request, _('Register'))
|
||||
if not request.POST:
|
||||
form = UserRegistrationForm()
|
||||
messages.info(request, _('If you already have an account, login <a href="%(url)s">here</a>.') % {'url': users.url_login(request)})
|
||||
else:
|
||||
form = UserRegistrationForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, _('Your account has been created! You are able to log in as %(username)s.') % {'username': form.cleaned_data.get('username')})
|
||||
return redirect('users-login')
|
||||
else:
|
||||
messages.error(request, _('Registration failed!'))
|
||||
context['form'] = form
|
||||
return render(request, 'users/register.html', context)
|
||||
|
||||
|
||||
def login(request):
|
||||
context = Context(request) # needs to be executed first because of time mesurement
|
||||
context_adaption(context, request, _('Login'))
|
||||
if not request.POST:
|
||||
form = AuthenticationForm()
|
||||
messages.info(request, _('If you don\'t have an acount, register <a href="%(url)s">here</a>.') % {'url': users.url_register(request)})
|
||||
else:
|
||||
form = AuthenticationForm(request, data=request.POST)
|
||||
if form.is_valid():
|
||||
username = form.cleaned_data.get('username')
|
||||
user = authenticate(username=username, password=form.cleaned_data.get('password'))
|
||||
django_login(request, user)
|
||||
messages.success(request, _('You are now logged in as %(username)s.') % {'username': username})
|
||||
return redirect(request.GET.get('next') or '/')
|
||||
else:
|
||||
messages.error(request, _('Login failed! You can do a password recorvery <a href="%(url_recover)s">here</a> or you can register <a href="%(url_register)s">here</a>.') % {'url_register': users.url_register(request), 'url_recover': users.url_password_recovery(request)})
|
||||
context['form'] = form
|
||||
return render(request, 'users/login.html', context)
|
||||
|
||||
|
||||
def logout(request):
|
||||
messages.success(request, _('You are no longer logged in as %(username)s.') % {'username': request.user.username})
|
||||
session_cache = {}
|
||||
try:
|
||||
for variable in settings.PERSISTENT_SESSION_VARIABLES:
|
||||
value = request.session.get(variable)
|
||||
if value is not None:
|
||||
session_cache[variable] = value
|
||||
except AttributeError:
|
||||
pass # PERSISTENT_SESSION_VARIABLES are possibly not defined in the settings
|
||||
django_logout(request)
|
||||
for variable in session_cache:
|
||||
request.session[variable] = session_cache[variable]
|
||||
return redirect(request.GET.get('next') or '/')
|
Loading…
x
Reference in New Issue
Block a user