diff --git a/django_accounts_app/__init__.py b/django_accounts_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django_accounts_app/admin.py b/django_accounts_app/admin.py new file mode 100644 index 0000000..34f3794 --- /dev/null +++ b/django_accounts_app/admin.py @@ -0,0 +1,48 @@ +from server.apps.accounts.models import CustomUser, CustomGroup +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin, GroupAdmin, Group + + +@admin.register(CustomUser) +class CustomUserAdmin(UserAdmin): + + model = CustomUser + + list_display = ('username', 'email', 'first_name', 'last_name', 'mobile', 'verification_code', + 'is_superuser', 'is_staff', 'is_active', ) + + list_filter = ('username', 'first_name', 'last_name', 'user_created', 'is_superuser', 'is_staff', 'is_active', ) + + ordering = ('user_created', 'username', 'first_name', 'is_superuser', 'is_staff', 'is_active', ) + + fieldsets = ( + (None, {'fields': ('username', 'password')}), + ('Main fields', {'fields': ('first_name', 'last_name', 'verification_code', 'user_created', )} + ), + ('Contacts', {'fields': ('email', 'mobile', )} + ), + ('Permissions', {'fields': ('groups', 'user_permissions', 'is_superuser', 'is_staff', 'is_active')} + ), + ) + add_fieldsets = ( + ('Main fields', { + 'classes': ('wide', ), + 'fields': ('username', 'email', 'mobile', 'password1', 'password2', 'verification_code', ) + }), + ('Permissions', { + 'classes': ('wide', ), + 'fields': ('groups', 'user_permissions', 'is_superuser', 'is_staff', 'is_active') + }) + ) + readonly_fields = ('user_created', ) + + search_fields = ('username', 'first_name', 'last_name', 'mobile', 'email') + filter_horizontal = ('groups', 'user_permissions',) + + +admin.site.unregister(Group) + + +@admin.register(CustomGroup) +class CustomGroupAdmin(GroupAdmin): + ... diff --git a/django_accounts_app/apps.py b/django_accounts_app/apps.py new file mode 100644 index 0000000..a248cc5 --- /dev/null +++ b/django_accounts_app/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CustomUserConfig(AppConfig): + name = 'server.apps.accounts' + verbose_name = 'Admin User manager' diff --git a/django_accounts_app/forms.py b/django_accounts_app/forms.py new file mode 100644 index 0000000..08b83ec --- /dev/null +++ b/django_accounts_app/forms.py @@ -0,0 +1,18 @@ +from django import forms +from django.contrib.auth.forms import UserCreationForm + +from server.apps.accounts.models import CustomUser + + +class CustomUserCreationForm(UserCreationForm): + # username = forms.CharField(label='Логин', widget=forms.TextInput(attrs={'class': 'form-input'})) + # email = forms.EmailField(label='Email', widget=forms.EmailInput(attrs={'class': 'form-input'})) + # password1 = forms.CharField(label='Пароль', widget=forms.PasswordInput(attrs={'class': 'form-input'})) + # password2 = forms.CharField(label='Повтор пароля', + # widget=forms.PasswordInput(attrs={'class': 'form-input'})) + + class Meta: + model = CustomUser + fields = ('username', 'first_name', 'last_name', 'email', 'mobile', 'password1', 'password2') + + diff --git a/django_accounts_app/managers.py b/django_accounts_app/managers.py new file mode 100644 index 0000000..bac8ad4 --- /dev/null +++ b/django_accounts_app/managers.py @@ -0,0 +1,27 @@ +from django.contrib.auth.base_user import BaseUserManager +from django.utils.translation import ugettext_lazy as _ + + +class CustomUserManager(BaseUserManager): + + def create_user(self, username, password, **extra_fields): + + if not username: + raise ValueError(_('The username must be set')) + user = self.model(username=username, **extra_fields) + user.set_password(password) + user.save() + return user + + def create_superuser(self, username, password, **extra_fields): + """ + Create and save a SuperUser with the given email and password. + """ + extra_fields.setdefault('is_staff', True) + extra_fields.setdefault('is_superuser', True) + extra_fields.setdefault('is_active', True) + if extra_fields.get('is_staff') is not True: + raise ValueError(_('Superuser must have is_staff=True.')) + if extra_fields.get('is_superuser') is not True: + raise ValueError(_('Superuser must have is_superuser=True.')) + return self.create_user(username, password, **extra_fields) diff --git a/django_accounts_app/migrations/__init__.py b/django_accounts_app/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django_accounts_app/migrations/create_accounts_models.py b/django_accounts_app/migrations/create_accounts_models.py new file mode 100644 index 0000000..017e375 --- /dev/null +++ b/django_accounts_app/migrations/create_accounts_models.py @@ -0,0 +1,59 @@ +# Generated by Django 3.2.8 on 2021-10-24 11:03 + +import django.contrib.auth.models +import django.contrib.auth.validators +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='CustomGroup', + fields=[ + ('group_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='auth.group')), + ], + options={ + 'verbose_name': 'Permissions group', + 'verbose_name_plural': 'Permissions groups', + }, + bases=('auth.group',), + managers=[ + ('objects', django.contrib.auth.models.GroupManager()), + ], + ), + migrations.CreateModel( + name='CustomUser', + 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')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, 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')), + ('mobile', models.CharField(blank=True, help_text='Users mobile phone', max_length=15, null=True, unique=True)), + ('verification_code', models.CharField(blank=True, help_text='Verification code for bot account', max_length=10, null=True, unique=True)), + ('user_created', models.DateField(auto_now_add=True, help_text='Date when user has been created', verbose_name='User created')), + ('email', models.EmailField(help_text='User email', max_length=30, null=True, unique=True)), + ('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, + }, + ), + ] diff --git a/django_accounts_app/models.py b/django_accounts_app/models.py new file mode 100644 index 0000000..efbf67c --- /dev/null +++ b/django_accounts_app/models.py @@ -0,0 +1,26 @@ +from django.db import models +from django.contrib.auth.models import AbstractUser, Group +from .managers import CustomUserManager + + +class CustomUser(AbstractUser): + + mobile = models.CharField(max_length=15, null=True, blank=True, unique=True, + help_text='Users mobile phone') + verification_code = models.CharField(max_length=10, unique=True, null=True, blank=True, + help_text='Verification code for bot account') + user_created = models.DateField(editable=False, auto_now_add=True, verbose_name='User created', + help_text='Date when user has been created') + email = models.EmailField(max_length=30, unique=True, blank=False, null=True, help_text='User email') + USERNAME_FIELD = 'username' + REQUIRED_FIELDS = ['email'] + objects = CustomUserManager() + + def __str__(self): + return self.username + + +class CustomGroup(Group): + class Meta: + verbose_name = 'Permissions group' + verbose_name_plural = 'Permissions groups' diff --git a/django_accounts_app/tasks.py b/django_accounts_app/tasks.py new file mode 100644 index 0000000..15b57af --- /dev/null +++ b/django_accounts_app/tasks.py @@ -0,0 +1,16 @@ +from celery import shared_task +from django.core.mail import send_mail +from django.conf import settings +from django.template.loader import render_to_string + + +@shared_task +def mail_send(to_email: str, subject: str, text_content: str = '', **kwargs) -> None: + + username = kwargs.get('username') + from_email = settings.DEFAULT_FROM_EMAIL + + msg_html = render_to_string('registration/message.html', + {'username': username}) + + send_mail(subject, text_content, from_email, [to_email], html_message=msg_html, fail_silently=False) diff --git a/django_accounts_app/templates/base.html b/django_accounts_app/templates/base.html new file mode 100644 index 0000000..b7e6baf --- /dev/null +++ b/django_accounts_app/templates/base.html @@ -0,0 +1,3 @@ +{% block content %} + +{% endblock %} diff --git a/django_accounts_app/templates/registration/login.html b/django_accounts_app/templates/registration/login.html new file mode 100644 index 0000000..30152ca --- /dev/null +++ b/django_accounts_app/templates/registration/login.html @@ -0,0 +1,120 @@ +{% extends "base.html" %} + +{% block content %} + + + + +Bootstrap Simple Login Form with Blue Background + + + + + + +
+
+

