Commit 54eede10 authored by Weblate's avatar Weblate

Merge remote-tracking branch 'origin/master'

parents 1172f974 f015acd4
......@@ -25,7 +25,6 @@ from accounts.models import Profile
from lang.models import Language
from trans.models import Project
from django.contrib.auth.models import User
from registration.forms import RegistrationFormUniqueEmail
from django.utils.encoding import force_unicode
from itertools import chain
......@@ -196,10 +195,9 @@ class ContactForm(forms.Form):
return ''
class RegistrationForm(RegistrationFormUniqueEmail):
class RegistrationForm(forms.Form):
'''
Registration form, please note it does not save first/last name
this is done by signal handler in accounts.models.
Registration form.
'''
required_css_class = "required"
error_css_class = "error"
......@@ -208,6 +206,7 @@ class RegistrationForm(RegistrationFormUniqueEmail):
regex=r'^[\w.@+-]+$',
max_length=30,
label=_("Username"),
help_text=_('At least five characters long.'),
error_messages={
'invalid': _(
'This value may contain only letters, '
......@@ -215,34 +214,61 @@ class RegistrationForm(RegistrationFormUniqueEmail):
)
}
)
email = forms.EmailField(
max_length=75,
label=_("E-mail"),
help_text=_('Activation email will be sent here.'),
)
password1 = forms.CharField(
widget=forms.PasswordInput(render_value=False),
label=_("Password"),
help_text=_('At least six characters long.'),
)
password2 = forms.CharField(
widget=forms.PasswordInput(render_value=False),
label=_("Password (again)"),
help_text=_(
'Repeat the password so we can verify '
'you typed it in correctly.'
),
)
first_name = forms.CharField(label=_('First name'))
last_name = forms.CharField(label=_('Last name'))
content = forms.CharField(required=False)
def __init__(self, *args, **kwargs):
def clean_username(self):
'''
Username validation, requires length of five chars and unique.
'''
if len(self.cleaned_data['username']) < 5:
raise forms.ValidationError(
_(u'Username needs to have at least five characters.')
)
super(RegistrationForm, self).__init__(*args, **kwargs)
# Change labels to match Weblate language
self.fields['username'].label = \
_('Username')
self.fields['username'].help_text = \
_('At least five characters long.')
self.fields['email'].label = \
_('E-mail')
self.fields['email'].help_text = \
_('Activation email will be sent here.')
self.fields['password1'].label = \
_('Password')
self.fields['password1'].help_text = \
_('At least six characters long.')
self.fields['password2'].label = \
_('Password (again)')
self.fields['password2'].help_text = \
_(
'Repeat the password so we can verify '
'you typed it in correctly.'
existing = User.objects.filter(
username__iexact=self.cleaned_data['username']
)
if existing.exists():
raise forms.ValidationError(
_("A user with that username already exists.")
)
else:
return self.cleaned_data['username']
def clean_email(self):
"""
Validate that the supplied email address is unique for the
site.
"""
if User.objects.filter(email__iexact=self.cleaned_data['email']):
raise forms.ValidationError(
_(
"This email address is already in use. "
"Please supply a different email address."
)
)
return self.cleaned_data['email']
def clean_password1(self):
'''
......@@ -254,16 +280,6 @@ class RegistrationForm(RegistrationFormUniqueEmail):
)
return self.cleaned_data['password1']
def clean_username(self):
'''
Username validation, requires length of five chars.
'''
if len(self.cleaned_data['username']) < 5:
raise forms.ValidationError(
_(u'Username needs to have at least five characters.')
)
return super(RegistrationForm, self).clean_username()
def clean_content(self):
'''
Check if content is empty.
......@@ -271,3 +287,44 @@ class RegistrationForm(RegistrationFormUniqueEmail):
if self.cleaned_data['content'] != '':
raise forms.ValidationError('Invalid value')
return ''
def clean(self):
"""
Verifiy that the values entered into the two password fields
match. Note that an error here will end up in
``non_field_errors()`` because it doesn't apply to a single
field.
"""
try:
password1 = self.cleaned_data['password1']
password2 = self.cleaned_data['password2']
if password1 != password2:
raise forms.ValidationError(
_("The two password fields didn't match.")
)
except KeyError:
pass
return self.cleaned_data
def save(self):
'''
Creates user.
'''
user = User.objects.create(
email=self.cleaned_data['email'],
username=self.cleaned_data['username'],
first_name=self.cleaned_data['first_name'],
last_name=self.cleaned_data['last_name'],
is_active=False,
)
user.set_password(
self.cleaned_data['password1']
)
user.save()
# Ensure user has profile
Profile.objects.get_or_create(user=user)
return user
......@@ -29,7 +29,6 @@ from django.contrib.auth.models import (
Group, User, UNUSABLE_PASSWORD, Permission
)
from django.db.models.signals import post_syncdb
from registration.signals import user_registered
from django.contrib.sites.models import Site
from django.utils import translation as django_translation
from django.template.loader import render_to_string
......@@ -166,8 +165,9 @@ def notify_new_comment(unit, comment, user, report_source_bugs):
)
def send_notification_email(language, email, notification, translation_obj,
context=None, headers=None, user=None):
def send_notification_email(language, email, notification,
translation_obj=None, context=None, headers=None,
user=None, info=None):
'''
Renders and sends notification email.
'''
......@@ -177,15 +177,18 @@ def send_notification_email(language, email, notification, translation_obj,
if headers is None:
headers = {}
try:
if info is None:
info = translation_obj.__unicode__()
weblate.logger.info(
'sending notification %s on %s to %s',
notification,
translation_obj.__unicode__(),
info,
email
)
# Load user language
django_translation.activate(language)
if language is not None:
django_translation.activate(language)
# Template names
subject_template = 'mail/%s_subject.txt' % notification
......@@ -194,12 +197,13 @@ def send_notification_email(language, email, notification, translation_obj,
# Adjust context
site = Site.objects.get_current()
context['translation'] = translation_obj
context['current_site'] = site.domain
context['site'] = site
context['translation_url'] = get_site_url(
translation_obj.get_absolute_url()
)
if translation_obj is not None:
context['translation'] = translation_obj
context['translation_url'] = get_site_url(
translation_obj.get_absolute_url()
)
context['subject_template'] = subject_template
# Render subject
......@@ -674,19 +678,6 @@ def sync_create_groups(sender, app, **kwargs):
create_groups(False)
@receiver(user_registered)
def store_user_details(sender, user, request, **kwargs):
'''
Stores user details on registration and creates user profile. We rely on
validation done by RegistrationForm.
'''
user.first_name = request.POST['first_name']
user.last_name = request.POST['last_name']
user.save()
# Ensure user has profile
Profile.objects.get_or_create(user=user)
@receiver(post_save, sender=User)
def create_profile_callback(sender, **kwargs):
'''
......
......@@ -19,9 +19,14 @@
#
from django.shortcuts import redirect
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from social.pipeline.partial import partial
from social.exceptions import AuthException
from social.exceptions import AuthException, AuthForbidden
from accounts.models import send_notification_email
from weblate import appsettings
@partial
......@@ -45,10 +50,44 @@ def user_password(strategy, user, is_new=False, *args, **kwargs):
if strategy.backend_name != 'email':
return
password = strategy.request_data()['password']
request = strategy.request_data()
if ('password' not in request
or not user.check_password(request['password'])):
if is_new:
user.set_password(password)
user.is_active = True
user.save()
elif not user.check_password(password):
raise AuthException(strategy.backend)
raise AuthException(
strategy.backend,
_('Activation completed, you can now login.')
)
def send_validation(strategy, code):
'''
Sends verification email.
'''
url = '%s?verification_code=%s' % (
reverse('social:complete', args=(strategy.backend_name,)),
code.code
)
send_notification_email(
None,
code.email,
'activation',
info=code.code,
context={
'url': url
}
)
def verify_open(strategy, user, *args, **kwargs):
'''
Checks whether it is possible to create new user.
'''
if not user and not appsettings.REGISTRATION_OPEN:
raise AuthForbidden(strategy.backend)
......@@ -57,7 +57,7 @@ REGISTRATION_DATA = {
class RegistrationTest(TestCase):
def test_register(self):
response = self.client.post(
reverse('registration_register'),
reverse('register'),
REGISTRATION_DATA
)
# Check we did succeed
......@@ -94,7 +94,7 @@ class RegistrationTest(TestCase):
data = REGISTRATION_DATA.copy()
data['username'] = 'u'
response = self.client.post(
reverse('registration_register'),
reverse('register'),
data
)
self.assertContains(
......@@ -106,7 +106,7 @@ class RegistrationTest(TestCase):
data = REGISTRATION_DATA.copy()
data['email'] = 'x'
response = self.client.post(
reverse('registration_register'),
reverse('register'),
data
)
self.assertContains(
......@@ -123,7 +123,7 @@ class RegistrationTest(TestCase):
data = REGISTRATION_DATA.copy()
data['content'] = 'x'
response = self.client.post(
reverse('registration_register'),
reverse('register'),
data
)
self.assertContains(
......
......@@ -25,27 +25,11 @@ from django.conf import settings
from registration.views import activate, register
from accounts.forms import RegistrationForm
from accounts.views import RegistrationTemplateView
urlpatterns = patterns(
'',
url(
r'^register/$', register, {
'backend': 'registration.backends.default.DefaultBackend',
'form_class': RegistrationForm,
'extra_context': {'title': _('User registration')}
},
name='registration_register'
),
url(
r'^register/complete/$',
RegistrationTemplateView.as_view(
template_name='registration/registration_complete.html'
),
name='registration_complete'
),
url(
r'^register/closed/$',
RegistrationTemplateView.as_view(
......@@ -118,7 +102,15 @@ urlpatterns = patterns(
name='auth_password_reset_done'
),
url(
r'^email-sent/$',
RegistrationTemplateView.as_view(
template_name='registration/registration_complete.html'
),
name='email-sent'
),
url(r'^profile/', 'accounts.views.user_profile', name='profile'),
url(r'^login/$', 'accounts.views.weblate_login', name='login'),
url(r'^register/$', 'accounts.views.register', name='register'),
url(r'', include('social.apps.django_app.urls', namespace='social')),
)
......@@ -31,14 +31,17 @@ from django.contrib.auth.views import login
from django.views.generic import TemplateView
from urllib import urlencode
from accounts.forms import RegistrationForm
from social.backends.utils import load_backends
from social.apps.django_app.utils import BACKENDS
from social.apps.django_app.views import complete
from accounts.models import set_lang, Profile
from trans.models import Change, Project
from accounts.forms import (
ProfileForm, SubscriptionForm, UserForm, ContactForm
)
from weblate import appsettings
class RegistrationTemplateView(TemplateView):
......@@ -225,6 +228,9 @@ def weblate_login(request):
if request.user.is_authenticated():
return redirect('profile')
if 'message' in request.GET:
messages.info(request, request.GET['message'])
return login(
request,
template_name='registration/login.html',
......@@ -235,3 +241,30 @@ def weblate_login(request):
'title': _('Login'),
}
)
def register(request):
'''
Registration form.
'''
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid() and appsettings.REGISTRATION_OPEN:
request.user = form.save()
return complete(request, 'email')
else:
form = RegistrationForm()
return render_to_response(
'registration/registration_form.html',
RequestContext(
request,
{
'registration_backends': [
x for x in load_backends(BACKENDS).keys() if x != 'email'
],
'title': _('User registration'),
'form': form,
}
)
)
......@@ -52,5 +52,5 @@ def weblate_context(request):
'mt_enabled': appsettings.MACHINE_TRANSLATION_ENABLED,
'hooks_enabled': appsettings.ENABLE_HOOKS,
'registration_open': getattr(settings, 'REGISTRATION_OPEN', True),
'registration_open': appsettings.REGISTRATION_OPEN,
}
......@@ -145,3 +145,6 @@ TTF_PATH = get('TTF_PATH', os.path.join(WEB_ROOT, 'ttf'))
# Anonymous user name
ANONYMOUS_USER_NAME = get('ANONYMOUS_USER_NAME', 'anonymous')
# Enable registrations
REGISTRATION_OPEN = get('REGISTRATION_OPEN', True)
......@@ -39,7 +39,7 @@ class WeblateExceptionReporterFilter(SafeExceptionReporterFilter):
else:
lang = None
if request.user.is_authenticated():
if hasattr(request, 'user') and request.user.is_authenticated():
user = request.user.username
else:
user = None
......
......@@ -41,7 +41,7 @@
<li><a class="button" href="{% url 'auth_logout' %}">{% trans "Logout" %}</a></li>
{% else %}
{% if registration_open %}
<li><a class="button" href="{% url 'registration_register' %}">{% trans "Register" %}</a></li>
<li><a class="button" href="{% url 'register' %}">{% trans "Register" %}</a></li>
{% endif %}
<li><a class="button" href="{% url 'login' %}{% if not skip_next %}?next={{ current_url }}{% endif %}">{% trans "Login" %}</a></li>
{% endif %}
......
......@@ -34,7 +34,7 @@
{{ status_text }}
</p>
<p>
{% url 'registration_register' as reg_url %}
{% url 'register' as reg_url %}
{% blocktrans %}If you would like to contribute to translation of {{ project }}, you need to <a href="{{ reg_url }}">register on this server</a>.{% endblocktrans %}
</p>
<p>
......
{% extends "mail/base.html" %}
{% load url from future %}
{% load i18n %}{% load translations %}
{% block content %}
<p>
{% trans "Hi,"%}
</p>
<p>
{% blocktrans with site|site_title as site_title %}This is an automatic email to help you complete your registration with {{ site_title }}.{% endblocktrans %}
</p>
<p>
{% blocktrans %}Please open the following link in your web browser. If the link is split over several lines, you may need to copy it in the address bar.{% endblocktrans %}
</p>
<p>
<a href="http://{{ site.domain }}{{ url }}">http://{{ site.domain }}{{ url }}</a>
</p>
<p>
{% blocktrans %}If there is a problem with your registration, please contact us:{% endblocktrans %}
</p>
<p>
<a href="http://{{ site.domain }}{% url 'contact' %}?subject=Registration">http://{{ site.domain }}{% url 'contact' %}?subject=Registration</a>
</p>
{% endblock %}
......@@ -4,7 +4,7 @@
{% blocktrans %}Please open the following link in your web browser. If the link is split over several lines, you may need to copy it in the address bar.{% endblocktrans %}
http://{{ site.domain }}{% url 'registration.views.activate' activation_key=activation_key %}
http://{{ site.domain }}{{ url }}
{% blocktrans %}If there is a problem with your registration, please contact us:{% endblocktrans %}
......
......@@ -7,7 +7,7 @@
<h2>{% trans "Your account could not be activated" %}</h2>
<p>
{% blocktrans count expiration_days as days %}This may be because it is already active or because you waited over {{ days }} day to activate it.{% plural %}This may be because it is already active or because you waited over {{ days }} days to activate it.{% endblocktrans %}
{% url 'registration_register' as reg_url %}
{% url 'register' as reg_url %}
{% url 'contact' as contact_url %}
{% blocktrans %}If this is not the case, please <a href="{{ contact_url }}">contact</a> the website administrator. Otherwise, you may <a href="{{ reg_url }}">register again</a>.{% endblocktrans %}
</p>
......
......@@ -4,6 +4,8 @@
{% block content %}
{% if registration_open %}
{% if form.errors %}
<div class="ui-widget">
<div style="padding: 0pt 0.7em;" class="ui-state-error ui-corner-all">
......@@ -14,7 +16,7 @@
</div>
{% endif %}
<form action="{% url 'registration_register' %}" method="post" accept-charset="utf-8">
<form action="{% url 'register' %}" method="post" accept-charset="utf-8">
{% csrf_token %}
<table>
{{ form.as_table }}
......@@ -25,5 +27,12 @@
<p><input type="submit" value="{% trans 'Register' %}" class="button" /></p>
</form>
{% else %}
<p>
{% trans "Sorry but this site is not open for registrations." %}
</p>
{% endif %}
{% endblock %}
......@@ -189,6 +189,8 @@ SOCIAL_AUTH_PIPELINE = (
'social.pipeline.user.get_username',
'accounts.pipeline.require_email',
'social.pipeline.mail.mail_validation',
'social.pipeline.social_auth.associate_by_email',
'accounts.pipeline.verify_open',
'social.pipeline.user.create_user',
'accounts.pipeline.user_password',
'social.pipeline.social_auth.associate_user',
......@@ -196,6 +198,11 @@ SOCIAL_AUTH_PIPELINE = (
'social.pipeline.user.user_details'
)
SOCIAL_AUTH_EMAIL_VALIDATION_FUNCTION = 'accounts.pipeline.send_validation'
SOCIAL_AUTH_EMAIL_VALIDATION_URL = '%s/accounts/email-sent/' % URL_PREFIX
SOCIAL_AUTH_LOGIN_ERROR_URL = '%s/accounts/login/' % URL_PREFIX
# Middleware
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
......@@ -204,6 +211,7 @@ MIDDLEWARE_CLASSES = (
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'social.apps.django_app.middleware.SocialAuthExceptionMiddleware',
'accounts.middleware.RequireLoginMiddleware',
)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment