diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index b19833bbed..b2fcc73ca3 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -262,10 +262,15 @@ def login_user(request, error=""): try_change_enrollment(request) return HttpResponse(json.dumps({'success': True})) - - log.warning("Login failed - Account not active for user {0}".format(username)) + + log.warning("Login failed - Account not active for user {0}, resending activation".format(username)) + + reactivation_email_for_user(user) + not_activated_msg = "This account has not been activated. We have " + \ + "sent another activation message. Please check your " + \ + "e-mail for the activation instructions." return HttpResponse(json.dumps({'success': False, - 'value': 'This account has not been activated. Please check your e-mail for the activation instructions.'})) + 'value': not_activated_msg})) @ensure_csrf_cookie @@ -517,6 +522,17 @@ def password_reset(request): ''' Attempts to send a password reset e-mail. ''' if request.method != "POST": raise Http404 + + # By default, Django doesn't allow Users with is_active = False to reset their passwords, + # but this bites people who signed up a long time ago, never activated, and forgot their + # password. So for their sake, we'll auto-activate a user for whome password_reset is called. + try: + user = User.objects.get(email=request.POST['email']) + user.is_active = True + user.save() + except: + log.exception("Tried to auto-activate user to enable password reset, but failed.") + form = PasswordResetForm(request.POST) if form.is_valid(): form.save(use_https = request.is_secure(), @@ -529,7 +545,6 @@ def password_reset(request): return HttpResponse(json.dumps({'success': False, 'error': 'Invalid e-mail'})) - @ensure_csrf_cookie def reactivation_email(request): ''' Send an e-mail to reactivate a deactivated account, or to @@ -540,25 +555,22 @@ def reactivation_email(request): except User.DoesNotExist: return HttpResponse(json.dumps({'success': False, 'error': 'No inactive user with this e-mail exists'})) + return reactivation_email_for_user(user) - if user.is_active: - return HttpResponse(json.dumps({'success': False, - 'error': 'User is already active'})) - +def reactivation_email_for_user(user): reg = Registration.objects.get(user=user) - reg.register(user) - d = {'name': UserProfile.get(user=user).name, - 'key': r.activation_key} + d = {'name': user.profile.name, + 'key': reg.activation_key} - subject = render_to_string('reactivation_email_subject.txt', d) + subject = render_to_string('emails/activation_email_subject.txt', d) subject = ''.join(subject.splitlines()) - message = render_to_string('reactivation_email.txt', d) + message = render_to_string('emails/activation_email.txt', d) res = user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL) return HttpResponse(json.dumps({'success': True})) - + @ensure_csrf_cookie def change_email_request(request): @@ -642,9 +654,12 @@ def confirm_email_change(request, key): meta['old_emails'].append([user.email, datetime.datetime.now().isoformat()]) up.set_meta(meta) up.save() + # Send it to the old email... + user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL) user.email = pec.new_email user.save() pec.delete() + # And send it to the new email... user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL) return render_to_response("email_change_successful.html", d) @@ -665,9 +680,12 @@ def change_name_request(request): pnc.rationale = request.POST['rationale'] if len(pnc.new_name) < 2: return HttpResponse(json.dumps({'success': False, 'error': 'Name required'})) - if len(pnc.rationale) < 2: - return HttpResponse(json.dumps({'success': False, 'error': 'Rationale required'})) pnc.save() + + # 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})) @@ -702,14 +720,9 @@ def reject_name_change(request): 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 ''' - if not request.user.is_staff: - raise Http404 - +def accept_name_change_by_id(id): try: - pnc = PendingNameChange.objects.get(id=int(request.POST['id'])) + pnc = PendingNameChange.objects.get(id=id) except PendingNameChange.DoesNotExist: return HttpResponse(json.dumps({'success': False, 'error': 'Invalid ID'})) @@ -728,3 +741,17 @@ def accept_name_change(request): pnc.delete() 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 + + 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'])) diff --git a/lms/templates/dashboard.html b/lms/templates/dashboard.html index 6be72277cc..3fdcbb5c1c 100644 --- a/lms/templates/dashboard.html +++ b/lms/templates/dashboard.html @@ -16,19 +16,63 @@ $(".unenroll").click(function(event) { $("#unenroll_course_id").val( $(event.target).data("course-id") ); $("#unenroll_course_number").text( $(event.target).data("course-number") ); - }); - $(document).delegate('#unenroll_form', 'ajax:success', function(data, json, xhr) { - if(json.success) { - location.href="${reverse('dashboard')}"; - } else { - if($('#unenroll_error').length == 0) { - $('#unenroll_form').prepend('
'); - } - $('#unenroll_error').text(json.error).stop().css("display", "block"); - } - }); + $(document).delegate('#unenroll_form', 'ajax:success', function(data, json, xhr) { + if(json.success) { + location.href="${reverse('dashboard')}"; + } else { + if($('#unenroll_error').length == 0) { + $('#unenroll_form').prepend(''); + } + $('#unenroll_error').text(json.error).stop().css("display", "block"); + } + }); + + $('#pwd_reset_button').click(function() { + $.post('${reverse("password_reset")}', + {"email" : $('#id_email').val()}, + function(data){ + $("#password_reset_complete_link").click(); + }); + }); + + $("#change_email_form").submit(function(){ + var new_email = $('#new_email_field').val(); + var new_password = $('#new_email_password').val(); + + $.post('${reverse("change_email")}', + {"new_email" : new_email, "password" : new_password}, + function(data) { + if (data.success) { + $("#change_email_title").html("Please verify your new email"); + $("#change_email_form").html("You'll receive a confirmation in your " + + "in-box. Please click the link in the " + + "email to confirm the email change.
"); + } else { + $("#change_email_error").html(data.error).stop().css("display", "block"); + } + }); + return false; + }); + + $("#change_name_form").submit(function(){ + var new_name = $('#new_name_field').val(); + var rationale = $('#name_rationale_field').val(); + + $.post('${reverse("change_name")}', + {"new_name":new_name, "rationale":rationale}, + function(data) { + if(data.success) { + location.reload(); + // $("#change_name_body").html("Name changed.
"); + } else { + $("#change_name_error").html(data.error).stop().css("display", "block"); + } + }); + return false; + }); + })(this) %block> @@ -48,10 +92,17 @@ @@ -121,13 +172,11 @@ - -✕
+✕
+✕
+You should see your new name in your profile. +
You should see your new email in your dashboard.
\ No newline at end of file diff --git a/lms/templates/emails/confirm_email_change.txt b/lms/templates/emails/confirm_email_change.txt index e1b5d63376..02aa20facf 100644 --- a/lms/templates/emails/confirm_email_change.txt +++ b/lms/templates/emails/confirm_email_change.txt @@ -1,8 +1,7 @@ <%! from django.core.urlresolvers import reverse %> This is to confirm that you changed the e-mail associated with edX from ${old_email} to ${new_email}. If you did not make this request, -please contact the course staff immediately. Contact information is -listed at: +please contact us immediately. Contact information is listed at: % if is_secure: https://${ site }${reverse('contact')} diff --git a/lms/templates/registration/password_reset_email.html b/lms/templates/registration/password_reset_email.html index 6d906c84ff..bf6c3e0891 100644 --- a/lms/templates/registration/password_reset_email.html +++ b/lms/templates/registration/password_reset_email.html @@ -5,7 +5,8 @@ {% block reset_link %} https://{{domain}}{% url 'django.contrib.auth.views.password_reset_confirm' uidb36=uid token=token %} {% endblock %} -{% trans "Your username, in case you've forgotten:" %} {{ user.username }} + +If you didn't request this change, you can disregard this email - we have not yet reset your password. {% trans "Thanks for using our site!" %} diff --git a/lms/urls.py b/lms/urls.py index e025478387..f3867cb7af 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -19,9 +19,9 @@ urlpatterns = ('', # (specifically missing get parameters in certain cases) url(r'^debug_request$', 'util.views.debug_request'), - url(r'^change_email$', 'student.views.change_email_request'), + url(r'^change_email$', 'student.views.change_email_request', name="change_email"), url(r'^email_confirm/(?P