diff --git a/DjangoFiles/Administration/admin_public.py b/DjangoFiles/Administration/admin_public.py index 2812f8d..9ecc1d1 100644 --- a/DjangoFiles/Administration/admin_public.py +++ b/DjangoFiles/Administration/admin_public.py @@ -1,7 +1,16 @@ from django.contrib import admin +from django.contrib.auth.models import Group + from django.contrib.admin import AdminSite +from django.contrib.auth.admin import UserAdmin, GroupAdmin + from Customers.models import Client, Domain -# Register your models here. +from AuthBillet.models import TibilletUser, HumanUser, TermUser, SuperHumanUser +from django.utils.translation import gettext, gettext_lazy as _ + + +# from boutique.models import Category, Product, Tag, VAT, Event, LandingPageContent, Billet +# from solo.admin import SingletonModelAdmin class PublicAdminSite(AdminSite): site_header = "TiBillet Public Admin" @@ -15,9 +24,99 @@ class PublicAdminSite(AdminSite): public_admin_site = PublicAdminSite(name='public_admin') +# USER +# -------------------------------------/ + +class UserAdminTibillet(UserAdmin): + list_display = ( + 'email', + 'is_active', + 'is_staff', + 'is_superuser', + 'client_source', + 'achat', + 'administre', + 'espece', + ) + + list_filter = ( + 'email', + 'is_active', + 'client_source', + 'espece', + ) + + fieldsets = ( + (None, {'fields': ('username', 'password')}), + (_('Personal info'), { + 'fields': ( + 'first_name', + 'last_name', + 'email', + 'client_source', + 'client_admin', + 'client_achat', + 'offre', + )}), + (_('Permissions'), { + 'fields': ( + 'is_active', + 'is_staff', + 'is_superuser', + 'groups', + 'user_permissions', + ), + }), + (_('Important dates'), {'fields': ('last_login', 'date_joined')}), + ) + # + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ( + 'email', + 'password1', + 'password2', + 'is_active', + 'client_source', + 'client_achat', + 'espece', + )} + ), + ) + + search_fields = ('email',) + ordering = ('email',) + + # def save_model(self, request, obj, form, change): + # obj.client_source = request.tenant + # obj.save() + # + # staff_group = Group.objects.get_or_create(name="staff")[0] + # obj.groups.add(staff_group) + # obj.client_achat.add(request.tenant) + # + # super(UserAdminTibillet, self).save_model(request, obj, form, change) + + +public_admin_site.register(TibilletUser, UserAdminTibillet) + + +class CustomGroupAdmin(GroupAdmin): + pass + + +public_admin_site.register(Group, CustomGroupAdmin) + + +# -------------------------------------/ +# USER +# -------------------------------------/ + class DomainInline(admin.TabularInline): model = Domain + class ClientAdmin(admin.ModelAdmin): inlines = [DomainInline] list_display = ( diff --git a/DjangoFiles/Administration/admin_tenant.py b/DjangoFiles/Administration/admin_tenant.py index 2c2e8ae..61a0e19 100644 --- a/DjangoFiles/Administration/admin_tenant.py +++ b/DjangoFiles/Administration/admin_tenant.py @@ -1,8 +1,13 @@ from django.contrib import admin from django.contrib.admin import AdminSite +from django.contrib.auth.models import Group from solo.admin import SingletonModelAdmin +from AuthBillet.models import HumanUser, SuperHumanUser, TermUser from BaseBillet.models import Configuration, Event, OptionGenerale +from django.contrib.auth.admin import UserAdmin + +from Customers.models import Client class StaffAdminSite(AdminSite): @@ -11,12 +16,86 @@ class StaffAdminSite(AdminSite): site_url = '/' def has_permission(self, request): - return request.user.is_superuser + """ + Removed check for is_staff. + """ + try: + if request.tenant in request.user.client_admin.all(): + return request.user.is_staff + elif request.user.client_source == Client.objects.get(schema_name="public"): + return request.user.is_superuser + else: + return False + except AttributeError: + # AttributeError: 'AnonymousUser' object has no attribute 'client_source' + return False + except Exception as e: + raise e staff_admin_site = StaffAdminSite(name='staff_admin') +# USER +# -------------------------------------/ +class UserAdminTibillet(UserAdmin): + # list_display = ('email', 'client_source', 'achat') + list_display = ('email', 'is_active') + list_filter = ('email', 'is_active',) + fieldsets = ( + (None, {'fields': ('email', 'password')}), + # ('Permissions', {'fields': ('is_staff', 'is_active')}), + ) + + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('email', 'password1', 'password2', 'is_active')} + ), + ) + + search_fields = ('email',) + ordering = ('email',) + + def save_model(self, request, obj, form, change): + if not obj.client_source: + obj.client_source = request.tenant + obj.username = obj.email + obj.save() + obj.client_achat.add(request.tenant) + + +class HumanUserAdmin(UserAdminTibillet): + pass + + +staff_admin_site.register(HumanUser, HumanUserAdmin) + + +class SuperHumanUserAdmin(UserAdminTibillet): + def save_model(self, request, obj, form, change): + + super(SuperHumanUserAdmin, self).save_model(request, obj, form, change) + + staff_group = Group.objects.get_or_create(name="staff")[0] + obj.groups.add(staff_group) + obj.client_achat.add(request.tenant) + obj.client_admin.add(request.tenant) + + # execution du save de la classe orginale user admin ( heritage de l'hérité ) + super(UserAdminTibillet, self).save_model(request, obj, form, change) + + +staff_admin_site.register(SuperHumanUser, SuperHumanUserAdmin) + + +class TermUserAdmin(UserAdminTibillet): + pass + + +staff_admin_site.register(TermUser, TermUserAdmin) + + ######################################################################## diff --git a/DjangoFiles/AuthBillet/email.py b/DjangoFiles/AuthBillet/email.py new file mode 100644 index 0000000..d0f349b --- /dev/null +++ b/DjangoFiles/AuthBillet/email.py @@ -0,0 +1,90 @@ +from django.contrib.auth.tokens import default_token_generator +from rest_framework import serializers +from templated_mail.mail import BaseEmailMessage + +from djoser import utils +from djoser.conf import settings + +from BaseBillet.models import Configuration + + +class ActivationEmail(BaseEmailMessage): + template_name = "email/activation.html" + + def get_context_data(self): + # ActivationEmail can be deleted + print("activation !") + context = super().get_context_data() + # print(f"context : {context}") + # import ipdb; ipdb.set_trace() + + user = context.get("user") + context["site_name"] = self.request.tenant.name + context["domain"] = self.request.tenant.domain_url + context["uid"] = utils.encode_uid(user.pk) + context["token"] = default_token_generator.make_token(user) + context["url"] = settings.ACTIVATION_URL.format(**context) + print(f"context : {context}") + return context + + def send(self, to, *args, **kwargs): + configuration = Configuration.get_solo() + if not configuration.email : + return serializers.ValidationError(_(f"Manque l'email de la structure. Merci de configurer votre instance.")) + + self.render() + + self.to = to + self.cc = kwargs.pop('cc', []) + self.bcc = kwargs.pop('bcc', []) + self.reply_to = kwargs.pop('reply_to', []) + self.from_email = kwargs.pop( + 'from_email', configuration.email + ) + + # import ipdb; ipdb.set_trace() + mail_send = super(BaseEmailMessage, self).send(*args, **kwargs) + print(f'mail_send to {self.to} from {self.from_email} : {mail_send}') + + +class ConfirmationEmail(BaseEmailMessage): + template_name = "email/confirmation.html" + + +class PasswordResetEmail(BaseEmailMessage): + template_name = "email/password_reset.html" + + def get_context_data(self): + # PasswordResetEmail can be deleted + context = super().get_context_data() + + user = context.get("user") + context["site_name"] = self.request.tenant.name + context["domain"] = self.request.tenant.domain_url + context["uid"] = utils.encode_uid(user.pk) + context["token"] = default_token_generator.make_token(user) + context["url"] = settings.PASSWORD_RESET_CONFIRM_URL.format(**context) + return context + + +class PasswordChangedConfirmationEmail(BaseEmailMessage): + template_name = "email/password_changed_confirmation.html" + + +class UsernameChangedConfirmationEmail(BaseEmailMessage): + template_name = "email/username_changed_confirmation.html" + + +class UsernameResetEmail(BaseEmailMessage): + template_name = "email/username_reset.html" + + def get_context_data(self): + context = super().get_context_data() + + user = context.get("user") + context["site_name"] = self.request.tenant.name + context["domain"] = self.request.tenant.domain_url + context["uid"] = utils.encode_uid(user.pk) + context["token"] = default_token_generator.make_token(user) + context["url"] = settings.USERNAME_RESET_CONFIRM_URL.format(**context) + return context diff --git a/DjangoFiles/AuthBillet/migrations/0001_initial.py b/DjangoFiles/AuthBillet/migrations/0001_initial.py new file mode 100644 index 0000000..8583765 --- /dev/null +++ b/DjangoFiles/AuthBillet/migrations/0001_initial.py @@ -0,0 +1,83 @@ +# Generated by Django 2.2 on 2021-06-23 09:09 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0011_update_proxy_permissions'), + ('Customers', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='TibilletUser', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('email', models.EmailField(max_length=254, unique=True, verbose_name='email')), + ('username', models.CharField(blank=True, max_length=200, null=True)), + ('espece', models.CharField(choices=[('TE', 'Terminal'), ('AN', 'Android'), ('HU', 'Humain')], default='HU', max_length=2)), + ('offre', models.CharField(choices=[('PU', 'Public'), ('FR', 'Gratuit'), ('PR', 'Premium'), ('EN', 'Entreprise'), ('CU', 'Custom')], default='PU', max_length=2)), + ('client_achat', models.ManyToManyField(blank=True, related_name='user_achats', to='Customers.Client')), + ('client_admin', models.ManyToManyField(blank=True, related_name='user_admin', to='Customers.Client')), + ('client_source', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_principal', to='Customers.Client', verbose_name='Inscription depuis')), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + ), + migrations.CreateModel( + name='HumanUser', + fields=[ + ], + options={ + 'verbose_name': 'Utilisateur', + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('AuthBillet.tibilletuser',), + ), + migrations.CreateModel( + name='SuperHumanUser', + fields=[ + ], + options={ + 'verbose_name': 'Administrateur', + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('AuthBillet.tibilletuser',), + ), + migrations.CreateModel( + name='TermUser', + fields=[ + ], + options={ + 'verbose_name': 'Terminal', + 'verbose_name_plural': 'Terminaux', + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('AuthBillet.tibilletuser',), + ), + ] diff --git a/DjangoFiles/AuthBillet/models.py b/DjangoFiles/AuthBillet/models.py index 71a8362..a344fb6 100644 --- a/DjangoFiles/AuthBillet/models.py +++ b/DjangoFiles/AuthBillet/models.py @@ -1,3 +1,222 @@ +from django.contrib.auth.base_user import BaseUserManager, AbstractBaseUser from django.db import models +from django.utils.translation import ugettext_lazy as _ +import uuid +from django.contrib.auth.models import AbstractUser, Group, Permission +from Customers.models import Client +from django.db import connection -# Create your models here. + +class TibilletManager(BaseUserManager): + def _create_user(self, email, password, **extra_fields): + # import ipdb; ipdb.set_trace() + if not email: + raise ValueError(_("email obligatoire")) + + email = self.normalize_email(email) + user = self.model(username=email, email=email, **extra_fields) + user.set_password(password) + + user.client_source = connection.tenant + user.save() + + user.client_achat.add(connection.tenant) + return user + + def create_user(self, email, password, **extra_fields): + extra_fields.setdefault('is_staff', False) + extra_fields.setdefault('is_superuser', False) + return self._create_user(email, password, **extra_fields) + + def create_staffuser(self, email, password, **extra_fields): + extra_fields.setdefault('is_staff', True) + extra_fields.setdefault('is_superuser', False) + return self.set_permission_staff(self._create_user(email, password, **extra_fields)) + + def create_superuser(self, email, password, **extra_fields): + extra_fields.setdefault('is_staff', True) + extra_fields.setdefault('is_superuser', True) + return self._create_user(email, password, **extra_fields) + + def set_permission_staff(self, user): + staff_group = Group.objects.get_or_create(name="staff")[0] + user.groups.add(staff_group) + + +class TibilletUser(AbstractUser): + + #TODO regarder du coté du dashboard de jet, ça plante avec uuid ! + # uuid_user = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True, db_index=True) + + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = [] # removes email from REQUIRED_FIELDS + + email = models.EmailField(_('email'), unique=True) # changes email to unique and blank to false + username = models.CharField(max_length=200, null=True, blank=True) + + TYPE_TERM, TYPE_HUM, TYPE_ANDR = 'TE', 'HU', 'AN' + ESPECE_CHOICES = ( + (TYPE_TERM, 'Terminal'), + (TYPE_ANDR, 'Android'), + (TYPE_HUM, 'Humain'), + ) + + espece = models.CharField(max_length=2, + choices=ESPECE_CHOICES, + default=TYPE_HUM) + + PUBLIC, FREE, PREMIUM, ENTREPRISE, CUSTOM = 'PU', 'FR', 'PR', 'EN', 'CU' + OFFRE_CHOICES = ( + (PUBLIC, 'Public'), + (FREE, 'Gratuit'), + (PREMIUM, 'Premium'), + (ENTREPRISE, 'Entreprise'), + (CUSTOM, 'Custom'), + ) + + offre = models.CharField(max_length=2, + choices=OFFRE_CHOICES, + default=PUBLIC) + + # Inscription depuis : + client_source = models.ForeignKey(Client, + on_delete=models.SET_NULL, + null=True, + blank=True, + verbose_name=_("Inscription depuis"), + related_name="user_principal", + ) + + # ou as t il acheté ? + client_achat = models.ManyToManyField(Client, + related_name="user_achats", blank=True) + + # sur quelle interface d'admin peut il aller ? + client_admin = models.ManyToManyField(Client, + related_name="user_admin", blank=True) + + objects = TibilletManager() + + def achat(self): + return " ".join([achat["schema_name"] for achat in self.client_achat.values("schema_name")]) + + def administre(self): + return " ".join([admin["schema_name"] for admin in self.client_admin.values("schema_name")]) + + def new_tenant_authorised(self): + + # si l'user est déja staff ou root, c'est qu'il a déja une billetterie. + if self.offre in [self.PUBLIC, self.FREE, self.PREMIUM]: + if len(self.client_admin.all()) > 0: + # superuser peut toujours. + return self.is_superuser + else: + return True + + elif self.offre == self.ENTREPRISE: + if len(self.client_admin.all()) > 4: + # superuser peut toujours. + return self.is_superuser + else: + return True + + elif self.offre == self.CUSTOM: + return True + + def set_staff(self, tenant): + self.client_admin.add(tenant) + self.is_staff = True + self.groups.add(Group.objects.get(name="staff")) + self.save() + + def __str__(self): + return self.email + + +# --------------------------------------------------------------------------------------------------------------------- + +class TermUserManager(TibilletManager): + def get_queryset(self): + return super().get_queryset().filter(espece=TibilletUser.TYPE_TERM, + client_source=connection.tenant, + ) + + +class TermUser(TibilletUser): + class Meta: + proxy = True + verbose_name = "Terminal" + verbose_name_plural = "Terminaux" + + objects = TermUserManager() + + def save(self, *args, **kwargs): + # Si création : + if not self.pk: + self.espece = TibilletUser.TYPE_TERM + + super().save(*args, **kwargs) + + +# --------------------------------------------------------------------------------------------------------------------- + +class HumanUserManager(TibilletManager): + + def get_queryset(self): + return super().get_queryset().filter(espece=TibilletUser.TYPE_HUM, + is_staff=False, + is_superuser=False, + client_achat__id__in=[connection.tenant.id, ], + ) + # .distinct() ??? + + +class HumanUser(TibilletUser): + class Meta: + proxy = True + verbose_name = "Utilisateur" + + objects = HumanUserManager() + + def save(self, *args, **kwargs): + # Si création : + if not self.pk: + self.espece = TibilletUser.TYPE_HUM + + self.is_staff = False + self.is_superuser = False + + super().save(*args, **kwargs) + + +# --------------------------------------------------------------------------------------------------------------------- + +class SuperHumanUserManager(TibilletManager): + def get_queryset(self): + return super().get_queryset().filter( + espece=TibilletUser.TYPE_HUM, + is_staff=True, + is_superuser=False, + client_admin__id__in=[connection.tenant.id, ], + ) + + +class SuperHumanUser(TibilletUser): + class Meta: + proxy = True + verbose_name = "Administrateur" + + objects = SuperHumanUserManager() + + def save(self, *args, **kwargs): + # import ipdb; ipdb.set_trace() + # Si création : + if not self.pk: + self.espece = TibilletUser.TYPE_HUM + + self.is_staff = True + self.is_superuser = False + + super().save(*args, **kwargs) + +# --------------------------------------------------------------------------------------------------------------------- diff --git a/DjangoFiles/AuthBillet/templates/email/activation.html b/DjangoFiles/AuthBillet/templates/email/activation.html new file mode 100644 index 0000000..e155ce1 --- /dev/null +++ b/DjangoFiles/AuthBillet/templates/email/activation.html @@ -0,0 +1,29 @@ +{% load i18n %} + +{% block subject %} +{% blocktrans %}Account activation on {{ site_name }}{% endblocktrans %} +{% endblock subject %} + +{% block text_body %} +{% blocktrans %}You're receiving this email because you need to finish activation process on {{ site_name }}.{% endblocktrans %} + +{% trans "Please go to the following page to activate account:" %} +{{ protocol }}://{{ domain }}/{{ url|safe }} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} +{% endblock text_body %} + +{% block html_body %} +

{% blocktrans %}You're receiving this email because you need to finish activation process on {{ site_name }}.{% endblocktrans %}

+ +

{% trans "Please go to the following page to activate account:" %}

+

{{ protocol }}://{{ domain }}/{{ url|safe }}

+ +

{% trans "Thanks for using our site!" %}

+ +

{% blocktrans %}The {{ site_name }} team{% endblocktrans %}

+ +{% endblock html_body %} + diff --git a/DjangoFiles/AuthBillet/templates/email/confirmation.html b/DjangoFiles/AuthBillet/templates/email/confirmation.html new file mode 100644 index 0000000..1d26efc --- /dev/null +++ b/DjangoFiles/AuthBillet/templates/email/confirmation.html @@ -0,0 +1,21 @@ +{% load i18n %} + +{% block subject %} +{% blocktrans %}{{ site_name }} - Your account has been successfully created and activated!{% endblocktrans %} +{% endblock %} + +{% block text_body %} +{% trans "Your account has been created and is ready to use!" %} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} +{% endblock text_body %} + +{% block html_body %} +

{% trans "Your account has been created and is ready to use!" %}

+ +

{% trans "Thanks for using our site!" %}

+ +

{% blocktrans %}The {{ site_name }} team{% endblocktrans %}

+{% endblock html_body %} diff --git a/DjangoFiles/AuthBillet/templates/email/password_changed_confirmation.html b/DjangoFiles/AuthBillet/templates/email/password_changed_confirmation.html new file mode 100644 index 0000000..fdf6658 --- /dev/null +++ b/DjangoFiles/AuthBillet/templates/email/password_changed_confirmation.html @@ -0,0 +1,21 @@ +{% load i18n %} + +{% block subject %} +{% blocktrans %}{{ site_name }} - Your password has been successfully changed!{% endblocktrans %} +{% endblock %} + +{% block text_body %} +{% trans "Your password has been changed!" %} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} +{% endblock text_body %} + +{% block html_body %} +

{% trans "Your password has been changed!" %}

+ +

{% trans "Thanks for using our site!" %}

+ +

{% blocktrans %}The {{ site_name }} team{% endblocktrans %}

+{% endblock html_body %} diff --git a/DjangoFiles/AuthBillet/templates/email/password_reset.html b/DjangoFiles/AuthBillet/templates/email/password_reset.html new file mode 100644 index 0000000..88b1fec --- /dev/null +++ b/DjangoFiles/AuthBillet/templates/email/password_reset.html @@ -0,0 +1,29 @@ +{% load i18n %} + +{% block subject %} +{% blocktrans %}Password reset on {{ site_name }}{% endblocktrans %} +{% endblock subject %} + +{% block text_body %} +{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %} + +{% trans "Please go to the following page and choose a new password:" %} +{{ protocol }}://{{ domain }}/{{ url }} +{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} +{% endblock text_body %} + +{% block html_body %} +

{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}

+ +

{% trans "Please go to the following page and choose a new password:" %}

+{{ protocol }}://{{ domain }}/{{ url }} +

{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}

+ +

{% trans "Thanks for using our site!" %}

+ +

{% blocktrans %}The {{ site_name }} team{% endblocktrans %}

+{% endblock html_body %} diff --git a/DjangoFiles/AuthBillet/templates/email/username_changed_confirmation.html b/DjangoFiles/AuthBillet/templates/email/username_changed_confirmation.html new file mode 100644 index 0000000..053ac66 --- /dev/null +++ b/DjangoFiles/AuthBillet/templates/email/username_changed_confirmation.html @@ -0,0 +1,21 @@ +{% load i18n %} + +{% block subject %} +{% blocktrans %}{{ site_name }} - Your username has been successfully changed!{% endblocktrans %} +{% endblock %} + +{% block text_body %} +{% trans "Your username has been changed!" %} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} +{% endblock text_body %} + +{% block html_body %} +

{% trans "Your username has been changed!" %}

+ +

{% trans "Thanks for using our site!" %}

+ +

{% blocktrans %}The {{ site_name }} team{% endblocktrans %}

+{% endblock html_body %} diff --git a/DjangoFiles/AuthBillet/templates/email/username_reset.html b/DjangoFiles/AuthBillet/templates/email/username_reset.html new file mode 100644 index 0000000..6b437ca --- /dev/null +++ b/DjangoFiles/AuthBillet/templates/email/username_reset.html @@ -0,0 +1,29 @@ +{% load i18n %} + +{% block subject %} +{% blocktrans %}Username reset on {{ site_name }}{% endblocktrans %} +{% endblock subject %} + +{% block text_body %} +{% blocktrans %}You're receiving this email because you requested a username reset for your user account at {{ site_name }}.{% endblocktrans %} + +{% trans "Please go to the following page and choose a new username:" %} +{{ protocol }}://{{ domain }}/{{ url }} +{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} +{% endblock text_body %} + +{% block html_body %} +

{% blocktrans %}You're receiving this email because you requested a username reset for your user account at {{ site_name }}.{% endblocktrans %}

+ +

{% trans "Please go to the following page and choose a new username:" %}

+{{ protocol }}://{{ domain }}/{{ url }} +

