Added email verification

This commit is contained in:
ayxan 2022-09-10 01:35:48 +04:00
parent e513c2f6df
commit a7f615b336
10 changed files with 130 additions and 27 deletions

View File

@ -5,7 +5,7 @@ https://ayxan.pythonanywhere.com
**pip install -r requirements.txt<br /> **pip install -r requirements.txt<br />
cd src/<br />** cd src/<br />**
add SECRET_KEY to .env.example file<br /> add SECRET_KEY, EMAIL_HOST_PASSWORD to .env.example file<br />
rename .env.example to .env<br /> rename .env.example to .env<br />
**python3 manage.py makemigrations account --settings=config.settings.development<br /> **python3 manage.py makemigrations account --settings=config.settings.development<br />

View File

@ -2,3 +2,4 @@ SECRET_KEY=
DB_NAME= DB_NAME=
DB_USER= DB_USER=
DB_PASSWORD= DB_PASSWORD=
EMAIL_HOST_PASSWORD=

View File

@ -5,9 +5,21 @@ from .models import User
@admin.register(User) @admin.register(User)
class CustomAdmin(UserAdmin): class CustomAdmin(UserAdmin):
list_display = ('username', 'email') fieldsets = (
fieldsets = UserAdmin.fieldsets + ( (None, {'fields': ('username', 'email', 'password')}),
('IMDB API Key Field', { (('Permissions'), {
'fields': ['imdb_api_key'] 'fields': ('is_active', 'email_notification_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'),
}), }),
) )
list_display = ('username', 'email', 'is_staff')
list_filter = ('is_staff', 'email_notification_is_active', 'is_superuser', 'is_active', 'groups')
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ('groups', 'user_permissions',)

View File

@ -3,7 +3,11 @@ from django.contrib.auth.models import AbstractUser
class User(AbstractUser): class User(AbstractUser):
first_name = None
last_name = None
email = models.EmailField('email', unique=True, blank=True, null=True)
imdb_api_key = models.CharField(max_length=15, blank=False, null=False) imdb_api_key = models.CharField(max_length=15, blank=False, null=False)
email_notification_is_active = models.BooleanField(default=False)
class Meta: class Meta:
db_table = 'user' db_table = 'user'

View File

@ -0,0 +1,6 @@
{% autoescape off %}
Hi {{ user.username }},
Please click on the link to confirm your registration,
http://{{ domain }}{% url 'activate' uidb64=uid token=token %}
If you think, it's not you, then just ignore this email.
{% endautoescape %}

View File

@ -8,8 +8,9 @@ urlpatterns = [
template_name = 'login.html' template_name = 'login.html'
), name='login'), ), name='login'),
path('register', views.register_view, name='register'), path('register', views.register_view, name='register'),
path('activate/<uidb64>/<token>/', views.activate_view, name='activate'),
path('logout', views.logout_view, name='logout'), path('logout', views.logout_view, name='logout'),
path('change-password', views.change_password_view, name='change-password'), path('change-password', views.change_password_view, name='change-password'),
path('profile-editing', views.profile_editing_view, name='profile-editing'), path('profile-editing', views.profile_editing_view, name='profile-editing'),
path('profile', views.ProfileDetailView.as_view(), name='profile') path('profile', views.ProfileDetailView.as_view(), name='profile'),
] ]

View File

@ -1,4 +1,5 @@
from .register import register_view from .register import (register_view,
activate_view)
from .logout import logout_view from .logout import logout_view
from .change_password import change_password_view from .change_password import change_password_view
from .profile_editing import profile_editing_view from .profile_editing import profile_editing_view

View File