Login

+

Please fill in this form to Log in

+
+ {% csrf_token %} +
+ +
+
+ +
+
+ +
+
+ {% if form.non_field_errors %} + {% for error in form.non_field_errors %} +
  • {{ error }}
  • + {% endfor %} + {% endif %} +
    +
    +
    Don't have an account? Register here
    +
    + +{% endblock %} diff --git a/django_accounts_app/templates/registration/message.html b/django_accounts_app/templates/registration/message.html new file mode 100644 index 0000000..14e0b6a --- /dev/null +++ b/django_accounts_app/templates/registration/message.html @@ -0,0 +1,6 @@ + + +
    +

    Добро пожаловать, {{ username }}!

    +
    + diff --git a/django_accounts_app/templates/registration/password_change_done.html b/django_accounts_app/templates/registration/password_change_done.html new file mode 100644 index 0000000..64d7695 --- /dev/null +++ b/django_accounts_app/templates/registration/password_change_done.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block content %} + +

    Password Changed successfully

    +Back to dashboard + +{% endblock %} diff --git a/django_accounts_app/templates/registration/password_change_form.html b/django_accounts_app/templates/registration/password_change_form.html new file mode 100644 index 0000000..6f3b1f6 --- /dev/null +++ b/django_accounts_app/templates/registration/password_change_form.html @@ -0,0 +1,13 @@ +{% extends "base.html" %} + +{% block content %} + +

    Password Change

    +
    + {% csrf_token %} + {{form.as_p}} + +
    +Back to dashboard + +{% endblock %} diff --git a/django_accounts_app/templates/users/dashboard.html b/django_accounts_app/templates/users/dashboard.html new file mode 100644 index 0000000..164543c --- /dev/null +++ b/django_accounts_app/templates/users/dashboard.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block content %} + + Hello {{ user.username|default:'guest' }}! + +
    + {% if user.is_authenticated %} + Logout + Change Password + Go to Profile page + {% else %} + Login + {% endif %} +
    + +{% endblock %} diff --git a/django_accounts_app/templates/users/register.html b/django_accounts_app/templates/users/register.html new file mode 100644 index 0000000..1708fdd --- /dev/null +++ b/django_accounts_app/templates/users/register.html @@ -0,0 +1,154 @@ +{% extends "base.html" %} + +{% block content %} + + + + +Bootstrap Simple Login Form with Blue Background + + + + + + +
    +
    +

    Sign Up

    +

    Please fill in this form to create an account!

    +
    + {% csrf_token %} +
    + {% if form.username.value %} + + {% else %} + + {% endif %} +
    {{ form.username.errors }}
    +
    +
    +
    + {% if form.first_name.value %} +
    + {% else %} +
    + {% endif %} + {% if form.last_name.value %} +
    + {% else %} +
    + {% endif %} +
    +
    +
    + {% if form.email.value %} + + {% else %} + + {% endif %} +
    {{ form.email.errors }}
    +
    +
    + {% if form.mobile.value %} + + {% else %} + + {% endif %} + +
    {{ form.mobile.errors }}
    +
    +
    + +
    +
    + +
    {{ form.password2.errors }}
    +
    + +
    + +
    +
    {{ form.non_field_errors }}
    +
    +
    Already have an account? Login here
    +
    +{% endblock content %} diff --git a/django_accounts_app/tests.py b/django_accounts_app/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/django_accounts_app/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/django_accounts_app/urls.py b/django_accounts_app/urls.py new file mode 100644 index 0000000..221021f --- /dev/null +++ b/django_accounts_app/urls.py @@ -0,0 +1,10 @@ +from django.urls import path, include +from .views import dashboard, RegisterUser + +app_name = 'accounts' + +urlpatterns = [ + path('', include("django.contrib.auth.urls")), + path('dashboard/', dashboard, name='dashboard'), + path('registration/', RegisterUser.as_view(), name='registration') +] diff --git a/django_accounts_app/views.py b/django_accounts_app/views.py new file mode 100644 index 0000000..2e7ce94 --- /dev/null +++ b/django_accounts_app/views.py @@ -0,0 +1,34 @@ +from django.forms import BaseModelForm +from django.shortcuts import render, redirect +from django.contrib.auth import login +from django.views.generic import CreateView +from django.http import HttpResponse +from server.apps.accounts.forms import CustomUserCreationForm +from django.core.validators import validate_email +from .tasks import mail_send + + +# Create your views here. +def dashboard(request): + return render(request, "users/dashboard.html", {}) + + +class RegisterUser(CreateView): + form_class = CustomUserCreationForm + template_name = 'users/register.html' + + def form_valid(self, form: BaseModelForm) -> HttpResponse: + user = form.save() + user.backend = 'django.contrib.auth.backends.ModelBackend' + + validate_email(form.instance.email) + email = form.instance.email + username = f'{form.instance.first_name} {form.instance.last_name}' + subject = 'Welcome to book bot administration' + mail_send(to_email=email, subject=subject, username=username) + + login(self.request, user) + + return redirect('admin:index') + +