{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}

+ +

{% trans "Thanks for using our site!" %}

+ +

{% blocktrans %}The {{ site_name }} team{% endblocktrans %}

+{% endblock html_body %} diff --git a/DjangoFiles/BaseBillet/migrations/0001_initial.py b/DjangoFiles/BaseBillet/migrations/0001_initial.py index 39771b9..cff500b 100644 --- a/DjangoFiles/BaseBillet/migrations/0001_initial.py +++ b/DjangoFiles/BaseBillet/migrations/0001_initial.py @@ -1,6 +1,9 @@ -# Generated by Django 2.2 on 2021-06-21 14:58 +# Generated by Django 2.2 on 2021-06-23 09:09 from django.db import migrations, models +import django.db.models.deletion +import stdimage.models +import stdimage.validators class Migration(migrations.Migration): @@ -11,12 +14,63 @@ class Migration(migrations.Migration): ] operations = [ + migrations.CreateModel( + name='Event', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('short_description', models.CharField(max_length=250)), + ('long_description', models.TextField(blank=True, null=True)), + ('datetime', models.DateTimeField()), + ('img', stdimage.models.StdImageField(blank=True, null=True, upload_to='images/', validators=[stdimage.validators.MaxSizeValidator(1920, 1920)])), + ('reservations', models.PositiveSmallIntegerField(default=0)), + ], + options={ + 'verbose_name': 'Evenement', + 'verbose_name_plural': 'Evenements', + 'ordering': ('datetime',), + }, + ), + migrations.CreateModel( + name='OptionGenerale', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=30)), + ('poids', models.PositiveSmallIntegerField(default=0, verbose_name='Poids')), + ], + options={ + 'verbose_name': 'Options Generales', + 'verbose_name_plural': 'Options Generales', + 'ordering': ('poids',), + }, + ), + migrations.CreateModel( + name='reservation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.CharField(choices=[('NAN', 'Annulée'), ('NOV', 'Email non validé'), ('VAL', 'Validée'), ('PAY', 'Payée')], default='NOV', max_length=3, verbose_name='Status de la réservation')), + ('qty', models.IntegerField()), + ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reservation', to='BaseBillet.Event')), + ], + ), migrations.CreateModel( name='Configuration', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('organisation', models.CharField(max_length=50)), ('short_description', models.CharField(max_length=250)), + ('adresse', models.CharField(max_length=250)), + ('phone', models.CharField(max_length=20)), + ('email', models.EmailField(max_length=254)), + ('twitter', models.URLField(blank=True, null=True)), + ('facebook', models.URLField(blank=True, null=True)), + ('instagram', models.URLField(blank=True, null=True)), + ('img', stdimage.models.StdImageField(blank=True, null=True, upload_to='images/')), + ('mollie_api_key', models.CharField(blank=True, max_length=50, null=True)), + ('reservation_par_user_max', models.PositiveSmallIntegerField(default=6)), + ('jauge_max', models.PositiveSmallIntegerField(default=50)), + ('option_generale_checkbox', models.ManyToManyField(blank=True, related_name='checkbox', to='BaseBillet.OptionGenerale')), + ('option_generale_radio', models.ManyToManyField(blank=True, related_name='radiobutton', to='BaseBillet.OptionGenerale')), ], options={ 'abstract': False, diff --git a/DjangoFiles/BaseBillet/migrations/0002_auto_20210621_1926.py b/DjangoFiles/BaseBillet/migrations/0002_auto_20210621_1926.py deleted file mode 100644 index 19b595d..0000000 --- a/DjangoFiles/BaseBillet/migrations/0002_auto_20210621_1926.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 2.2 on 2021-06-21 15:26 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('BaseBillet', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='configuration', - name='facebook', - field=models.URLField(blank=True, null=True), - ), - migrations.AddField( - model_name='configuration', - name='instagram', - field=models.URLField(blank=True, null=True), - ), - migrations.AddField( - model_name='configuration', - name='twitter', - field=models.URLField(blank=True, null=True), - ), - ] diff --git a/DjangoFiles/BaseBillet/migrations/0003_event.py b/DjangoFiles/BaseBillet/migrations/0003_event.py deleted file mode 100644 index 59c0c49..0000000 --- a/DjangoFiles/BaseBillet/migrations/0003_event.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2 on 2021-06-21 15:36 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('BaseBillet', '0002_auto_20210621_1926'), - ] - - operations = [ - migrations.CreateModel( - name='Event', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200)), - ('short_description', models.CharField(max_length=250)), - ('long_description', models.TextField(blank=True, null=True)), - ('datetime', models.DateTimeField()), - ], - ), - ] diff --git a/DjangoFiles/BaseBillet/migrations/0004_auto_20210621_2059.py b/DjangoFiles/BaseBillet/migrations/0004_auto_20210621_2059.py deleted file mode 100644 index e22449a..0000000 --- a/DjangoFiles/BaseBillet/migrations/0004_auto_20210621_2059.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 2.2 on 2021-06-21 16:59 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('BaseBillet', '0003_event'), - ] - - operations = [ - migrations.AlterModelOptions( - name='event', - options={'ordering': ('datetime',), 'verbose_name': 'Evenement', 'verbose_name_plural': 'Evenements'}, - ), - migrations.AddField( - model_name='configuration', - name='adresse', - field=models.CharField(default='route de la joie', max_length=250), - preserve_default=False, - ), - migrations.AddField( - model_name='configuration', - name='email', - field=models.EmailField(default='demo@demo.demo', max_length=254), - preserve_default=False, - ), - migrations.AddField( - model_name='configuration', - name='phone', - field=models.CharField(default='-262 6 121212', max_length=20), - preserve_default=False, - ), - ] diff --git a/DjangoFiles/BaseBillet/migrations/0005_auto_20210621_2119.py b/DjangoFiles/BaseBillet/migrations/0005_auto_20210621_2119.py deleted file mode 100644 index 001d383..0000000 --- a/DjangoFiles/BaseBillet/migrations/0005_auto_20210621_2119.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 2.2 on 2021-06-21 17:19 - -from django.db import migrations -import stdimage.models -import stdimage.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('BaseBillet', '0004_auto_20210621_2059'), - ] - - operations = [ - migrations.AddField( - model_name='configuration', - name='img', - field=stdimage.models.StdImageField(blank=True, null=True, upload_to='images/', validators=[stdimage.validators.MaxSizeValidator(1920, 1920)]), - ), - migrations.AddField( - model_name='event', - name='img', - field=stdimage.models.StdImageField(blank=True, null=True, upload_to='images/', validators=[stdimage.validators.MaxSizeValidator(1920, 1920)]), - ), - ] diff --git a/DjangoFiles/BaseBillet/migrations/0006_auto_20210622_1053.py b/DjangoFiles/BaseBillet/migrations/0006_auto_20210622_1053.py deleted file mode 100644 index 2ceb71d..0000000 --- a/DjangoFiles/BaseBillet/migrations/0006_auto_20210622_1053.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2 on 2021-06-22 06:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('BaseBillet', '0005_auto_20210621_2119'), - ] - - operations = [ - migrations.AddField( - model_name='configuration', - name='mollie_api_key', - field=models.CharField(blank=True, max_length=50, null=True), - ), - migrations.AddField( - model_name='configuration', - name='reservation_max', - field=models.PositiveSmallIntegerField(default=6), - ), - ] diff --git a/DjangoFiles/BaseBillet/migrations/0007_auto_20210622_1103.py b/DjangoFiles/BaseBillet/migrations/0007_auto_20210622_1103.py deleted file mode 100644 index 59ffdec..0000000 --- a/DjangoFiles/BaseBillet/migrations/0007_auto_20210622_1103.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2 on 2021-06-22 07:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('BaseBillet', '0006_auto_20210622_1053'), - ] - - operations = [ - migrations.RenameField( - model_name='configuration', - old_name='reservation_max', - new_name='reservation_par_user_max', - ), - migrations.AddField( - model_name='configuration', - name='jauge_max', - field=models.PositiveSmallIntegerField(default=50), - preserve_default=False, - ), - ] diff --git a/DjangoFiles/BaseBillet/migrations/0008_event_reservations.py b/DjangoFiles/BaseBillet/migrations/0008_event_reservations.py deleted file mode 100644 index 056205c..0000000 --- a/DjangoFiles/BaseBillet/migrations/0008_event_reservations.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2021-06-22 07:05 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('BaseBillet', '0007_auto_20210622_1103'), - ] - - operations = [ - migrations.AddField( - model_name='event', - name='reservations', - field=models.PositiveSmallIntegerField(default=6), - ), - ] diff --git a/DjangoFiles/BaseBillet/migrations/0009_auto_20210622_1139.py b/DjangoFiles/BaseBillet/migrations/0009_auto_20210622_1139.py deleted file mode 100644 index d7fbe18..0000000 --- a/DjangoFiles/BaseBillet/migrations/0009_auto_20210622_1139.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 2.2 on 2021-06-22 07:39 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('BaseBillet', '0008_event_reservations'), - ] - - operations = [ - migrations.CreateModel( - name='OptionGenerale', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=30)), - ], - ), - migrations.AlterField( - model_name='event', - name='reservations', - field=models.PositiveSmallIntegerField(default=0), - ), - migrations.AddField( - model_name='configuration', - name='option_generale_checkbox', - field=models.ManyToManyField(blank=True, related_name='checkbox', to='BaseBillet.OptionGenerale'), - ), - migrations.AddField( - model_name='configuration', - name='option_generale_radio', - field=models.ManyToManyField(blank=True, related_name='radiobutton', to='BaseBillet.OptionGenerale'), - ), - ] diff --git a/DjangoFiles/BaseBillet/migrations/0010_auto_20210622_1149.py b/DjangoFiles/BaseBillet/migrations/0010_auto_20210622_1149.py deleted file mode 100644 index 411780b..0000000 --- a/DjangoFiles/BaseBillet/migrations/0010_auto_20210622_1149.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 2.2 on 2021-06-22 07:49 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('BaseBillet', '0009_auto_20210622_1139'), - ] - - operations = [ - migrations.AlterModelOptions( - name='optiongenerale', - options={'ordering': ('poids',), 'verbose_name': 'Options Generales', 'verbose_name_plural': 'Options Generales'}, - ), - migrations.AddField( - model_name='optiongenerale', - name='poids', - field=models.PositiveSmallIntegerField(default=0, verbose_name='Poids'), - ), - ] diff --git a/DjangoFiles/BaseBillet/migrations/0011_reservation.py b/DjangoFiles/BaseBillet/migrations/0011_reservation.py deleted file mode 100644 index 6b3fc83..0000000 --- a/DjangoFiles/BaseBillet/migrations/0011_reservation.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2 on 2021-06-22 09:22 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('BaseBillet', '0010_auto_20210622_1149'), - ] - - operations = [ - migrations.CreateModel( - name='reservation', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('status', models.CharField(choices=[('NAN', 'Annulée'), ('NOV', 'Email non validé'), ('VAL', 'Validée'), ('PAY', 'Payée')], default='NOV', max_length=3, verbose_name='Status de la réservation')), - ('qty', models.IntegerField()), - ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reservation', to='BaseBillet.Event')), - ], - ), - ] diff --git a/DjangoFiles/BaseBillet/migrations/0012_auto_20210623_1021.py b/DjangoFiles/BaseBillet/migrations/0012_auto_20210623_1021.py deleted file mode 100644 index 19d2446..0000000 --- a/DjangoFiles/BaseBillet/migrations/0012_auto_20210623_1021.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2021-06-23 06:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('BaseBillet', '0011_reservation'), - ] - - operations = [ - migrations.AlterField( - model_name='configuration', - name='jauge_max', - field=models.PositiveSmallIntegerField(default=50), - ), - ] diff --git a/DjangoFiles/BaseBillet/migrations/0013_auto_20210623_1025.py b/DjangoFiles/BaseBillet/migrations/0013_auto_20210623_1025.py deleted file mode 100644 index 97cc5bc..0000000 --- a/DjangoFiles/BaseBillet/migrations/0013_auto_20210623_1025.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2021-06-23 06:25 - -from django.db import migrations -import stdimage.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('BaseBillet', '0012_auto_20210623_1021'), - ] - - operations = [ - migrations.AlterField( - model_name='configuration', - name='img', - field=stdimage.models.StdImageField(blank=True, null=True, upload_to='images/'), - ), - ] diff --git a/DjangoFiles/BaseBillet/models.py b/DjangoFiles/BaseBillet/models.py index c16427a..ef6bc40 100644 --- a/DjangoFiles/BaseBillet/models.py +++ b/DjangoFiles/BaseBillet/models.py @@ -53,7 +53,8 @@ class Configuration(SingletonModel): 'med': (480, 480), 'thumbnail': (150, 90), }, - delete_orphans=True + delete_orphans=True, + verbose_name='Background' ) mollie_api_key = models.CharField(max_length=50, diff --git a/DjangoFiles/BaseBillet/templates/html5up-massively/event.html b/DjangoFiles/BaseBillet/templates/html5up-massively/event.html index af330d8..6c10c41 100644 --- a/DjangoFiles/BaseBillet/templates/html5up-massively/event.html +++ b/DjangoFiles/BaseBillet/templates/html5up-massively/event.html @@ -39,12 +39,12 @@
- {{ event.datetime | date:"d F Y"}}
{{ event.datetime | time }}
+ {{ event.datetime | date:"d F Y" }}
{{ event.datetime | time }}

