Commit f046ff1451e8be3c618c962de53fe91cbb11f333
1 parent
e46a0179
Exists in
master
and in
39 other branches
Adding xmpp password change form with transactions and removing unnecessary changes from user
Showing
6 changed files
with
108 additions
and
30 deletions
Show diff stats
src/accounts/forms.py
| ... | ... | @@ -4,6 +4,8 @@ from django import forms |
| 4 | 4 | from django.contrib.auth import get_user_model |
| 5 | 5 | from django.utils.translation import ugettext_lazy as _ |
| 6 | 6 | |
| 7 | +from conversejs.models import XMPPAccount | |
| 8 | + | |
| 7 | 9 | from super_archives.models import MailingList |
| 8 | 10 | from .utils.validators import validate_social_account |
| 9 | 11 | |
| ... | ... | @@ -64,3 +66,31 @@ class ListsForm(forms.Form): |
| 64 | 66 | required=False, |
| 65 | 67 | widget=forms.CheckboxSelectMultiple, |
| 66 | 68 | choices=LISTS_NAMES) |
| 69 | + | |
| 70 | + | |
| 71 | +class ChangeXMPPPasswordForm(forms.ModelForm): | |
| 72 | + password1 = forms.CharField(label=_("Password"), | |
| 73 | + widget=forms.PasswordInput) | |
| 74 | + password2 = forms.CharField(label=_("Password confirmation"), | |
| 75 | + widget=forms.PasswordInput, | |
| 76 | + help_text=_("Enter the same password as above, for verification.")) | |
| 77 | + | |
| 78 | + class Meta: | |
| 79 | + model = XMPPAccount | |
| 80 | + fields = ('password1', 'password2') | |
| 81 | + | |
| 82 | + def __init__(self, *args, **kwargs): | |
| 83 | + super(ChangeXMPPPasswordForm, self).__init__(*args, **kwargs) | |
| 84 | + for field_name, field in self.fields.items(): | |
| 85 | + # Adds form-control class to all form fields | |
| 86 | + field.widget.attrs.update({'class': 'form-control'}) | |
| 87 | + | |
| 88 | + def clean_password2(self): | |
| 89 | + password1 = self.cleaned_data.get("password1") | |
| 90 | + password2 = self.cleaned_data.get("password2") | |
| 91 | + if password1 and password2 and password1 != password2: | |
| 92 | + raise forms.ValidationError( | |
| 93 | + self.error_messages['password_mismatch'], | |
| 94 | + code='password_mismatch', | |
| 95 | + ) | |
| 96 | + return password2 | ... | ... |
src/accounts/models.py
| ... | ... | @@ -2,8 +2,7 @@ |
| 2 | 2 | |
| 3 | 3 | import urlparse |
| 4 | 4 | |
| 5 | -from django.db import models | |
| 6 | -from django.db.models.signals import pre_save | |
| 5 | +from django.db import models, DatabaseError | |
| 7 | 6 | from django.contrib.auth.models import AbstractUser |
| 8 | 7 | from django.core.urlresolvers import reverse |
| 9 | 8 | |
| ... | ... | @@ -55,27 +54,3 @@ class User(AbstractUser): |
| 55 | 54 | # The following workaroud allows to change email field to unique |
| 56 | 55 | # without having to rewrite all AbstractUser here |
| 57 | 56 | User._meta.get_field('email')._unique = True |
| 58 | - | |
| 59 | - | |
| 60 | -def password_change(sender, instance, **kwargs): | |
| 61 | - from conversejs.models import XMPPAccount | |
| 62 | - # to register an XMPPAccount for an user, do the following: | |
| 63 | - # xmpp.register_account('username@domain', 'user_password' | |
| 64 | - # 'name', 'email') | |
| 65 | - # the domain can be found at conversejs.conf.CONVERSEJS_AUTO_REGISTER | |
| 66 | - | |
| 67 | - try: | |
| 68 | - user = sender.objects.get(pk=instance.pk) | |
| 69 | - except sender.DoesNotExist: | |
| 70 | - return # should I register a xmpp account here? | |
| 71 | - | |
| 72 | - try: | |
| 73 | - xmpp_account = XMPPAccount.objects.get(user=instance.pk) | |
| 74 | - except XMPPAccount.DoesNotExist: | |
| 75 | - return # User's XMPP account should be created here? | |
| 76 | - | |
| 77 | - if user.password != instance.password: | |
| 78 | - xmpp.change_password(xmpp_account, instance.password) | |
| 79 | - | |
| 80 | - | |
| 81 | -pre_save.connect(password_change, sender=User) | ... | ... |
| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | +{% extends "base.html" %} | |
| 2 | +{% load i18n %} | |
| 3 | + | |
| 4 | +{% block main-content %} | |
| 5 | +<form method="POST" role="form"> | |
| 6 | + {% csrf_token %} | |
| 7 | + <div class="row"> | |
| 8 | + <h2>{% trans "Change XMPP Client and SVN Password" %}</h2> | |
| 9 | + <div class="col-lg-4 col-md-4 col-sm-12 col-xs-12"> | |
| 10 | + {% for field in form %} | |
| 11 | + <div class="form-group required{% if field.errors %} alert alert-danger has-error{% endif %}"> | |
| 12 | + <label for="{{ field.name }}" class="control-label">{{ field.label }}</label> | |
| 13 | + {{ field }} | |
| 14 | + {{ field.errors }} | |
| 15 | + </div> | |
| 16 | + {% endfor %} | |
| 17 | + <button class="btn btn-primary">{% trans "Change Password" %}</button> | |
| 18 | + </div> | |
| 19 | + </div> | |
| 20 | +</form> | |
| 21 | +{% endblock %} | ... | ... |
src/accounts/templates/accounts/user_update_form.html
| ... | ... | @@ -178,6 +178,21 @@ $(function() { |
| 178 | 178 | </div> |
| 179 | 179 | </div> |
| 180 | 180 | </div> |
| 181 | + | |
| 182 | + <div class="col-lg-4 col-md-5 col-sm-12 col-xm-12"> | |
| 183 | + <div class="panel panel-default"> | |
| 184 | + <div class="panel-heading"> | |
| 185 | + <h3 class="panel-title">{% trans "Change SVN and XMPP Client password" %}</h3> | |
| 186 | + </div> | |
| 187 | + <div class="panel-body"> | |
| 188 | + <div class="form-group"> | |
| 189 | + {% trans "You don't need to change this password. Change it only if you really know what you are doing." %} | |
| 190 | + </div> | |
| 191 | + <a href="{% url 'change_password' %}" class="btn btn-primary pull-right">{% trans "Change Password" %}</a> | |
| 192 | + </div> | |
| 193 | + </div> | |
| 194 | + </div> | |
| 195 | + | |
| 181 | 196 | </div> |
| 182 | 197 | <div class="row"> |
| 183 | 198 | <div class="submit"> | ... | ... |
src/accounts/urls.py
| 1 | 1 | |
| 2 | 2 | from django.conf.urls import patterns, include, url |
| 3 | 3 | |
| 4 | -from .views import UserProfileDetailView, UserProfileUpdateView, \ | |
| 5 | - ManageUserSubscriptionsView | |
| 4 | +from .views import (UserProfileDetailView, UserProfileUpdateView, | |
| 5 | + ManageUserSubscriptionsView, ChangeXMPPPasswordView) | |
| 6 | 6 | |
| 7 | 7 | |
| 8 | 8 | urlpatterns = patterns('', |
| 9 | 9 | url(r'^register/$', 'accounts.views.signup', name='signup'), |
| 10 | 10 | |
| 11 | + url(r'^change-password/$', | |
| 12 | + ChangeXMPPPasswordView.as_view(), name='change_password'), | |
| 13 | + | |
| 11 | 14 | url(r'^(?P<username>[\w@+.-]+)/?$', |
| 12 | 15 | UserProfileDetailView.as_view(), name='user_profile'), |
| 13 | 16 | ... | ... |
src/accounts/views.py
| ... | ... | @@ -6,20 +6,25 @@ import datetime |
| 6 | 6 | from collections import OrderedDict |
| 7 | 7 | |
| 8 | 8 | from django.contrib import messages |
| 9 | +from django.db import transaction | |
| 9 | 10 | from django.db.models import Count |
| 10 | 11 | from django.contrib.auth import get_user_model |
| 11 | 12 | from django.utils.translation import ugettext as _ |
| 12 | -from django.shortcuts import render, redirect | |
| 13 | +from django.shortcuts import render, redirect, get_object_or_404 | |
| 13 | 14 | from django.core.urlresolvers import reverse |
| 14 | 15 | from django.core.exceptions import PermissionDenied |
| 15 | 16 | from django.views.generic import DetailView, UpdateView |
| 17 | +from django.utils.decorators import method_decorator | |
| 16 | 18 | |
| 19 | +from conversejs import xmpp | |
| 20 | +from conversejs.models import XMPPAccount | |
| 17 | 21 | from haystack.query import SearchQuerySet |
| 18 | 22 | |
| 19 | 23 | from super_archives.models import EmailAddress, Message |
| 20 | 24 | from super_archives.utils.email import send_email_lists |
| 21 | 25 | from search.utils import trans |
| 22 | -from .forms import UserCreationForm, ListsForm, UserUpdateForm | |
| 26 | +from .forms import (UserCreationForm, ListsForm, UserUpdateForm, | |
| 27 | + ChangeXMPPPasswordForm) | |
| 23 | 28 | from .utils import mailman |
| 24 | 29 | |
| 25 | 30 | |
| ... | ... | @@ -168,3 +173,32 @@ class ManageUserSubscriptionsView(UserProfileBaseMixin, DetailView): |
| 168 | 173 | context.update(kwargs) |
| 169 | 174 | |
| 170 | 175 | return super(ManageUserSubscriptionsView, self).get_context_data(**context) |
| 176 | + | |
| 177 | + | |
| 178 | +class ChangeXMPPPasswordView(UpdateView): | |
| 179 | + model = XMPPAccount | |
| 180 | + form_class = ChangeXMPPPasswordForm | |
| 181 | + fields = ['password', ] | |
| 182 | + template_name = 'accounts/change_password.html' | |
| 183 | + | |
| 184 | + @method_decorator(transaction.atomic) | |
| 185 | + def dispatch(self, request, *args, **kwargs): | |
| 186 | + return super(ChangeXMPPPasswordView, self).dispatch(request, *args, | |
| 187 | + **kwargs) | |
| 188 | + | |
| 189 | + def get_success_url(self): | |
| 190 | + return reverse('user_profile', kwargs={ | |
| 191 | + 'username': self.request.user.username | |
| 192 | + }) | |
| 193 | + | |
| 194 | + def get_object(self, queryset=None): | |
| 195 | + return get_object_or_404(XMPPAccount, user=self.request.user.pk) | |
| 196 | + | |
| 197 | + def form_valid(self, form): | |
| 198 | + self.object = form.save() | |
| 199 | + | |
| 200 | + xmpp_account = self.get_object() | |
| 201 | + changed = xmpp.change_password(xmpp_account, password) | |
| 202 | + if not changed: | |
| 203 | + raise Exception | |
| 204 | + return super(ChangeXMPPPassword, self).form_valid(form) | ... | ... |