Merge pull request #897 from MITx/feature/dave/email_bugfixes
Login/Profile changes
This commit is contained in:
@@ -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']))
|
||||
|
||||
@@ -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('<div id="unenroll_error" class="modal-form-error"></div>');
|
||||
}
|
||||
$('#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('<div id="unenroll_error" class="modal-form-error"></div>');
|
||||
}
|
||||
$('#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("<p>You'll receive a confirmation in your " +
|
||||
"in-box. Please click the link in the " +
|
||||
"email to confirm the email change.</p>");
|
||||
} 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("<p>Name changed.</p>");
|
||||
} else {
|
||||
$("#change_name_error").html(data.error).stop().css("display", "block");
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
})(this)
|
||||
</script>
|
||||
</%block>
|
||||
@@ -48,10 +92,17 @@
|
||||
<section class="user-info">
|
||||
<ul>
|
||||
<li>
|
||||
<span class="title"><div class="icon name-icon"></div>Full Name</span><span class="data">${ user.profile.name | h }</span>
|
||||
<span class="title"><div class="icon name-icon"></div>Full Name (<a href="#apply_name_change" rel="leanModal" class="edit-name">edit</a>)</span> <span class="data">${ user.profile.name | h }</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="title"><div class="icon email-icon"></div>Email</span><span class="data">${ user.email | h }</span>
|
||||
<span class="title"><div class="icon email-icon"></div>Email (<a href="#change_email" rel="leanModal" class="edit-email">edit</a>)</span> <span class="data">${ user.email | h }</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="title"><a href="#password_reset_complete" rel="leanModal" id="pwd_reset_button">Reset Password</a></span>
|
||||
<form id="password_reset_form" method="post" data-remote="true" action="${reverse('password_reset')}">
|
||||
<input id="id_email" type="hidden" name="email" maxlength="75" value="${user.email}" />
|
||||
<!-- <input type="submit" id="pwd_reset_button" value="Reset Password" /> -->
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
@@ -121,13 +172,11 @@
|
||||
</section>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<section id="unenroll-modal" class="modal unenroll-modal">
|
||||
<div class="inner-wrapper">
|
||||
<header>
|
||||
<h2>Are you sure you want to unregister from <span id="unenroll_course_number"></span>?</h2>
|
||||
<hr>
|
||||
<hr/>
|
||||
</header>
|
||||
|
||||
<form id="unenroll_form" method="post" data-remote="true" action="${reverse('change_enrollment')}">
|
||||
@@ -146,4 +195,89 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="password_reset_complete" class="modal">
|
||||
<div class="inner-wrapper">
|
||||
<header>
|
||||
<h2>Password Reset Email Sent</h2>
|
||||
<hr/>
|
||||
</header>
|
||||
<div>
|
||||
<form> <!-- Here for styling reasons -->
|
||||
<section>
|
||||
<p>An email has been sent to ${user.email}. Follow the link in the email to change your password.</p>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
<div class="close-modal">
|
||||
<div class="inner">
|
||||
<p>✕</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="change_email" class="modal">
|
||||
<div class="inner-wrapper">
|
||||
<header>
|
||||
<h2><span id="change_email_title">Change Email</span></h2>
|
||||
<hr/>
|
||||
</header>
|
||||
<div id="change_email_body">
|
||||
<form id="change_email_form">
|
||||
<div id="change_email_error" class="modal-form-error"> </div>
|
||||
<fieldset>
|
||||
<div class="input-group">
|
||||
<label>Please enter your new email address:</label>
|
||||
<input id="new_email_field" type="email" value="" />
|
||||
<label>Please confirm your password:</label>
|
||||
<input id="new_email_password" value="" type="password" />
|
||||
</div>
|
||||
<section>
|
||||
<p>We will send a confirmation to both ${user.email} and your new email as part of the process.</p>
|
||||
</section>
|
||||
<div class="submit">
|
||||
<input type="submit" id="submit_email_change" value="Change Email"/>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<div class="close-modal">
|
||||
<div class="inner">
|
||||
<p>✕</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="apply_name_change" class="modal">
|
||||
<div class="inner-wrapper">
|
||||
<header>
|
||||
<h2>Change your name</h2>
|
||||
<hr/>
|
||||
</header>
|
||||
<div id="change_name_body">
|
||||
<form id="change_name_form">
|
||||
<div id="change_name_error" class="modal-form-error"> </div>
|
||||
<p>To uphold the credibility of edX certificates, all name changes will be logged and recorded.</p>
|
||||
<br/>
|
||||
<fieldset>
|
||||
<div class="input-group">
|
||||
<label>Enter your desired full name, as it will appear on the edX certificates: </label>
|
||||
<input id="new_name_field" value="" type="text" />
|
||||
<label>Reason for name change:</label>
|
||||
<textarea id="name_rationale_field" value=""></textarea>
|
||||
</div>
|
||||
<div class="submit">
|
||||
<input type="submit" id="submit" value="Change My Name">
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<div class="close-modal">
|
||||
<div class="inner">
|
||||
<p>✕</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<h1>E-mail change successful!</h1>
|
||||
|
||||
<p> You should see your new name in your profile.
|
||||
<p>You should see your new email in your <a href="/dashboard">dashboard</a>.</p>
|
||||
@@ -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')}
|
||||
|
||||
@@ -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!" %}
|
||||
|
||||
|
||||
@@ -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<key>[^/]*)$', 'student.views.confirm_email_change'),
|
||||
url(r'^change_name$', 'student.views.change_name_request'),
|
||||
url(r'^change_name$', 'student.views.change_name_request', name="change_name"),
|
||||
url(r'^accept_name_change$', 'student.views.accept_name_change'),
|
||||
url(r'^reject_name_change$', 'student.views.reject_name_change'),
|
||||
url(r'^pending_name_changes$', 'student.views.pending_name_changes'),
|
||||
|
||||
Reference in New Issue
Block a user