@ -1,8 +1,15 @@
from django.shortcuts import render, redirect from django.shortcuts import (render,
redirect)
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib import messages from django.contrib import messages
from account.forms import ProfileEditingForm from account.forms import ProfileEditingForm
from requests import get from requests import get
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.core.mail import EmailMessage
@login_required(login_url='/') @login_required(login_url='/')
@ -10,25 +17,46 @@ def profile_editing_view(request):
if request.method == 'POST': if request.method == 'POST':
form = ProfileEditingForm(request.POST, instance=request.user) form = ProfileEditingForm(request.POST, instance=request.user)
if form.is_valid(): if form.is_valid():
raw_data = get(f"https://imdb-api.com/en/API/Title/{request.POST['imdb_api_key']}/tt0110413") if 'imdb_api_key' in form.changed_data:
raw_data = get(f"https://imdb-api.com/en/API/Title/{request.POST['imdb_api_key']}/tt0110413")
if raw_data.status_code != 200: if raw_data.status_code != 200:
messages.info(request, 'Account not created. Please try again later') messages.info(request, 'Account not created. Please try again later')
return redirect('profile-editing') return redirect('profile-editing')
data = raw_data.json() data = raw_data.json()
if data['errorMessage']: if data['errorMessage']:
if 'Maximum usage' in data['errorMessage']: if 'Maximum usage' in data['errorMessage']:
messages.info(request, f"IMDB API: {data['errorMessage']}")
return redirect('profile-editing')
elif data['errorMessage'] == 'Invalid API Key':
form.add_error('imdb_api_key', 'Invalid API Key')
return render(request, 'profile_editing.html', context={"form": form})
messages.info(request, f"IMDB API: {data['errorMessage']}") messages.info(request, f"IMDB API: {data['errorMessage']}")
return redirect('profile-editing') return redirect('profile-editing')
elif data['errorMessage'] == 'Invalid API Key':
form.add_error('imdb_api_key', 'Invalid API Key')
return render(request, 'profile_editing.html', context={"form": form})
messages.info(request, f"IMDB API: {data['errorMessage']}")
return redirect('profile-editing')
form.save() form.save()
if 'email' in form.changed_data:
user = request.user
user.email_notification_is_active = False
user.save()
to_email = form.cleaned_data.get('email')
current_site = get_current_site(request)
mail_subject = 'Activate your account.'
message = render_to_string('acc_active_email.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': default_token_generator.make_token(user),
})
email = EmailMessage(
mail_subject, message, to=[to_email]
)
email.send()
messages.warning(request, "Please confirm your email address to receive notifications of new episodes.")
return redirect('homepage')
messages.success(request, 'Profile Updated') messages.success(request, 'Profile Updated')
return redirect('homepage') return redirect('homepage')

View File

@ -1,8 +1,18 @@
from django.shortcuts import render, redirect from django.shortcuts import (render,
redirect)
from django.contrib import messages from django.contrib import messages
from account.forms import RegisterForm from account.forms import RegisterForm
from django.contrib.auth import login, authenticate from django.contrib.auth import (login,
authenticate)
from requests import get from requests import get
from account.models import User
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMessage
from django.shortcuts import render
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
def register_view(request): def register_view(request):
@ -27,11 +37,27 @@ def register_view(request):
messages.info(request, f"IMDB API: {data['errorMessage']}") messages.info(request, f"IMDB API: {data['errorMessage']}")
return redirect('register') return redirect('register')
to_email = form.cleaned_data.get('email')
form.save() form.save()
username = form.cleaned_data.get('username') username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password1') password = form.cleaned_data.get('password1')
user = authenticate(username=username, password=password) user = authenticate(username=username, password=password)
login(request, user) login(request, user)
if to_email is not None:
current_site = get_current_site(request)
mail_subject = 'Activate your account.'
message = render_to_string('acc_active_email.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': default_token_generator.make_token(user),
})
email = EmailMessage(
mail_subject, message, to=[to_email]
)
email.send()
messages.warning(request, "Please confirm your email address to receive notifications of new episodes.")
return redirect('homepage')
messages.success(request, 'Registration Successful') messages.success(request, 'Registration Successful')
return redirect('homepage') return redirect('homepage')
@ -40,3 +66,19 @@ def register_view(request):
form = RegisterForm() form = RegisterForm()
return render(request, 'register.html', context={"form": form}) return render(request, 'register.html', context={"form": form})
def activate_view(request, uidb64, token):
try:
uid = urlsafe_base64_decode(uidb64).decode()
user = User._default_manager.get(pk=uid)
except(TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user is not None and default_token_generator.check_token(user, token):
user.email_notification_is_active = True
user.save()
messages.success(request, "Thank you for your email confirmation. You will receive notifications of new episodes. ")
return redirect('homepage')
else:
messages.info(request, "Activation link is invalid!")
return redirect('register')

View File

@ -82,3 +82,11 @@ USE_I18N = True
USE_TZ = True USE_TZ = True
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
EMAIL_BACKEND = 'django_smtp_ssl.SSLEmailBackend'
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'series.notification@gmail.com'
EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD")
DEFAULT_FROM_EMAIL = 'series.notification@gmail.com'
EMAIL_PORT = 465