diff --git a/accounts_ext/__init__.py b/accounts_ext/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/accounts_ext/admin.py b/accounts_ext/admin.py
new file mode 100644
index 0000000..45eb74e
--- /dev/null
+++ b/accounts_ext/admin.py
@@ -0,0 +1,26 @@
+from django.contrib import admin
+from .models import Profile
+
+# def delete_model(modeladmin, request, queryset):
+# for obj in queryset:
+# user_profile = obj.user.profile
+# if user_profile.user_points >= obj.points:
+# user_profile.user_points -= obj.points
+# user_profile.save()
+# obj.delete()
+#
+# delete_model.short_description = "Удалить как исполненный"
+
+# class PickUpRequestAdmin(admin.ModelAdmin):
+# list_display = [field.name for field in PickUpRequest._meta.fields]
+# actions = [delete_model]
+# class Meta:
+# model = PickUpRequest
+#
+# class UserProfileAdmin(admin.ModelAdmin):
+# list_display = [field.name for field in Profile._meta.fields]
+# class Meta:
+# model = Profile
+
+# admin.site.register(Profile, UserProfileAdmin)
+# admin.site.register(PickUpRequest, PickUpRequestAdmin)
diff --git a/accounts_ext/apps.py b/accounts_ext/apps.py
new file mode 100644
index 0000000..c708551
--- /dev/null
+++ b/accounts_ext/apps.py
@@ -0,0 +1,7 @@
+from django.apps import AppConfig
+from django.utils.translation import ugettext_lazy as _
+
+
+class AccountsExtConfig(AppConfig):
+ name = 'accounts_ext'
+ verbose_name = _("Authentication and Authorization")
diff --git a/accounts_ext/forms.py b/accounts_ext/forms.py
new file mode 100644
index 0000000..4bdc31d
--- /dev/null
+++ b/accounts_ext/forms.py
@@ -0,0 +1,298 @@
+import logging
+from captcha.fields import CaptchaField
+from crispy_forms.helper import FormHelper
+from crispy_forms.layout import Layout, Field, Div, Submit, HTML
+from django import forms
+from django.conf import settings
+from django.contrib.auth import get_user_model, password_validation
+from django.core.exceptions import ObjectDoesNotExist
+from django.forms import inlineformset_factory
+from django.urls import reverse, reverse_lazy
+from django.utils.translation import ugettext_lazy as _
+from registration.forms import RegistrationFormUniqueEmail
+from django.contrib.auth.forms import (
+ AuthenticationForm as AuthenticationFormBase,
+ PasswordResetForm as PasswordResetFormBase,
+ PasswordChangeForm as PasswordChangeFormBase
+)
+
+from core.models import STATUS_ACTIVE
+from .models import User, Profile, Company
+
+logger = logging.getLogger(__name__)
+
+
+# class PickUpPointsForm(layout.ModelForm):
+# class Meta:'
+# model = PickUpRequest
+# fields = ('requisites',)
+
+
+class RegistrationForm(RegistrationFormUniqueEmail):
+ email = forms.EmailField(label=_("E-mail"), widget=forms.EmailInput(attrs={'class': 'reg__text'}))
+ password1 = forms.CharField(
+ label=_('Пароль'),
+ strip=False,
+ widget=forms.PasswordInput(attrs={'class': 'reg__text-label'}),
+ help_text=password_validation.password_validators_help_text_html(),
+ )
+ password2 = forms.CharField(
+ label=_('Подтверждение пароля'),
+ widget=forms.PasswordInput(attrs={'class': 'reg__text-label'}),
+ strip=False,
+ help_text=_('Для потверждения пароля введите его еще раз'),
+ )
+ first_name = forms.CharField(label=_("Имя"), max_length=255, widget=forms.TextInput(attrs={'class': 'reg__text'}))
+ last_name = forms.CharField(label=_("Фамилия"), max_length=255,
+ widget=forms.TextInput(attrs={'class': 'reg__text'}))
+ patronymic = forms.CharField(label=_("Отчество"), max_length=255,
+ widget=forms.TextInput(attrs={'class': 'reg__text'}))
+ agreement = forms.BooleanField()
+
+ captcha = CaptchaField(required=not settings.DEBUG)
+
+ title = _('Регистрация')
+
+ def save(self, commit=True):
+ user = super().save(commit)
+ profile = Profile.objects.filter(user=user).first()
+ if profile:
+ profile.first_name = self.cleaned_data.get('first_name')
+ profile.last_name = self.cleaned_data.get('last_name')
+ profile.patronymic = self.cleaned_data.get('patronymic')
+ profile.save()
+ else:
+ logger.error(
+ msg="User {user_email} missing profile object".format({'user_email': user.email})
+ )
+ return user
+
+ class Meta:
+ model = User
+ fields = ('username',)
+ labels = {
+ 'username': _('Логин'),
+ }
+ help_texts = {
+ 'username': _('Не менее 5х символов'),
+ }
+ widgets = {
+ 'username': forms.TextInput(attrs={'class': 'reg__text-label'}),
+ }
+
+
+class RegistrationCompanyForm(forms.ModelForm):
+ is_individual = forms.BooleanField(label=_('Я физическое лицо'), required=False)
+
+ def save(self, user, commit=True):
+ self.instance.user = user
+ return super().save(commit)
+
+ class Meta:
+ model = Company
+ fields = ('company_name', 'address', 'inn', 'ogrn')
+ labels = {
+ 'company_name': _('Наименование'),
+ 'inn': _('ИНН'),
+ 'ogrn': _('ОГРН'),
+ 'address': _('Юридический адрес')
+ }
+ widgets = {
+ 'company_name': forms.TextInput(attrs={'class': 'reg__text'}),
+ 'inn': forms.TextInput(attrs={'class': 'reg__text'}),
+ 'ogrn': forms.TextInput(attrs={'class': 'reg__text'}),
+ 'address': forms.Textarea(attrs={'class': 'reg__text', 'rows': 5, 'cols': 40})
+ }
+
+
+class AuthenticationForm(AuthenticationFormBase):
+ username = forms.CharField(max_length=255, widget=forms.TextInput(attrs={
+ 'class': 'reg__text',
+ 'style': 'text-align:center',
+ 'placeholder': _('Логин')
+ }))
+ password = forms.CharField(max_length=255, widget=forms.PasswordInput(attrs={
+ 'class': 'reg__text',
+ 'style': 'text-align:center',
+ 'placeholder': _('Пароль')
+ }))
+
+ field_template = 'bootstrap/forms/authentication.html'
+ title = _('Вход')
+
+ def __init__(self, *args, **kwargs):
+ self.helper = FormHelper()
+ self.helper.layout = Layout(
+ Field('username', template=self.field_template),
+ Field('password', template=self.field_template),
+ Div(
+ HTML(''.format(
+ reverse_lazy('accounts_ext:reset_password'),
+ _('Забыли пароль ?'))
+ ),
+ css_class="text-right"
+ ),
+ Div(
+ Div(
+ Submit('submit', _('Войти'), css_class='btn-danger'),
+ css_class='col-lg-12 text-center'
+ ),
+ css_class='row'
+ ),
+ )
+ super().__init__(*args, **kwargs)
+
+
+class PasswordResetRequestForm(PasswordResetFormBase):
+ field_template = 'bootstrap/forms/authentication.html'
+
+ email_slug = 'reset_password'
+ title = _('Сброс пароля')
+
+ def __init__(self, *args, **kwargs):
+ self.helper = FormHelper()
+ self.helper.form_action = reverse_lazy('accounts_ext:reset_password')
+ self.helper.layout = Layout(
+ Field('email', css_class='reg__text', template=self.field_template, placeholder="example@email.com"),
+ Div(
+ Div(
+ Submit('submit', _('Сбросить'), css_class='btn-danger'),
+ css_class='col-lg-12 text-center'
+ ),
+ css_class="row"
+ ),
+ )
+ super().__init__(*args, **kwargs)
+
+ def get_users(self, email):
+ active_users = get_user_model()._default_manager.filter(**{
+ '%s__iexact' % get_user_model().get_email_field_name(): email,
+ 'status': STATUS_ACTIVE,
+ })
+ return (u for u in active_users if u.has_usable_password())
+
+ def send_mail(self, subject_template_name, email_template_name, context, from_email, to_email,
+ html_email_template_name=None):
+ try:
+ # mail_template = MailTemplate.objects.filter(slug=self.email_slug).first()
+ kwargs = {
+ 'subject': subject_template_name,
+ # 'message': get_template(html_email_template_name).templatetags.source
+ }
+ if not kwargs:
+ kwargs = {
+ 'slug': self.email_slug,
+ 'name': email_template_name,
+ 'num_of_retries': 3,
+ 'is_html': True
+ }
+ # MailTemplate.objects.create(**kwargs)
+ else:
+ pass
+ # mail_template.subject = kwargs.get('subject', mail_template.subject)
+ # mail_template.message = kwargs.get('message', mail_template.message)
+ # mail_template.save()
+ user = User.active.filter(email__iexact=to_email).first()
+ first_name = user.profile.first_name or ""
+ last_name = user.profile.last_name or ""
+ full_name = first_name + ' ' + last_name
+ if len(full_name.strip()) != 0:
+ context.update({'username': full_name})
+ # send_db_mail(
+ # self.email_slug,
+ # to_email,
+ # subject_template_name,
+ # context,
+ # from_email=from_email,
+ # retry=True,
+ # retry_delay=300,
+ # max_retries=3,
+ # send_at_date=datetime.now(),
+ # use_celery=celery_supported()
+ # )
+ except ObjectDoesNotExist as e:
+ logger.critical(e)
+
+ def clean(self):
+ return super().clean()
+
+
+class PasswordResetForm(PasswordChangeFormBase):
+ field_template = 'bootstrap/field.html'
+
+ field_order = ['new_password1', 'new_password2']
+
+ title = _('Сброс пароля')
+
+ def __init__(self, *args, **kwargs):
+ self.request = kwargs.pop('request')
+
+ self.helper = FormHelper()
+ self.helper.form_action = self.request.path
+ self.helper.layout = Layout(
+ Field('new_password1', template=self.field_template),
+ Field('new_password2', template=self.field_template),
+ Div(
+ Div(
+ Submit('submit', _('Сброс'), css_class='btn-danger'),
+ css_class='col-lg-12 text-center'
+ ),
+ css_class="row"
+ ),
+ )
+
+ super().__init__(kwargs.pop('user'), *args, **kwargs)
+ self.fields['new_password1'].help_text = self.fields['new_password1'].help_text.replace('
',
+ '')
+
+ def is_valid(self):
+ valid = super().is_valid()
+ if not valid:
+ if 'old_password' in self.errors:
+ self.errors.pop('old_password')
+ valid = super().is_valid()
+ return valid
+
+
+class ProfileForm(forms.Form):
+ email = forms.CharField(required=False)
+ field_template = 'bootstrap/field_admin.html'
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ self.helper = FormHelper()
+ self.helper.form_action = reverse('accounts_ext:update')
+ self.helper.form_class = 'formajax form-replace-block'
+ self.helper.layout = Layout(
+ Div(
+ Field('email', template=self.field_template),
+ Field('first_name', template=self.field_template),
+ Field('last_name', template=self.field_template),
+ Field('patronymic', template=self.field_template),
+ Field('birthday', template=self.field_template),
+ Field('company_name', template=self.field_template),
+ Field('inn', template=self.field_template),
+ Field('ogrn', template=self.field_template),
+ Field('legal_address', template=self.field_template),
+ css_class="setProfile"
+ ),
+ Div(
+ Submit('submit', _('Save'), css_class='button button--bigOrange'),
+ css_class="settings-button"
+ ),
+ )
+ self.initial['email'] = self.instance.user.email
+ self.fields['email'].widget.attrs['readonly'] = True
+ self.fields['birthday'].widget.attrs['class'] = 'date inputText'
+
+ def clean_email(self):
+ instance = getattr(self, 'instance', None)
+ if instance and instance.pk:
+ return instance.user.email
+ else:
+ return self.cleaned_data['email']
+
+ class Meta:
+ model = Profile
+ exclude = ('user', 'create_at', 'updated_at', 'status',)
diff --git a/accounts_ext/models.py b/accounts_ext/models.py
new file mode 100644
index 0000000..b0582d9
--- /dev/null
+++ b/accounts_ext/models.py
@@ -0,0 +1,172 @@
+from django.contrib.auth import get_user_model
+from django.contrib.auth.models import (
+ Group as GroupBase, AbstractBaseUser, BaseUserManager, PermissionsMixin
+)
+from django.core.exceptions import ObjectDoesNotExist
+from django.core.validators import RegexValidator
+from django.db import models, transaction
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+from django.utils.translation import ugettext_lazy as _
+from django.utils.timezone import now as datetime_now
+
+from registration.signals import user_activated
+
+from core.models import AbstractStatusModel, ActualOnlyManager, STATUS_ACTIVE
+
+
+# Create your models here.
+
+class CaseInsensitiveQuerySet(models.QuerySet):
+ CASE_INSENSITIVE_FIELDS = ('email',)
+
+ def _filter_or_exclude(self, negate, *args, **kwargs):
+ for field in self.CASE_INSENSITIVE_FIELDS:
+ if field in kwargs:
+ kwargs[field + '__iexact'] = kwargs[field]
+ del kwargs[field]
+ return super()._filter_or_exclude(negate, *args, **kwargs)
+
+
+class UserManager(ActualOnlyManager, BaseUserManager):
+ def create_superuser(self, email, password):
+ user = self.model(
+ email=email,
+ is_superuser=True,
+ status=STATUS_ACTIVE,
+ )
+ user.set_password(password)
+ user.save(using=self._db)
+ return user
+
+ def get_queryset(self):
+ return CaseInsensitiveQuerySet(self.model)
+
+
+class User(AbstractStatusModel, PermissionsMixin, AbstractBaseUser):
+ username = models.CharField(_('username'),max_length=255, blank=False, null=False)
+ email = models.EmailField(
+ _('email'),
+ unique=True,
+ error_messages={
+ 'unique': _("A user with that email already exists."),
+ },
+ )
+ # referral = models.ForeignKey('referral.Referral',
+ # verbose_name=_('Referral code'),
+ # on_delete=models.DO_NOTHING,
+ # blank=True, null=True,
+ # related_name='ref_code')
+ referral_user = models.ForeignKey('self',
+ verbose_name=_('Referral user'),
+ on_delete=models.DO_NOTHING,
+ blank=True, null=True,
+ related_name='ref_user')
+
+ confirmed_at = models.DateTimeField(_('email confirmed at'), blank=True, null=True)
+
+ USERNAME_FIELD = 'email'
+
+ objects = UserManager()
+
+ @property
+ def is_staff(self):
+ return self.is_superuser
+
+ @property
+ def date_joined(self):
+ return self.create_at
+
+ @date_joined.setter
+ def date_joined(self, value):
+ self.create_at = value
+
+ def get_short_name(self):
+ return self.email
+
+
+class Group(GroupBase):
+ class Meta:
+ proxy = True
+ verbose_name = GroupBase._meta.verbose_name
+ verbose_name_plural = GroupBase._meta.verbose_name_plural
+
+
+class Profile(AbstractStatusModel):
+ user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
+ first_name = models.CharField(_('first name'), max_length=100, blank=True, null=True)
+ last_name = models.CharField(_('last name'), max_length=100, blank=True, null=True)
+ patronymic = models.CharField(_('patronymic'), max_length=100, blank=True, null=True)
+ birthday = models.DateField(_('birthday'), null=True, blank=True)
+
+ phone_regex = RegexValidator(regex=r'^\((+7)|8)?\d{10}$',
+ message="Phone number must be entered in the format: '+99999999999'. Up to 12 digits allowed.")
+ phone = models.CharField(validators=[phone_regex], max_length=12, blank=True, null=True)
+
+ @property
+ def is_valid(self):
+ return self.first_name and self.last_name and self.patronymic
+
+ @property
+ def is_complete(self):
+ return self.first_name and self.last_name and self.patronymic and self.birthday
+
+ class Meta:
+ verbose_name = _('profile')
+ verbose_name_plural = _('profiles')
+
+ def __str__(self):
+ return str(self.user)
+
+
+COMPANY_STATUS_LEGAL = 25
+COMPANY_STATUS_INDIVIDUAL = 50
+COMPANY_STATUS_LIST = (
+ (COMPANY_STATUS_INDIVIDUAL, _("Legal")),
+ (COMPANY_STATUS_INDIVIDUAL, _("Individual"))
+)
+
+
+class Company(AbstractStatusModel):
+ user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
+ company_name = models.CharField(max_length=255, blank=True, null=True)
+ address = models.TextField(blank=True,null=True)
+ inn = models.CharField(max_length=12, blank=True, null=True)
+ ogrn = models.CharField(max_length=13, blank=True, null=True)
+ status = models.SmallIntegerField(choices=COMPANY_STATUS_LIST, blank=True, null=True)
+
+ @property
+ def is_individual(self):
+ return self.status == COMPANY_STATUS_INDIVIDUAL
+
+ @property
+ def is_legal(self):
+ return self.status == COMPANY_STATUS_LEGAL
+
+ def __str__(self):
+ return self.company_name
+
+ class Meta:
+ verbose_name = _('company')
+ verbose_name_plural = _('companies')
+
+
+@receiver(post_save, sender=get_user_model())
+def create_user_profile(sender, instance, created, **kwargs):
+ if created:
+ Profile.objects.create(user=instance)
+
+
+@receiver(post_save, sender=get_user_model())
+def save_user_profile(sender, instance, **kwargs):
+ try:
+ instance.profile.save()
+ except ObjectDoesNotExist:
+ Profile.objects.create(user=instance)
+
+
+@receiver(user_activated)
+def save_activation_date(sender, user, request, **kwargs):
+ user.confirmed_at = datetime_now()
+ user.is_active = user.status == STATUS_ACTIVE
+ user.save()
diff --git a/accounts_ext/templatetags/__init__.py b/accounts_ext/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/accounts_ext/templatetags/accounts_ext_filters.py b/accounts_ext/templatetags/accounts_ext_filters.py
new file mode 100644
index 0000000..24281a4
--- /dev/null
+++ b/accounts_ext/templatetags/accounts_ext_filters.py
@@ -0,0 +1,7 @@
+from django.template import Library
+
+register = Library()
+
+@register.filter(name='get_by_key')
+def get_by_key(dict, key):
+ return getattr(dict,key,None)
diff --git a/accounts_ext/tests.py b/accounts_ext/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/accounts_ext/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/accounts_ext/urls.py b/accounts_ext/urls.py
new file mode 100644
index 0000000..7c49521
--- /dev/null
+++ b/accounts_ext/urls.py
@@ -0,0 +1,14 @@
+from django.urls import re_path
+from . import views
+
+
+urlpatterns = [
+ re_path(r'^login/$', views.LoginView.as_view(), name='login'),
+ re_path(r'^logout/$', views.LogoutView.as_view(), name='logout'),
+ re_path(r'^register/$', views.RegistrationView.as_view(), name='register'),
+ re_path(r'^password/reset/$', views.ResetPasswordView.as_view(), name='reset_password'),
+ re_path(r'^password/reset/done/$', views.ResetPasswordDoneView.as_view(), name='reset_password_done'),
+ re_path(r'^password/change/$', views.ChangePasswordForm.as_view(), name='change_password'),
+ re_path(r'^password/change/done/$', views.PasswordChangeViewBase.as_view(), name='change_password_done'),
+ re_path(r'^password/reset/complete/$', views.ResetPasswordCompleteView.as_view(), name='reset_password_complete')
+]
diff --git a/accounts_ext/utils.py b/accounts_ext/utils.py
new file mode 100644
index 0000000..7637f27
--- /dev/null
+++ b/accounts_ext/utils.py
@@ -0,0 +1,20 @@
+
+def get_ballances(user):
+ res = {}
+ if user:
+ for wallet in user.wallet_set.all():
+ info = '%.8f' % wallet.amount
+ if wallet.amount_blocked:
+ info += ' (%.8f)' % wallet.amount_blocked
+ res[wallet.currency.iso] = info
+ return res
+
+
+def get_ballances_decimal(user):
+ res = {}
+ if user:
+ for wallet in user.wallet_set.all():
+ info = wallet.amount
+ blocked = wallet.amount_blocked
+ res[wallet.currency.iso] = (info, blocked)
+ return res
diff --git a/accounts_ext/views.py b/accounts_ext/views.py
new file mode 100644
index 0000000..6e41535
--- /dev/null
+++ b/accounts_ext/views.py
@@ -0,0 +1,203 @@
+import logging
+from django.conf import settings
+from django.contrib.auth.views import (
+ LoginView as LoginViewBase,
+ LogoutView,
+ PasswordResetView as PasswordResetViewBase,
+ PasswordResetDoneView as PasswordResetDoneViewBase,
+ PasswordResetConfirmView as PasswordResetConfirmViewBase,
+ PasswordResetCompleteView as PasswordResetCompleteViewBase,
+ PasswordChangeView as PasswordChangeViewBase,
+ PasswordChangeDoneView as PasswordChangeDoneViewBase
+)
+from django.views.generic import TemplateView
+
+from registration.backends.default.views import (
+ RegistrationView as RegistrationViewBase,
+ ResendActivationView as ResendActivationViewBase
+)
+from django.contrib.messages.views import SuccessMessageMixin
+from django.http import JsonResponse
+from django.shortcuts import redirect, resolve_url
+from django.urls import reverse, reverse_lazy
+from django.utils.http import is_safe_url
+from django.utils.translation import ugettext_lazy as _
+
+from core.views import ProtectedView
+from .forms import (
+ PasswordResetRequestForm, PasswordResetForm, RegistrationCompanyForm,
+ AuthenticationForm,
+ RegistrationForm)
+
+logger = logging.getLogger(__name__)
+
+
+# Create your views here.
+
+class LoginView(LoginViewBase):
+ redirect_field_name = 'next'
+ form_class = AuthenticationForm
+ redirect_authenticated_user = True
+ template_name = 'accounts_ext/authentication.html'
+
+ def dispatch(self, request, *args, **kwargs):
+ if self.request.user.is_authenticated:
+ redirect_to = self.request.POST.get(
+ self.redirect_field_name,
+ self.request.GET.get(self.redirect_field_name, '')
+ )
+ if not is_safe_url(url=redirect_to, host=self.request.get_host()):
+ redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)
+ if redirect_to == self.request.path:
+ raise ValueError(
+ "Redirection loop for authenticated user detected. Check that "
+ "your LOGIN_REDIRECT_URL doesn't point to a login page."
+ )
+ return redirect(redirect_to)
+ return super().dispatch(request, *args, **kwargs)
+
+ def render_done(self, form, **kwargs):
+ """
+ This method gets called when all forms passed. The method should also
+ re-validate all steps to prevent manipulation. If any form don't
+ validate, `render_revalidation_failure` should get called.
+ If everything is fine call `done`.
+ """
+ final_form_list = []
+ # walk through the form list and try to validate the data again.
+ for form_key in self.get_form_list():
+ form_obj = self.get_form(step=form_key,
+ data=self.storage.get_step_data(form_key),
+ files=self.storage.get_step_files(
+ form_key))
+ form_obj.exclude_second_captcha_validation = True
+ if not (form_key in self.idempotent_dict or form_obj.is_valid()):
+ return self.render_revalidation_failure(form_key, form_obj,
+ **kwargs)
+ final_form_list.append(form_obj)
+
+ # render the done view and reset the wizard before returning the
+ # response. This is needed to prevent from rendering done with the
+ # same data twice.
+ done_response = self.done(final_form_list, **kwargs)
+ self.storage.reset()
+ return done_response
+
+ def get_user(self):
+ """
+ Returns the user authenticated by the AuthenticationForm. Returns False
+ if not a valid user; see also issue #65.
+ """
+ if not self.user_cache:
+ form_obj = self.get_form(step='auth',
+ data=self.storage.get_step_data('auth'))
+ form_obj.exclude_second_captcha_validation = True
+ self.user_cache = form_obj.is_valid() and form_obj.user_cache
+ return self.user_cache
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['title'] = AuthenticationForm.title
+ return context
+
+class ResetPasswordView(PasswordResetViewBase):
+ form_class = PasswordResetRequestForm
+ template_name = 'accounts_ext/reset_password.html'
+ email_template_name = 'Reset password email'
+ html_email_template_name = 'emails/reset_password_email.html'
+ success_url = reverse_lazy('accounts_ext:reset_password_done')
+ from_email = settings.DEFAULT_FROM_EMAIL
+ subject_template_name = _('Сброс пароля')
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['title'] = PasswordResetForm.title
+ return context
+
+
+class ResetPasswordDoneView(PasswordResetDoneViewBase):
+ template_name = 'accounts_ext/reset_password_done.html'
+
+
+class ResetPasswordConfirmView(PasswordResetConfirmViewBase):
+ form_class = PasswordResetForm
+ template_name = 'accounts_ext/reset_password_confirm.html'
+ success_url = reverse_lazy('accounts_ext:reset_password_complete')
+
+ def get_form_kwargs(self):
+ kwargs = super().get_form_kwargs()
+ kwargs['request'] = self.request
+ return kwargs
+
+
+class ResetPasswordCompleteView(PasswordResetCompleteViewBase):
+ template_name = 'accounts_ext/reset_password_complete.html'
+ title = _('Password reset complete')
+
+
+class RegistrationView(RegistrationViewBase):
+ http_method_names = ['get', 'post']
+ template_name = 'registration/registration.html'
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ if self.request.POST:
+ context['company_form'] = RegistrationCompanyForm(self.request.POST)
+ else:
+ context['company_form'] = RegistrationCompanyForm()
+ context['title'] = RegistrationForm.title
+ return context
+
+ def form_valid(self, form):
+ context = self.get_context_data()
+ company_form = context.get('company_form')
+ try:
+ user = form.save()
+ if company_form.is_valid():
+ company_form.save(user=user)
+ else:
+ logger.error("User {email} company has invalid data during registration")
+ except Exception as e:
+ logger.error(e)
+ return self.form_invalid(form=form)
+ return super().form_valid(form)
+
+
+class RegistrationDoneView(TemplateView):
+ template_name = 'registration/registration_done.html'
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['title'] = _('Отправка письма активации')
+ return context
+
+
+class ResendActivationView(ResendActivationViewBase):
+ template_name = 'registration/resend_activation.tml'
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['title'] = _('Отправка письма активации')
+ return context
+
+
+class ChangePasswordForm(PasswordChangeViewBase):
+ template_name = 'accounts_ext/password_reset_change.html'
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['title'] = _('Введение нового пароля')
+ return context
+
+
+class ChangePasswordDoneForm(PasswordChangeDoneViewBase):
+ template_name = 'accounts_ext/password_reset_change_done.html'
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['title'] = _('Сброс пароля')
+ return context
+
+
+class ProfileView(ProtectedView):
+ template_name = 'accounts_ext/index.html'