Newer
Older
# (For now, we just assume there is one at most.)
# if there is no exam now (because someone bookmarked this stupid page),
# then return a 404:
exam_info = course.current_test_center_exam
if exam_info is None:
raise Http404
# determine if the user is registered for this course:
registration = exam_registration_info(user, course)
Brian Wilson
committed
# we want to populate the registration page with the relevant information,
# if it already exists. Create an empty object otherwise.
try:
testcenteruser = TestCenterUser.objects.get(user=user)
except TestCenterUser.DoesNotExist:
testcenteruser = TestCenterUser()
testcenteruser.user = user
Brian Wilson
committed
context = {'course': course,
'user': user,
'testcenteruser': testcenteruser,
'registration': registration,
'exam_info': exam_info,
Brian Wilson
committed
}
return render_to_response('test_center_register.html', context)
def create_exam_registration(request, post_override=None):
JSON call to create a test center exam registration.
Called by form in test_center_register.html
post_vars = post_override if post_override else request.POST
# first determine if we need to create a new TestCenterUser, or if we are making any update
# to an existing TestCenterUser.
Brian Wilson
committed
username = post_vars['username']
user = User.objects.get(username=username)
Brian Wilson
committed
course_id = post_vars['course_id']
course = course_from_id(course_id) # assume it will be found....
# make sure that any demographic data values received from the page have been stripped.
# Whitespace is not an acceptable response for any of these values
demographic_data = {}
for fieldname in TestCenterUser.user_provided_fields():
if fieldname in post_vars:
demographic_data[fieldname] = (post_vars[fieldname]).strip()
try:
testcenter_user = TestCenterUser.objects.get(user=user)
needs_updating = testcenter_user.needs_update(demographic_data)
log.info("User {0} enrolled in course {1} {2}updating demographic info for exam registration".format(user.username, course_id, "" if needs_updating else "not "))
except TestCenterUser.DoesNotExist:
# do additional initialization here:
testcenter_user = TestCenterUser.create(user)
log.info("User {0} enrolled in course {1} creating demographic info for exam registration".format(user.username, course_id))
# perform validation:
if needs_updating:
# first perform validation on the user information
form = TestCenterUserForm(instance=testcenter_user, data=demographic_data)
form.update_and_save()
else:
response_data = {'success': False}
# return a list of errors...
response_data['field_errors'] = form.errors
response_data['non_field_errors'] = form.non_field_errors()
return HttpResponse(json.dumps(response_data), mimetype="application/json")
Brian Wilson
committed
# create and save the registration:
needs_saving = False
exam = course.current_test_center_exam
exam_code = exam.exam_series_code
registrations = get_testcenter_registration(user, course_id, exam_code)
registration = registrations[0]
# NOTE: we do not bother to check here to see if the registration has changed,
# because at the moment there is no way for a user to change anything about their
# registration. They only provide an optional accommodation request once, and
# cannot make changes to it thereafter.
# It is possible that the exam_info content has been changed, such as the
# scheduled exam dates, but those kinds of changes should not be handled through
else:
accommodation_request = post_vars.get('accommodation_request', '')
registration = TestCenterRegistration.create(testcenter_user, exam, accommodation_request)
needs_saving = True
log.info("User {0} enrolled in course {1} creating new exam registration".format(user.username, course_id))
if needs_saving:
# do validation of registration. (Mainly whether an accommodation request is too long.)
form = TestCenterRegistrationForm(instance=registration, data=post_vars)
if form.is_valid():
form.update_and_save()
else:
response_data = {'success': False}
# return a list of errors...
response_data['field_errors'] = form.errors
response_data['non_field_errors'] = form.non_field_errors()
return HttpResponse(json.dumps(response_data), mimetype="application/json")
# only do the following if there is accommodation text to send,
# and a destination to which to send it.
# TODO: still need to create the accommodation email templates
# if 'accommodation_request' in post_vars and 'TESTCENTER_ACCOMMODATION_REQUEST_EMAIL' in settings:
# d = {'accommodation_request': post_vars['accommodation_request'] }
# # composes accommodation email
# subject = render_to_string('emails/accommodation_email_subject.txt', d)
# # Email subject *must not* contain newlines
# subject = ''.join(subject.splitlines())
# message = render_to_string('emails/accommodation_email.txt', d)
#
# try:
# dest_addr = settings['TESTCENTER_ACCOMMODATION_REQUEST_EMAIL']
# from_addr = user.email
# send_mail(subject, message, from_addr, [dest_addr], fail_silently=False)
# except:
# log.exception(sys.exc_info())
# response_data = {'success': False}
# response_data['non_field_errors'] = [ 'Could not send accommodation e-mail.', ]
# return HttpResponse(json.dumps(response_data), mimetype="application/json")
js = {'success': True}
return HttpResponse(json.dumps(js), mimetype="application/json")
def auto_auth(request):
Automatically logs the user in with a generated random credentials
This view is only accessible when
settings.MITX_SETTINGS['AUTOMATIC_AUTH_FOR_TESTING'] is true.
def get_dummy_post_data(username, password, email, name):
"""
Return a dictionary suitable for passing to post_vars of _do_create_account or post_override
of create_account, with specified values.
"""
return {'username': username,
'email': email,
'password': password,
'name': name,
'honor_code': u'true',
'terms_of_service': u'true', }
# generate random user credentials from a small name space (determined by settings)
name_base = 'USER_'
pass_base = 'PASS_'
max_users = settings.MITX_FEATURES.get('MAX_AUTO_AUTH_USERS', 200)
number = random.randint(1, max_users)
# Get the params from the request to override default user attributes if specified
qdict = request.GET
# Use the params from the request, otherwise use these defaults
username = qdict.get('username', name_base + str(number))
password = qdict.get('password', pass_base + str(number))
email = qdict.get('email', '%s_dummy_test@mitx.mit.edu' % username)
name = qdict.get('name', '%s Test' % username)
Bridger Maxwell
committed
# if they already are a user, log in
try:
user = User.objects.get(username=username)
user = authenticate(username=username, password=password, request=request)
login(request, user)
# else create and activate account info
except ObjectDoesNotExist:
post_override = get_dummy_post_data(username, password, email, name)
create_account(request, post_override=post_override)
request.user.is_active = True
request.user.save()
# return empty success
return HttpResponse('')
Bridger Maxwell
committed
@ensure_csrf_cookie
"""When link in activation e-mail is clicked"""
r = Registration.objects.filter(activation_key=key)
if len(r) == 1:
Bridger Maxwell
committed
user_logged_in = request.user.is_authenticated()
already_active = True
if not r[0].user.is_active:
r[0].activate()
Bridger Maxwell
committed
already_active = False
Brian Wilson
committed
# Enroll student in any pending courses he/she may have if auto_enroll flag is set
student = User.objects.filter(id=r[0].user_id)
if student:
ceas = CourseEnrollmentAllowed.objects.filter(email=student[0].email)
for cea in ceas:
if cea.auto_enroll:
David Ormsbee
committed
CourseEnrollment.enroll(student[0], cea.course_id)
resp = render_to_response(
"registration/activation_complete.html",
{
'user_logged_in': user_logged_in,
'already_active': already_active
}
)
David Ormsbee
committed
return render_to_response(
"registration/activation_invalid.html",
{'csrf': csrf(request)['csrf_token']}
)
return HttpResponse(_("Unknown error. Please e-mail us to let us know how it happened."))
@ensure_csrf_cookie
""" Attempts to send a password reset e-mail. """
form = PasswordResetFormNoActive(request.POST)
form.save(use_https=request.is_secure(),
from_email=settings.DEFAULT_FROM_EMAIL,
request=request,
domain_override=request.get_host())
return HttpResponse(json.dumps({'success': True,
'value': render_to_string('registration/password_reset_done.html', {})}))
else:
return HttpResponse(json.dumps({'success': False,
def password_reset_confirm_wrapper(
request,
uidb36=None,
token=None,
):
""" A wrapper around django.contrib.auth.views.password_reset_confirm.
Needed because we want to set the user as active at this step.
Brian Wilson
committed
# cribbed from django.contrib.auth.views.password_reset_confirm
try:
uid_int = base36_to_int(uidb36)
user = User.objects.get(id=uid_int)
user.is_active = True
user.save()
except (ValueError, User.DoesNotExist):
pass
# we also want to pass settings.PLATFORM_NAME in as extra_context
extra_context = {"platform_name": settings.PLATFORM_NAME}
return password_reset_confirm(
request, uidb36=uidb36, token=token, extra_context=extra_context
)
def reactivation_email_for_user(user):
try:
reg = Registration.objects.get(user=user)
except Registration.DoesNotExist:
return HttpResponse(json.dumps({'success': False,
d = {'name': user.profile.name,
'key': reg.activation_key}
subject = render_to_string('emails/activation_email_subject.txt', d)
subject = ''.join(subject.splitlines())
message = render_to_string('emails/activation_email.txt', d)
Brian Wilson
committed
_res = user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
except:
log.warning('Unable to send reactivation email', exc_info=True)
return HttpResponse(json.dumps({'success': False, 'error': _('Unable to send reactivation email')}))
return HttpResponse(json.dumps({'success': True}))
@ensure_csrf_cookie
def change_email_request(request):
""" AJAX call from the profile page. User wants a new e-mail.
"""
## Make sure it checks for existing e-mail conflicts
if not request.user.is_authenticated:
raise Http404
user = request.user
if not user.check_password(request.POST['password']):
return HttpResponse(json.dumps({'success': False,
new_email = request.POST['new_email']
try:
validate_email(new_email)
except ValidationError:
return HttpResponse(json.dumps({'success': False,
if User.objects.filter(email=new_email).count() != 0:
## CRITICAL TODO: Handle case sensitivity for e-mails
return HttpResponse(json.dumps({'success': False,
'error': _('An account with this e-mail already exists.')}))
pec_list = PendingEmailChange.objects.filter(user=request.user)
pec = PendingEmailChange()
pec.user = user
pec = pec_list[0]
pec.new_email = request.POST['new_email']
pec.activation_key = uuid.uuid4().hex
pec.save()
return HttpResponse(json.dumps({'success': False,
d = {'key': pec.activation_key,
'old_email': user.email,
'new_email': pec.new_email}
subject = render_to_string('emails/email_change_subject.txt', d)
subject = ''.join(subject.splitlines())
message = render_to_string('emails/email_change.txt', d)
Brian Wilson
committed
_res = send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [pec.new_email])
return HttpResponse(json.dumps({'success': True}))
@transaction.commit_manually
def confirm_email_change(request, key):
""" User requested a new e-mail. This is called when the activation
link is clicked. We confirm with the old e-mail, and update
try:
pec = PendingEmailChange.objects.get(activation_key=key)
except PendingEmailChange.DoesNotExist:
response = render_to_response("invalid_email_key.html", {})
transaction.rollback()
return response
user = pec.user
address_context = {
'old_email': user.email,
'new_email': pec.new_email
}
if len(User.objects.filter(email=pec.new_email)) != 0:
response = render_to_response("email_exists.html", {})
transaction.rollback()
return response
subject = render_to_string('emails/email_change_subject.txt', address_context)
subject = ''.join(subject.splitlines())
message = render_to_string('emails/confirm_email_change.txt', address_context)
up = UserProfile.objects.get(user=user)
meta = up.get_meta()
if 'old_emails' not in meta:
meta['old_emails'] = []
meta['old_emails'].append([user.email, datetime.datetime.now(UTC).isoformat()])
up.set_meta(meta)
up.save()
# Send it to the old email...
try:
user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
except Exception:
log.warning('Unable to send confirmation email to old address', exc_info=True)
response = render_to_response("email_change_failed.html", {'email': user.email})
transaction.rollback()
return response
user.email = pec.new_email
user.save()
pec.delete()
# And send it to the new email...
try:
user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
except Exception:
log.warning('Unable to send confirmation email to new address', exc_info=True)
response = render_to_response("email_change_failed.html", {'email': pec.new_email})
transaction.rollback()
return response
response = render_to_response("email_change_successful.html", address_context)
transaction.commit()
return response
except Exception:
# If we get an unexpected exception, be sure to rollback the transaction
transaction.rollback()
raise
@ensure_csrf_cookie
def change_name_request(request):
if not request.user.is_authenticated:
raise Http404
pnc = PendingNameChange.objects.get(user=request.user)
pnc = PendingNameChange()
pnc.user = request.user
pnc.new_name = request.POST['new_name']
pnc.rationale = request.POST['rationale']
return HttpResponse(json.dumps({'success': False, 'error': _('Name required')}))
# The following automatically accepts name change requests. Remove this to
# go back to the old system where it gets queued up for admin approval.
accept_name_change_by_id(pnc.id)
return HttpResponse(json.dumps({'success': True}))
def pending_name_changes(request):
""" Web page which allows staff to approve or reject name changes. """
if not request.user.is_staff:
raise Http404
changes = list(PendingNameChange.objects.all())
js = {'students': [{'new_name': c.new_name,
'rationale': c.rationale,
'old_name': UserProfile.objects.get(user=c.user).name,
'email': c.user.email,
'uid': c.user.id,
'cid': c.id} for c in changes]}
return render_to_response('name_changes.html', js)
def reject_name_change(request):
""" JSON: Name change process. Course staff clicks 'reject' on a given name change """
if not request.user.is_staff:
raise Http404
pnc = PendingNameChange.objects.get(id=int(request.POST['id']))
return HttpResponse(json.dumps({'success': False, 'error': _('Invalid ID')}))
return HttpResponse(json.dumps({'success': True}))
def accept_name_change_by_id(id):
pnc = PendingNameChange.objects.get(id=id)
return HttpResponse(json.dumps({'success': False, 'error': _('Invalid ID')}))
u = pnc.user
up = UserProfile.objects.get(user=u)
# Save old name
meta = up.get_meta()
if 'old_names' not in meta:
meta['old_names'] = []
meta['old_names'].append([up.name, pnc.rationale, datetime.datetime.now(UTC).isoformat()])
up.set_meta(meta)
up.name = pnc.new_name
return HttpResponse(json.dumps({'success': True}))
@ensure_csrf_cookie
def accept_name_change(request):
""" JSON: Name change process. Course staff clicks 'accept' on a given name change
David Ormsbee
committed
We used this during the prototype but now we simply record name changes instead
of manually approving them. Still keeping this around in case we want to go
back to this approval method.
if not request.user.is_staff:
raise Http404
return accept_name_change_by_id(int(request.POST['id']))
@ensure_csrf_cookie
def change_email_settings(request):
"""Modify logged-in user's setting for receiving emails from a course."""
user = request.user
course_id = request.POST.get("course_id")
receive_emails = request.POST.get("receive_emails")
if receive_emails:
optout_object = Optout.objects.filter(user=user, course_id=course_id)
if optout_object:
optout_object.delete()
log.info(u"User {0} ({1}) opted in to receive emails from course {2}".format(user.username, user.email, course_id))
track.views.server_track(request, "change-email-settings", {"receive_emails": "yes", "course": course_id}, page='dashboard')
else:
Optout.objects.get_or_create(user=user, course_id=course_id)
log.info(u"User {0} ({1}) opted out of receiving emails from course {2}".format(user.username, user.email, course_id))
track.views.server_track(request, "change-email-settings", {"receive_emails": "no", "course": course_id}, page='dashboard')