From 5938bbf5d6510dcb51096a7e943a2e2208d06b7b Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 7 Apr 2017 15:52:53 +0530 Subject: Change in forms, models, urls, views - Add is_email_expired, activation_key, key_expiry_time in Profile model - Add new functions activate_user and new_activation in views for verifying and activating user via email - Change user_login and user_register functions in views for email verification - Change in forms.py to verify email during registration - Add activation urls in urls.py --- yaksh/forms.py | 24 +++++++++++++--- yaksh/models.py | 3 ++ yaksh/urls.py | 2 ++ yaksh/views.py | 86 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 98 insertions(+), 17 deletions(-) diff --git a/yaksh/forms.py b/yaksh/forms.py index f7f7a10..ac4e1b0 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -5,7 +5,7 @@ from yaksh.models import get_model_class, Profile, Quiz, Question, TestCase, Cou from django.contrib.auth import authenticate from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType - +from django.conf import settings from taggit.managers import TaggableManager from taggit.forms import TagField from django.forms.models import inlineformset_factory @@ -18,6 +18,7 @@ except ImportError: from string import punctuation, digits import datetime import pytz +from .email_verification import generate_activation_key languages = ( ("select", "Select Language"), @@ -117,6 +118,12 @@ class UserRegisterForm(forms.Form): return c_pwd + def clean_email(self): + user_email = self.cleaned_data['email'] + if User.objects.filter(email=user_email).exists(): + raise forms.ValidationError("This email already exists") + return user_email + def save(self): u_name = self.cleaned_data["username"] u_name = u_name.lower() @@ -135,9 +142,19 @@ class UserRegisterForm(forms.Form): new_profile.department = cleaned_data["department"] new_profile.position = cleaned_data["position"] new_profile.timezone = cleaned_data["timezone"] + if settings.IS_DEVELOPMENT: + new_profile.is_email_verified = True + new_profile.save() + return u_name, pwd + else: + new_profile.activation_key = generate_activation_key(new_user.username) + new_profile.key_expiry_time = datetime.datetime.strftime( + datetime.datetime.now() + \ + datetime.timedelta(minutes=20), + "%Y-%m-%d %H:%M:%S" + ) new_profile.save() - - return u_name, pwd + return new_user.email, new_profile.activation_key class UserLoginForm(forms.Form): @@ -307,4 +324,3 @@ class QuestionPaperForm(forms.ModelForm): class Meta: model = QuestionPaper fields = ['shuffle_questions'] - diff --git a/yaksh/models.py b/yaksh/models.py index 802a1fc..3e80570 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -251,6 +251,9 @@ class Profile(models.Model): default=pytz.utc.zone, choices=[(tz, tz) for tz in pytz.common_timezones] ) + is_email_verified = models.BooleanField(default=False) + activation_key = models.CharField(max_length=40, blank=True, null=True) + key_expiry_time = models.DateTimeField(blank=True, null=True) def get_user_dir(self): """Return the output directory for the user.""" diff --git a/yaksh/urls.py b/yaksh/urls.py index 00b34e4..03e5354 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -5,6 +5,8 @@ urlpatterns = [ url(r'^$', views.index), url(r'^login/$', views.user_login, name='login'), url(r'^logout/$', views.user_logout), + url(r'^activate/(?P.+)$', views.activate_user), + url(r'^new_activation/$', views.new_activation), url(r'^quizzes/$', views.quizlist_user, name='quizlist_user'), url(r'^quizzes/(?P\w+)/$', views.quizlist_user, name='quizlist_user'), url(r'^results/$', views.results_user), diff --git a/yaksh/views.py b/yaksh/views.py index db7498c..aa23d01 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -1,7 +1,7 @@ import random import string import os -from datetime import datetime +from datetime import datetime, timedelta import collections import csv from django.http import HttpResponse @@ -17,11 +17,13 @@ from django.contrib.auth.models import Group from django.forms.models import inlineformset_factory from django.utils import timezone from django.core.exceptions import MultipleObjectsReturned +from django.conf import settings import pytz from taggit.models import Tag from itertools import chain import json import six +from textwrap import dedent # Local imports. from yaksh.models import get_model_class, Quiz, Question, QuestionPaper, QuestionSet, Course from yaksh.models import Profile, Answer, AnswerPaper, User, TestCase, FileUpload,\ @@ -35,6 +37,7 @@ from yaksh.forms import UserRegisterForm, UserLoginForm, QuizForm,\ from .settings import URL_ROOT from yaksh.models import AssignmentUpload from .file_utils import extract_files +from .email_verification import send_user_mail, generate_activation_key @@ -73,7 +76,7 @@ def index(request): """ user = request.user if user.is_authenticated(): - if user.groups.filter(name='moderator').count() > 0: + if is_moderator(user): return my_redirect('/exam/manage/') return my_redirect("/exam/quizzes/") @@ -88,15 +91,20 @@ def user_register(request): ci = RequestContext(request) if user.is_authenticated(): return my_redirect("/exam/quizzes/") - + context = {} if request.method == "POST": form = UserRegisterForm(request.POST) if form.is_valid(): data = form.cleaned_data - u_name, pwd = form.save() - new_user = authenticate(username=u_name, password=pwd) - login(request, new_user) - return my_redirect("/exam/quizzes/") + if settings.IS_DEVELOPMENT: + u_name, pwd = form.save() + new_user = authenticate(username=u_name, password=pwd) + login(request, new_user) + return index(request) + user_email, key = form.save() + success, msg = send_user_mail(user_email, key) + context = {'activation_msg': msg} + return my_render_to_response('yaksh/activation_status.html', context) else: return my_render_to_response('yaksh/register.html', {'form': form}, context_instance=ci) @@ -323,19 +331,29 @@ def user_login(request): user = request.user ci = RequestContext(request) + context = {} if user.is_authenticated(): - if user.groups.filter(name='moderator').count() > 0: - return my_redirect('/exam/manage/') - return my_redirect("/exam/quizzes/") + if not settings.IS_DEVELOPMENT: + if not user.profile.is_email_verified: + context['success'] = False + context['msg'] = "Your account is not verified" + return my_render_to_response('yaksh/activation_status.html', + context, context_instance=ci) + return index(request) if request.method == "POST": form = UserLoginForm(request.POST) if form.is_valid(): user = form.cleaned_data + if not settings.IS_DEVELOPMENT: + if not user.profile.is_email_verified: + context['success'] = False + context['msg'] = "Your account is not verified. \ + Please verify your account" + return my_render_to_response('yaksh/activation_status.html', + context, context_instance=ci) login(request, user) - if user.groups.filter(name='moderator').count() > 0: - return my_redirect('/exam/manage/') - return my_redirect('/exam/login/') + return index(request) else: context = {"form": form} return my_render_to_response('yaksh/login.html', context, @@ -1403,3 +1421,45 @@ def download_course_csv(request, course_id): for student in students: writer.writerow(student) return response + +def activate_user(request, key): + ci = RequestContext(request) + profile = get_object_or_404(Profile, activation_key=key) + context = {} + context['success'] = False + if timezone.now() > profile.key_expiry_time: + context['msg'] = dedent(""" + Your activation time expired. + Please try again. + """) + else: + context['success'] = True + profile.is_email_verified = True + profile.save() + context['msg'] = "Your account is activated" + return my_render_to_response('yaksh/activation_status.html', context, + context_instance=ci) + +def new_activation(request): + ci = RequestContext(request) + context = {} + if request.method == "POST": + email = request.POST.get('email') + user = get_object_or_404(User, email=email) + if not user.profile.is_email_verified: + user.profile.activation_key = generate_activation_key(user.username) + user.profile.key_expiry_time = datetime.strftime( + datetime.now() + \ + timedelta(minutes=20), "%Y-%m-%d %H:%M:%S" + ) + user.profile.save() + success, msg = send_user_mail(user.email, user.profile.activation_key) + if success: + context['new_activation_msg'] = msg + else: + context['msg'] = msg + else: + context['new_activation_msg'] = "Your account is already verified" + + return my_render_to_response('yaksh/activation_status.html', context, + context_instance=ci) -- cgit From 8d0186ad51d3b0aa3991cb43c4477bcf2cdfb283 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 7 Apr 2017 15:57:46 +0530 Subject: Change settings for production deployment --- online_test/settings.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/online_test/settings.py b/online_test/settings.py index dcbbb21..8d0613b 100644 --- a/online_test/settings.py +++ b/online_test/settings.py @@ -102,6 +102,7 @@ MEDIA_URL = "/data/" MEDIA_ROOT = os.path.join(BASE_DIR, "yaksh", "data") +# Set this varable to if smtp-server is not allowing to send email. EMAIL_USE_TLS = False EMAIL_HOST = 'your_email_host' @@ -110,6 +111,28 @@ EMAIL_PORT = 'your_email_port' EMAIL_HOST_USER = 'email_host_user' +EMAIL_HOST_PASSWORD = 'email_host_password' + +# SENDER_EMAIL, REPLY_EMAIL, PRODUCTION_URL, IS_DEVELOPMENT are used in email +# verification. Set the variables accordingly to avoid errors in production + +# This email id will be used as for sending emails. +# For example no_reply@.in can be used. +SENDER_EMAIL = 'your_email' + +# This email id will be used by users to send their queries +# For example queries@.in can be used. +REPLY_EMAIL = 'your_reply_email' + +# This url will be used in email verification to create activation link. +# Add your hosted url to this variable. +# For example https://127.0.0.1:8000 or 127.0.0.1:8000 +PRODUCTION_URL = 'your_project_url' + +# Set this variable to once the project is in production. +# If this variable is kept in production, email will not be verified. +IS_DEVELOPMENT = True + DEFAULT_FROM_EMAIL = EMAIL_HOST_USER TEMPLATES = [ -- cgit From 09429a3a01a591e1a87df51e8584bb4358508b67 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 7 Apr 2017 15:58:20 +0530 Subject: Change Default timezone from UTC to Asia/Kolkata --- yaksh/middleware/user_time_zone.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yaksh/middleware/user_time_zone.py b/yaksh/middleware/user_time_zone.py index 0bd4831..ff9ec5c 100644 --- a/yaksh/middleware/user_time_zone.py +++ b/yaksh/middleware/user_time_zone.py @@ -5,10 +5,10 @@ from django.utils import timezone class TimezoneMiddleware(object): """ Middleware to get user's timezone and activate timezone - if user timezone is not available default value 'UTC' is activated """ + if user timezone is not available default value 'Asia/Kolkata' is activated """ def process_request(self, request): user = request.user - user_tz = 'UTC' + user_tz = 'Asia/Kolkata' if hasattr(user, 'profile'): if user.profile.timezone: user_tz = user.profile.timezone -- cgit From 6ac440c1ce1d26ce86376ee6bbba70525c5da945 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 7 Apr 2017 15:58:55 +0530 Subject: Add new module to send email for verification --- yaksh/email_verification.py | 55 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 yaksh/email_verification.py diff --git a/yaksh/email_verification.py b/yaksh/email_verification.py new file mode 100644 index 0000000..4eded1c --- /dev/null +++ b/yaksh/email_verification.py @@ -0,0 +1,55 @@ +# Local imports +try: + from string import letters +except ImportError: + from string import ascii_letters as letters +from string import digits, punctuation +# Local imports +import hashlib +from random import randint +from textwrap import dedent + +# Django imports +from django.utils.crypto import get_random_string +from django.core.mail import send_mail +from django.conf import settings + +def generate_activation_key(username): + """ Generate hashed secret key for email activation """ + chars = letters + digits + punctuation + secret_key = get_random_string(randint(10, 40), chars) + return hashlib.sha256((secret_key + username).encode('utf-8')).hexdigest() + +def send_user_mail(user_mail, key): + """ Send mail to user whose email is to be verified + This function should get two args i.e user_email and secret_key. + The activation url is generated from settings.PRODUCTION_URL and key. + """ + try: + to = user_mail + subject = "Yaksh Email Verification" + msg = dedent("""\ + To activate your account and verify your email address, + please click the following link: + {0}/exam/activate/{1} + If clicking the link above does not work, + copy and paste the URL in a new browser window instead. + For any issue, please write us on {2} + + Regards + Yaksh Team + """.format(settings.PRODUCTION_URL, key, settings.REPLY_EMAIL) + ) + + send_mail(subject, msg, settings.SENDER_EMAIL, [to]) + msg = "An activation link is sent to your registered email.\ + Please activate the link within 20 minutes." + success = True + except Exception as exc_msg: + msg = """Error: {0}. Please check your email address.\ + If email address is correct then + Please contact your \ + instructor/administrator.""".format(exc_msg) + success = False + + return success, msg -- cgit From 8db6fd363b27eab9d538173f07f90844eb5a7d0d Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 7 Apr 2017 15:59:41 +0530 Subject: Add template to display account activation status of user --- yaksh/templates/yaksh/activation_status.html | 47 ++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 yaksh/templates/yaksh/activation_status.html diff --git a/yaksh/templates/yaksh/activation_status.html b/yaksh/templates/yaksh/activation_status.html new file mode 100644 index 0000000..4ffd50e --- /dev/null +++ b/yaksh/templates/yaksh/activation_status.html @@ -0,0 +1,47 @@ +{% extends "base.html" %} + +{% block pagetitle %} Yaksh Account Activation {% endblock %} +{% block title %} Yaksh Account Activation {% endblock %} +{% block content %} +{% if success %} +
+ + Click Here to Login +
+{% else %} + {% if msg %} +
+ +
+
+ {% csrf_token %} +
+ Enter Email Address for verification:

+ + +
+
+ {% endif %} +{% endif %} +
+{% if new_activation_msg %} +
+ + Click Here to Login
+{% endif %} +{% if activation_msg %} +
+ + Click Here to Login
+{% endif %} + +{% endblock content %} \ No newline at end of file -- cgit From 80526e97c29ecc9bf67dd439c3a7a6cb5a404083 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 7 Apr 2017 16:03:13 +0530 Subject: Add new migrations for Profile model additions --- yaksh/migrations/0004_auto_20170407_0941.py | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 yaksh/migrations/0004_auto_20170407_0941.py diff --git a/yaksh/migrations/0004_auto_20170407_0941.py b/yaksh/migrations/0004_auto_20170407_0941.py new file mode 100644 index 0000000..88dad11 --- /dev/null +++ b/yaksh/migrations/0004_auto_20170407_0941.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2017-04-07 09:41 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('yaksh', '0003_auto_20170321_0917'), + ] + + operations = [ + migrations.AddField( + model_name='profile', + name='activation_key', + field=models.CharField(blank=True, max_length=40, null=True), + ), + migrations.AddField( + model_name='profile', + name='is_email_verified', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='profile', + name='key_expiry_time', + field=models.DateTimeField(blank=True, null=True), + ), + ] -- cgit From 70820e4831ad728b2bb3616be8fad24f9eaee64f Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 7 Apr 2017 16:19:26 +0530 Subject: Delete older migrations --- yaksh/migrations/0004_auto_20170407_0941.py | 30 ----------------------------- 1 file changed, 30 deletions(-) delete mode 100644 yaksh/migrations/0004_auto_20170407_0941.py diff --git a/yaksh/migrations/0004_auto_20170407_0941.py b/yaksh/migrations/0004_auto_20170407_0941.py deleted file mode 100644 index 88dad11..0000000 --- a/yaksh/migrations/0004_auto_20170407_0941.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.5 on 2017-04-07 09:41 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('yaksh', '0003_auto_20170321_0917'), - ] - - operations = [ - migrations.AddField( - model_name='profile', - name='activation_key', - field=models.CharField(blank=True, max_length=40, null=True), - ), - migrations.AddField( - model_name='profile', - name='is_email_verified', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='profile', - name='key_expiry_time', - field=models.DateTimeField(blank=True, null=True), - ), - ] -- cgit From 66add60208394d9aebdc642564ad905d4fc096c4 Mon Sep 17 00:00:00 2001 From: adityacp Date: Fri, 7 Apr 2017 16:20:22 +0530 Subject: Add new migrations for profile model --- yaksh/migrations/0005_auto_20170407_1048.py | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 yaksh/migrations/0005_auto_20170407_1048.py diff --git a/yaksh/migrations/0005_auto_20170407_1048.py b/yaksh/migrations/0005_auto_20170407_1048.py new file mode 100644 index 0000000..de7e2c2 --- /dev/null +++ b/yaksh/migrations/0005_auto_20170407_1048.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2017-04-07 10:48 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('yaksh', '0004_auto_20170331_0632'), + ] + + operations = [ + migrations.AddField( + model_name='profile', + name='activation_key', + field=models.CharField(blank=True, max_length=40, null=True), + ), + migrations.AddField( + model_name='profile', + name='is_email_verified', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='profile', + name='key_expiry_time', + field=models.DateTimeField(blank=True, null=True), + ), + ] -- cgit From 966fd7f33c67b9d20e42c35bd3825119e3ba066f Mon Sep 17 00:00:00 2001 From: adityacp Date: Wed, 26 Apr 2017 14:38:53 +0530 Subject: Remove old migrations for email verification --- yaksh/migrations/0005_auto_20170407_1048.py | 30 ----------------------------- 1 file changed, 30 deletions(-) delete mode 100644 yaksh/migrations/0005_auto_20170407_1048.py diff --git a/yaksh/migrations/0005_auto_20170407_1048.py b/yaksh/migrations/0005_auto_20170407_1048.py deleted file mode 100644 index de7e2c2..0000000 --- a/yaksh/migrations/0005_auto_20170407_1048.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.5 on 2017-04-07 10:48 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('yaksh', '0004_auto_20170331_0632'), - ] - - operations = [ - migrations.AddField( - model_name='profile', - name='activation_key', - field=models.CharField(blank=True, max_length=40, null=True), - ), - migrations.AddField( - model_name='profile', - name='is_email_verified', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='profile', - name='key_expiry_time', - field=models.DateTimeField(blank=True, null=True), - ), - ] -- cgit From 82e30d89fb1814d12a1ee40a0f1b81e164f0ed85 Mon Sep 17 00:00:00 2001 From: adityacp Date: Thu, 27 Apr 2017 15:29:09 +0530 Subject: Changes in email_verification.py, views.py, forms.py - Updation of user email if multiple users found with same email - Update user registration form - Change email_verification.py to send email --- yaksh/email_verification.py | 27 ++++++--- yaksh/forms.py | 4 +- yaksh/views.py | 140 +++++++++++++++++++++++++++++++------------- 3 files changed, 119 insertions(+), 52 deletions(-) diff --git a/yaksh/email_verification.py b/yaksh/email_verification.py index 4eded1c..721fa61 100644 --- a/yaksh/email_verification.py +++ b/yaksh/email_verification.py @@ -4,14 +4,13 @@ try: except ImportError: from string import ascii_letters as letters from string import digits, punctuation -# Local imports import hashlib from random import randint from textwrap import dedent +import smtplib # Django imports from django.utils.crypto import get_random_string -from django.core.mail import send_mail from django.conf import settings def generate_activation_key(username): @@ -27,8 +26,8 @@ def send_user_mail(user_mail, key): """ try: to = user_mail - subject = "Yaksh Email Verification" - msg = dedent("""\ + subject = 'Yaksh Email Verification' + message = dedent("""\ To activate your account and verify your email address, please click the following link: {0}/exam/activate/{1} @@ -41,15 +40,29 @@ def send_user_mail(user_mail, key): """.format(settings.PRODUCTION_URL, key, settings.REPLY_EMAIL) ) - send_mail(subject, msg, settings.SENDER_EMAIL, [to]) + user = settings.EMAIL_HOST_USER + pwd = settings.EMAIL_HOST_PASSWORD + smtpserver = smtplib.SMTP(settings.EMAIL_HOST, settings.EMAIL_PORT) + smtpserver.ehlo() + smtpserver.starttls() + smtpserver.ehlo() + smtpserver.esmtp_features['auth']='LOGIN DIGEST-MD5 PLAIN' + + smtpserver.login(user, pwd) + header = 'To:{0}\nFrom:{1}\nSubject:{2}\n'.format(to, + settings.SENDER_EMAIL, subject) + message = '{0}\n{1}\n\n'.format(header, message) + smtpserver.sendmail(user, to, message) + smtpserver.close() + msg = "An activation link is sent to your registered email.\ Please activate the link within 20 minutes." success = True + except Exception as exc_msg: msg = """Error: {0}. Please check your email address.\ If email address is correct then - Please contact your \ - instructor/administrator.""".format(exc_msg) + Please contact {1}.""".format(exc_msg, settings.REPLY_EMAIL) success = False return success, msg diff --git a/yaksh/forms.py b/yaksh/forms.py index ac4e1b0..b34ad59 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -144,8 +144,6 @@ class UserRegisterForm(forms.Form): new_profile.timezone = cleaned_data["timezone"] if settings.IS_DEVELOPMENT: new_profile.is_email_verified = True - new_profile.save() - return u_name, pwd else: new_profile.activation_key = generate_activation_key(new_user.username) new_profile.key_expiry_time = datetime.datetime.strftime( @@ -154,7 +152,7 @@ class UserRegisterForm(forms.Form): "%Y-%m-%d %H:%M:%S" ) new_profile.save() - return new_user.email, new_profile.activation_key + return u_name, pwd, new_user.email, new_profile.activation_key class UserLoginForm(forms.Form): diff --git a/yaksh/views.py b/yaksh/views.py index fa72008..751efb5 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -16,7 +16,7 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.models import Group from django.forms.models import inlineformset_factory from django.utils import timezone -from django.core.exceptions import MultipleObjectsReturned +from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.conf import settings import pytz from taggit.models import Tag @@ -43,7 +43,7 @@ from .settings import URL_ROOT from yaksh.models import AssignmentUpload from .file_utils import extract_files from .email_verification import send_user_mail, generate_activation_key - +from .decorators import email_verified def my_redirect(url): @@ -75,7 +75,7 @@ def add_to_group(users): if not is_moderator(user): user.groups.add(group) - +@email_verified def index(request): """The start page. """ @@ -101,15 +101,15 @@ def user_register(request): form = UserRegisterForm(request.POST) if form.is_valid(): data = form.cleaned_data - if settings.IS_DEVELOPMENT: - u_name, pwd = form.save() - new_user = authenticate(username=u_name, password=pwd) - login(request, new_user) - return index(request) - user_email, key = form.save() - success, msg = send_user_mail(user_email, key) - context = {'activation_msg': msg} - return my_render_to_response('yaksh/activation_status.html', context) + u_name, pwd, user_email, key = form.save() + new_user = authenticate(username=u_name, password=pwd) + login(request, new_user) + if user_email and key: + success, msg = send_user_mail(user_email, key) + context = {'activation_msg': msg} + return my_render_to_response('yaksh/activation_status.html', + context) + return index(request) else: return my_render_to_response('yaksh/register.html', {'form': form}, context_instance=ci) @@ -127,6 +127,7 @@ def user_logout(request): @login_required +@email_verified def quizlist_user(request, enrolled=None): """Show All Quizzes that is available to logged-in user.""" user = request.user @@ -141,6 +142,7 @@ def quizlist_user(request, enrolled=None): @login_required +@email_verified def results_user(request): """Show list of Results of Quizzes that is taken by logged-in user.""" user = request.user @@ -150,6 +152,7 @@ def results_user(request): @login_required +@email_verified def add_question(request, question_id=None): user = request.user ci = RequestContext(request) @@ -226,6 +229,7 @@ def add_question(request, question_id=None): @login_required +@email_verified def add_quiz(request, course_id, quiz_id=None): """To add a new quiz in the database. Create a new quiz and store it.""" @@ -268,6 +272,7 @@ def add_quiz(request, course_id, quiz_id=None): @login_required +@email_verified def show_all_questionpapers(request, questionpaper_id=None): user = request.user ci = RequestContext(request) @@ -291,6 +296,7 @@ def show_all_questionpapers(request, questionpaper_id=None): @login_required +@email_verified def prof_manage(request, msg=None): """Take credentials of the user with professor/moderator rights/permissions and log in.""" @@ -340,25 +346,12 @@ def user_login(request): ci = RequestContext(request) context = {} if user.is_authenticated(): - if not settings.IS_DEVELOPMENT: - if not user.profile.is_email_verified: - context['success'] = False - context['msg'] = "Your account is not verified" - return my_render_to_response('yaksh/activation_status.html', - context, context_instance=ci) - return index(request) + return index(request) if request.method == "POST": form = UserLoginForm(request.POST) if form.is_valid(): user = form.cleaned_data - if not settings.IS_DEVELOPMENT: - if not user.profile.is_email_verified: - context['success'] = False - context['msg'] = "Your account is not verified. \ - Please verify your account" - return my_render_to_response('yaksh/activation_status.html', - context, context_instance=ci) login(request, user) return index(request) else: @@ -372,8 +365,8 @@ def user_login(request): context_instance=ci) - @login_required +@email_verified def start(request, questionpaper_id=None, attempt_num=None): """Check the user cedentials and if any quiz is available, start the exam.""" @@ -430,6 +423,7 @@ def start(request, questionpaper_id=None, attempt_num=None): @login_required +@email_verified def show_question(request, question, paper, error_message=None, notification=None): """Show a question if possible.""" user = request.user @@ -461,6 +455,7 @@ def show_question(request, question, paper, error_message=None, notification=Non @login_required +@email_verified def skip(request, q_id, next_q=None, attempt_num=None, questionpaper_id=None): user = request.user paper = get_object_or_404(AnswerPaper, user=request.user, attempt_number=attempt_num, @@ -482,6 +477,7 @@ def skip(request, q_id, next_q=None, attempt_num=None, questionpaper_id=None): @login_required +@email_verified def check(request, q_id, attempt_num=None, questionpaper_id=None): """Checks the answers of the user for particular question""" user = request.user @@ -607,6 +603,7 @@ def quit(request, reason=None, attempt_num=None, questionpaper_id=None): @login_required +@email_verified def complete(request, reason=None, attempt_num=None, questionpaper_id=None): """Show a page to inform user that the quiz has been compeleted.""" user = request.user @@ -627,6 +624,7 @@ def complete(request, reason=None, attempt_num=None, questionpaper_id=None): @login_required +@email_verified def add_course(request, course_id=None): user = request.user ci = RequestContext(request) @@ -655,6 +653,7 @@ def add_course(request, course_id=None): @login_required +@email_verified def enroll_request(request, course_id): user = request.user ci = RequestContext(request) @@ -672,6 +671,7 @@ def enroll_request(request, course_id): @login_required +@email_verified def self_enroll(request, course_id): user = request.user ci = RequestContext(request) @@ -686,6 +686,7 @@ def self_enroll(request, course_id): @login_required +@email_verified def courses(request): user = request.user ci = RequestContext(request) @@ -699,6 +700,7 @@ def courses(request): @login_required +@email_verified def course_detail(request, course_id): user = request.user ci = RequestContext(request) @@ -715,6 +717,7 @@ def course_detail(request, course_id): @login_required +@email_verified def enroll(request, course_id, user_id=None, was_rejected=False): user = request.user ci = RequestContext(request) @@ -743,6 +746,7 @@ def enroll(request, course_id, user_id=None, was_rejected=False): @login_required +@email_verified def reject(request, course_id, user_id=None, was_enrolled=False): user = request.user ci = RequestContext(request) @@ -766,6 +770,7 @@ def reject(request, course_id, user_id=None, was_enrolled=False): @login_required +@email_verified def toggle_course_status(request, course_id): user = request.user if not is_moderator(user): @@ -784,6 +789,7 @@ def toggle_course_status(request, course_id): @login_required +@email_verified def show_statistics(request, questionpaper_id, attempt_number=None): user = request.user if not is_moderator(user): @@ -810,6 +816,7 @@ def show_statistics(request, questionpaper_id, attempt_number=None): @login_required +@email_verified def monitor(request, quiz_id=None): """Monitor the progress of the papers taken so far.""" @@ -906,6 +913,7 @@ def _remove_already_present(questionpaper_id, questions): @login_required +@email_verified def design_questionpaper(request, quiz_id, questionpaper_id=None): user = request.user @@ -992,6 +1000,7 @@ def design_questionpaper(request, quiz_id, questionpaper_id=None): @login_required +@email_verified def show_all_questions(request): """Show a list of all the questions currently in the database.""" @@ -1061,6 +1070,7 @@ def show_all_questions(request): context_instance=ci) @login_required +@email_verified def user_data(request, user_id, questionpaper_id=None): """Render user data.""" current_user = request.user @@ -1075,6 +1085,7 @@ def user_data(request, user_id, questionpaper_id=None): @login_required +@email_verified def download_csv(request, questionpaper_id): user = request.user if not is_moderator(user): @@ -1120,6 +1131,7 @@ def download_csv(request, questionpaper_id): return response @login_required +@email_verified def grade_user(request, quiz_id=None, user_id=None, attempt_number=None): """Present an interface with which we can easily grade a user's papers and update all their marks and also give comments for each paper. @@ -1190,6 +1202,7 @@ def grade_user(request, quiz_id=None, user_id=None, attempt_number=None): @login_required +@email_verified def view_profile(request): """ view moderators and users profile """ user = request.user @@ -1212,6 +1225,7 @@ def view_profile(request): @login_required +@email_verified def edit_profile(request): """ edit profile details facility for moderator and students """ @@ -1250,6 +1264,7 @@ def edit_profile(request): @login_required +@email_verified def search_teacher(request, course_id): """ search teachers for the course """ user = request.user @@ -1279,6 +1294,7 @@ def search_teacher(request, course_id): @login_required +@email_verified def add_teacher(request, course_id): """ add teachers to the course """ @@ -1307,6 +1323,7 @@ def add_teacher(request, course_id): @login_required +@email_verified def remove_teachers(request, course_id): """ remove user from a course """ @@ -1340,6 +1357,7 @@ def test_mode(user, godmode=False, questions_list=None, quiz_id=None): @login_required +@email_verified def test_quiz(request, mode, quiz_id): """creates a trial quiz for the moderators""" godmode = True if mode == "godmode" else False @@ -1353,6 +1371,7 @@ def test_quiz(request, mode, quiz_id): @login_required +@email_verified def view_answerpaper(request, questionpaper_id): user = request.user quiz = get_object_or_404(QuestionPaper, pk=questionpaper_id).quiz @@ -1368,6 +1387,7 @@ def view_answerpaper(request, questionpaper_id): @login_required +@email_verified def create_demo_course(request): """ creates a demo course for user """ user = request.user @@ -1384,6 +1404,7 @@ def create_demo_course(request): @login_required +@email_verified def grader(request, extra_context=None): user = request.user if not is_moderator(user): @@ -1397,6 +1418,7 @@ def grader(request, extra_context=None): @login_required +@email_verified def regrade(request, course_id, question_id=None, answerpaper_id=None, questionpaper_id=None): user = request.user course = get_object_or_404(Course, pk=course_id) @@ -1418,6 +1440,7 @@ def regrade(request, course_id, question_id=None, answerpaper_id=None, questionp return grader(request, extra_context={'details': details}) @login_required +@email_verified def download_course_csv(request, course_id): user = request.user if not is_moderator(user): @@ -1464,6 +1487,11 @@ def activate_user(request, key): profile = get_object_or_404(Profile, activation_key=key) context = {} context['success'] = False + if profile.is_email_verified: + context['activation_msg'] = "Your account is already verified" + return my_render_to_response('yaksh/activation_status.html', context, + context_instance=ci) + if timezone.now() > profile.key_expiry_time: context['msg'] = dedent(""" Your activation time expired. @@ -1477,31 +1505,59 @@ def activate_user(request, key): return my_render_to_response('yaksh/activation_status.html', context, context_instance=ci) -def new_activation(request): +def new_activation(request, email=None): ci = RequestContext(request) context = {} if request.method == "POST": email = request.POST.get('email') - user = get_object_or_404(User, email=email) - if not user.profile.is_email_verified: - user.profile.activation_key = generate_activation_key(user.username) - user.profile.key_expiry_time = datetime.strftime( - datetime.now() + \ - timedelta(minutes=20), "%Y-%m-%d %H:%M:%S" - ) - user.profile.save() - success, msg = send_user_mail(user.email, user.profile.activation_key) - if success: - context['new_activation_msg'] = msg - else: - context['msg'] = msg + + try: + user = User.objects.get(email=email) + except MultipleObjectsReturned: + context['email_err_msg'] = "Multiple entries found for this email"\ + "Please change your email" + return my_render_to_response('yaksh/activation_status.html', context, + context_instance=ci) + except ObjectDoesNotExist: + context['success'] = False + context['msg'] = "Your account is not verified. \ + Please verify your account" + return render_to_response('yaksh/activation_status.html', + context, context_instance=ci) + + if not user.profile.is_email_verified: + user.profile.activation_key = generate_activation_key(user.username) + user.profile.key_expiry_time = datetime.strftime( + datetime.now() + \ + timedelta(minutes=20), "%Y-%m-%d %H:%M:%S" + ) + user.profile.save() + success, msg = send_user_mail(user.email, user.profile.activation_key) + if success: + context['activation_msg'] = msg else: - context['new_activation_msg'] = "Your account is already verified" + context['msg'] = msg + else: + context['activation_msg'] = "Your account is already verified" return my_render_to_response('yaksh/activation_status.html', context, context_instance=ci) +def update_email(request): + if request.method == "POST": + email = request.POST.get('email') + username = request.POST.get('username') + user = get_object_or_404(User, username=username) + user.email = email + user.save() + return new_activation(request, email) + else: + context['email_err_msg'] = "Please Update your email" + return my_render_to_response('yaksh/activation_status.html', context, + context_instance=ci) + @login_required +@email_verified def download_assignment_file(request, quiz_id, question_id=None, user_id=None): user = request.user qp = QuestionPaper.objects.get(quiz_id=quiz_id) -- cgit From da1d135a1834d20c0aa5d2f75ea15a0d7a9eba8a Mon Sep 17 00:00:00 2001 From: adityacp Date: Thu, 27 Apr 2017 15:33:47 +0530 Subject: Change activation_status template and add update_email url in urls.py --- yaksh/templates/yaksh/activation_status.html | 58 +++++++++++++++++++++------- yaksh/urls.py | 1 + 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/yaksh/templates/yaksh/activation_status.html b/yaksh/templates/yaksh/activation_status.html index 4ffd50e..c196300 100644 --- a/yaksh/templates/yaksh/activation_status.html +++ b/yaksh/templates/yaksh/activation_status.html @@ -2,13 +2,29 @@ {% block pagetitle %} Yaksh Account Activation {% endblock %} {% block title %} Yaksh Account Activation {% endblock %} +{% block css%} + +{% endblock %} +{% block nav %} + +{% endblock %} {% block content %} {% if success %}
- Click Here to Login + Home
{% else %} {% if msg %} @@ -21,27 +37,43 @@ {% csrf_token %}
Enter Email Address for verification:

- - +
{% endif %} {% endif %}
-{% if new_activation_msg %} -
- - Click Here to Login
-{% endif %} {% if activation_msg %}
- Click Here to Login
+ Home + {% endif %} -{% endblock content %} \ No newline at end of file +{% if email_err_msg %} +
+ +
+
+ {% csrf_token %} +
+ + + + + + + + + +
Enter Username:
Enter New Email Address:
+
+ +
+
+{% endif %} +{% endblock content %} diff --git a/yaksh/urls.py b/yaksh/urls.py index fae8204..20ce918 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -5,6 +5,7 @@ urlpatterns = [ url(r'^$', views.index), url(r'^login/$', views.user_login, name='login'), url(r'^logout/$', views.user_logout), + url(r'^update_email/$', views.update_email), url(r'^activate/(?P.+)$', views.activate_user), url(r'^new_activation/$', views.new_activation), url(r'^quizzes/$', views.quizlist_user, name='quizlist_user'), -- cgit From b191455c7d9b0f6c3548da174a68c8ab8052abf0 Mon Sep 17 00:00:00 2001 From: adityacp Date: Thu, 27 Apr 2017 15:34:41 +0530 Subject: Add email_verified decorator to check whether email is verified --- yaksh/decorators.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 yaksh/decorators.py diff --git a/yaksh/decorators.py b/yaksh/decorators.py new file mode 100644 index 0000000..f0d354c --- /dev/null +++ b/yaksh/decorators.py @@ -0,0 +1,25 @@ +from django.shortcuts import render_to_response +from django.conf import settings +from django.template import RequestContext + + +def email_verified(func): + """ This decorator is used to check if email is verified. + If email is not verified then redirect user for email + verification + """ + + def is_email_verified(request, *args, **kwargs): + ci = RequestContext(request) + user = request.user + context = {} + if not settings.IS_DEVELOPMENT: + if user.is_authenticated() and hasattr(user, 'profile'): + if not user.profile.is_email_verified: + context['success'] = False + context['msg'] = "Your account is not verified. \ + Please verify your account" + return render_to_response('yaksh/activation_status.html', + context, context_instance=ci) + return func(request, *args, **kwargs) + return is_email_verified \ No newline at end of file -- cgit