"""ORM Models for Users that could be members and user activation requests.""" import hashlib import random from datetime import timedelta from os import path from django.conf import settings from django.contrib.auth.models import AbstractUser from django.db import models from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext as _ from easy_thumbnails.fields import ThumbnailerImageField from utils import OverwriteStorage from . import PAID_MEMBERSHIP_GROUP, GENDER_CHOICES def get_upload_path(instance, filename): """Erstellt den Pfad und Dateinamen für den Upload dynmisch. @param instance: The Membership Object for the uploaded image @param filename: the filename of the uploaded image """ extension = path.splitext(filename)[1] return 'membership/%s%s' % (instance.username, extension) class ActivationManager(models.Manager): """Manages pending user registrations.""" def activate(self, activation_key): """ searches the pending registrations for the given activation key. Set the corresponding user to active, if the key was found and the key has not expired yet.s @param activation_key: the key found in the activation email """ try: activation_request = self.get(activation_key=activation_key) if activation_request.expired(): activation_request.user.delete() activation_request.delete() return False elif not activation_request.user.is_active: activation_request.user.is_active = True activation_request.user.save() activation_request.delete() return activation_request.user except self.model.DoesNotExist: return False def create_pending_registration(self, user): """Creates a PendingActivation instance with an random activation key. @param user: the user that requests activation. """ salt = str(user.pk * random.random()) salt += user.registration_date.isoformat() activation_key = hashlib.sha1(salt.encode()).hexdigest() return self.create(user=user, activation_key=activation_key) def expired(self): """Return all ActivationRequest that haven't been activated and whose valid activation time has been expired""" timespan = timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS) expiration_date = timezone.now() - timespan return self.filter( user__is_active=False, user__date_joined__lt=expiration_date ) class ActivationRequest(models.Model): """ Each ActivationRequest contains an activation key and an user. The key will be send by email to the user if the user clicks on the link he can activate his in_active account. """ user = models.OneToOneField( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name=_('user') ) activation_key = models.CharField(_('activation key'), max_length=40) objects = ActivationManager() class Meta: verbose_name = _('pending activation') verbose_name_plural = _('pending activations') def __str__(self): return _("user registration for %s") % self.user def activate(self): """Activate the user account and delete this ActivationRequest""" self.user.is_active = True self.user.save() self.delete() @property def expiration_date(self): """The datetime until this user activation request is valid.""" timespan = timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS) return self.user.date_joined + timespan @property def email(self): """The email adress of the user that should be activated.""" return self.user.email def expired(self): """True if user is inactive and expiration_date is in the past.""" if self.user.is_active: return False elif timezone.now() >= self.expiration_date: return True else: return False expired.boolean = True @property def first_name(self): """First name of the user that should be activated.""" return self.user.first_name @property def last_name(self): """Last name of the user that should be activated.""" return self.user.last_name @property def registration_date(self): """Date when the user joined/registered his account.""" return self.user.date_joined @property def username(self): """Loing name of the user that should be activated.""" return self.user.username class Membership(AbstractUser): """An expanded Django User with additional data to manage club memberships. """ gender = models.CharField( _("Gender"), max_length=1, choices=GENDER_CHOICES, blank=True, null=True, ) website = models.URLField(blank=True) avatar = ThumbnailerImageField( upload_to=get_upload_path, storage=OverwriteStorage(), blank=True, null=True ) membership = models.BooleanField( _('Membership'), default=False, help_text=_('Yes, I confirm that I am in agreement with the statutes \ and would like to become a member.') ) birthday = models.DateField( _("Birthday Date"), blank=True, null=True, ) telephone = models.CharField( _("Telephone"), max_length=30, blank=True, null=True ) street_name = models.CharField( _("Address"), max_length=75, blank=True, null=True ) post_code = models.PositiveSmallIntegerField( _("Postcode"), blank=True, null=True ) city = models.CharField( _("Town/City"), max_length=75, blank=True, null=True ) deposit = models.PositiveSmallIntegerField(default=0, editable=False) registration_date = models.DateField(auto_now_add=True, editable=False) paid_until = models.DateField( _('Paid until'), blank=True, null=True, editable=True ) confirmed = models.BooleanField( _('Confirmed'), default=False, help_text=_('This person has paid the membership fee.') ) nullable_personal_data = ( 'gender', 'birthday', 'telephone', 'street_name', 'post_code', 'city') blankable_personal_data = ( 'email', 'password', 'first_name', 'last_name', 'website',) class Meta(object): """To manage object ordering and dispalynames on the admin interface.""" ordering = ('username',) swappable = 'AUTH_USER_MODEL' verbose_name = _('Membership') verbose_name_plural = _('Memberships') @property def full_name(self): return " ".join([self.last_name, self.first_name]) def __str__(self): return self.username def get_absolute_url(self): """:return the URL for this Membership DetailView""" return reverse('membership-details', kwargs={'username': self.username}) def save(self, *args, **kwargs): """Save the Useraccount, and add him tho the "Paid Membership" group if he activated the "membership" checkbox and has been validated. :param args: passed through the save() method from django :param kwargs: passed through the save() method from django """ super(Membership, self).save(*args, **kwargs) if self.confirmed: self.groups.add(PAID_MEMBERSHIP_GROUP) else: self.groups.remove(PAID_MEMBERSHIP_GROUP)