{{ event.name }}

{{ event.short_description }}

-

{{ event.long_description | linebreaks}}

+

{{ event.long_description | linebreaks }}

@@ -79,7 +79,8 @@ {% for option_radio in configuration.option_generale_radio.all %}
- +
{% endfor %} @@ -87,7 +88,8 @@ {% for option_checkbox in configuration.option_generale_checkbox.all %}
- +
@@ -104,7 +106,7 @@
  • - +
    diff --git a/DjangoFiles/Customers/migrations/0001_initial.py b/DjangoFiles/Customers/migrations/0001_initial.py index 6d1bf81..a191149 100644 --- a/DjangoFiles/Customers/migrations/0001_initial.py +++ b/DjangoFiles/Customers/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2 on 2021-06-10 12:04 +# Generated by Django 2.2 on 2021-06-23 09:09 from django.db import migrations, models import django.db.models.deletion diff --git a/DjangoFiles/Customers/models.py b/DjangoFiles/Customers/models.py index 318080b..463fcf5 100644 --- a/DjangoFiles/Customers/models.py +++ b/DjangoFiles/Customers/models.py @@ -1,14 +1,19 @@ +from django.utils import timezone + from django.db import models from django_tenants.models import TenantMixin, DomainMixin class Client(TenantMixin): - name = models.CharField(max_length=100) - paid_until = models.DateField() - on_trial = models.BooleanField() + name = models.CharField(max_length=100, unique=True, db_index=True) + paid_until = models.DateField(default=timezone.now) + on_trial = models.BooleanField(default=True) created_on = models.DateField(auto_now_add=True) # default true, schema will be automatically created and synced when it is saved auto_create_schema = True + def __str__(self): + return self.name + class Domain(DomainMixin): pass \ No newline at end of file diff --git a/DjangoFiles/TiBillet/settings.py b/DjangoFiles/TiBillet/settings.py index bb1d724..5ffa0e1 100644 --- a/DjangoFiles/TiBillet/settings.py +++ b/DjangoFiles/TiBillet/settings.py @@ -79,6 +79,7 @@ TENANT_DOMAIN_MODEL = "Customers.Domain" # app.Model ROOT_URLCONF = 'TiBillet.urls_tenants' PUBLIC_SCHEMA_URLCONF = 'TiBillet.urls_public' SITE_ID = 1 +AUTH_USER_MODEL = 'AuthBillet.TibilletUser' MIDDLEWARE = [ @@ -131,7 +132,6 @@ DATABASE_ROUTERS = ( # Password validation # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators -# AUTH_USER_MODEL = 'AuthBillet.TibilletUser' AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', @@ -158,6 +158,25 @@ REST_FRAMEWORK = { SIMPLE_JWT = { 'AUTH_HEADER_TYPES': ('JWT',), } + +DJOSER = { + "SEND_ACTIVATION_EMAIL": True, + "PASSWORD_CHANGED_EMAIL_CONFIRMATION": True, + "PASSWORD_RESET_CONFIRM_URL": "password/reset/confirm/{uid}/{token}", + "USERNAME_RESET_CONFIRM_URL": "username/reset/confirm/{uid}/{token}", + # "ACTIVATION_URL": "activate/{uid}/{token}", + "ACTIVATION_URL": "activate/{uid}/{token}", + # "SOCIAL_AUTH_ALLOWED_REDIRECT_URIS": ["http://manap.django-local.org:8002/"], + 'EMAIL': { + 'activation': 'AuthBillet.email.ActivationEmail', + 'confirmation': 'AuthBillet.email.ConfirmationEmail', + 'password_reset': 'AuthBillet.email.PasswordResetEmail', + 'password_changed_confirmation': 'AuthBillet.email.PasswordChangedConfirmationEmail', + 'username_changed_confirmation': 'AuthBillet.email.UsernameChangedConfirmationEmail', + 'username_reset': 'AuthBillet.email.UsernameResetEmail', + }, +} + # Internationalization # https://docs.djangoproject.com/en/3.1/topics/i18n/ diff --git a/DjangoFiles/TiBillet/urls_tenants.py b/DjangoFiles/TiBillet/urls_tenants.py index 968cd36..9f2778b 100644 --- a/DjangoFiles/TiBillet/urls_tenants.py +++ b/DjangoFiles/TiBillet/urls_tenants.py @@ -27,6 +27,7 @@ urlpatterns = [ re_path(r'^auth/', include('djoser.urls')), re_path(r'^auth/', include('djoser.urls.authtoken')), + re_path(r'^auth/', include('djoser.urls.jwt')), path('', include('BaseBillet.urls')),