From e8d23004738a3fb92d825f8299a34e6623b8cc7c Mon Sep 17 00:00:00 2001 From: plt Date: Sat, 30 Apr 2011 13:18:32 +0800 Subject: [PATCH] Add authentication and registration to seahub --- {basic => base}/__init__.py | 0 base/accounts.py | 21 + {basic => base}/models.py | 0 {basic => base}/tests.py | 0 {basic => base}/views.py | 0 media/avatars/default.png | Bin 0 -> 7449 bytes media/css/seahub.css | 5 +- profile/__init__.py | 0 profile/admin.py | 4 + profile/forms.py | 8 + profile/models.py | 8 + profile/templates/profile/profile.html | 41 ++ profile/templates/profile/profile_base.html | 7 + profile/templates/profile/set_profile.html | 20 + profile/tests.py | 23 ++ profile/urls.py | 6 + profile/views.py | 47 +++ setenv.sh.template | 2 + settings.py | 27 +- templates/accounts.html | 3 + templates/avatar/base.html | 3 + templates/avatar/change.html | 31 ++ templates/avatar/confirm_delete.html | 18 + templates/base.html | 18 + templates/myhome.html | 1 + templates/myhome_base.html | 17 + .../registration/activation_complete.html | 8 + templates/registration/activation_email.txt | 4 + .../registration/activation_email_subject.txt | 1 + templates/registration/login.html | 30 ++ templates/registration/logout.html | 11 + .../registration/password_change_done.html | 14 + .../registration/password_change_form.html | 17 + .../registration/password_reset_complete.html | 16 + .../registration/password_reset_confirm.html | 32 ++ .../registration/password_reset_done.html | 14 + .../registration/password_reset_email.html | 15 + .../registration/password_reset_form.html | 14 + .../registration/registration_closed.html | 8 + .../registration/registration_complete.html | 6 + templates/registration/registration_form.html | 26 ++ thirdpart/avatar/__init__.py | 27 ++ thirdpart/avatar/admin.py | 4 + thirdpart/avatar/default.jpg | Bin 0 -> 3431 bytes thirdpart/avatar/forms.py | 31 ++ thirdpart/avatar/management/__init__.py | 14 + .../avatar/management/commands/__init__.py | 1 + .../management/commands/rebuild_avatars.py | 15 + thirdpart/avatar/models.py | 79 ++++ thirdpart/avatar/templates/avatar/base.html | 10 + thirdpart/avatar/templates/avatar/change.html | 21 + .../templates/avatar/confirm_delete.html | 15 + .../avatar_friend_updated/full.txt | 4 + .../avatar_friend_updated/notice.html | 2 + .../notifications/avatar_updated/full.txt | 4 + .../notifications/avatar_updated/notice.html | 2 + thirdpart/avatar/templatetags/__init__.py | 0 thirdpart/avatar/templatetags/avatar_tags.py | 63 +++ thirdpart/avatar/urls.py | 6 + thirdpart/avatar/views.py | 129 +++++++ thirdpart/registration/__init__.py | 12 + thirdpart/registration/admin.py | 46 +++ thirdpart/registration/auth_urls.py | 58 +++ thirdpart/registration/backends/__init__.py | 32 ++ .../registration/backends/default/__init__.py | 139 +++++++ .../registration/backends/default/urls.py | 54 +++ thirdpart/registration/forms.py | 131 +++++++ .../locale/ar/LC_MESSAGES/django.mo | Bin 0 -> 2135 bytes .../locale/ar/LC_MESSAGES/django.po | 81 ++++ .../locale/bg/LC_MESSAGES/django.mo | Bin 0 -> 2302 bytes .../locale/bg/LC_MESSAGES/django.po | 78 ++++ .../locale/da/LC_MESSAGES/django.mo | Bin 0 -> 1803 bytes .../locale/da/LC_MESSAGES/django.po | 92 +++++ .../locale/de/LC_MESSAGES/django.mo | Bin 0 -> 1999 bytes .../locale/de/LC_MESSAGES/django.po | 93 +++++ .../locale/el/LC_MESSAGES/django.mo | Bin 0 -> 2424 bytes .../locale/el/LC_MESSAGES/django.po | 84 ++++ .../locale/en/LC_MESSAGES/django.mo | Bin 0 -> 367 bytes .../locale/en/LC_MESSAGES/django.po | 89 +++++ .../locale/es/LC_MESSAGES/django.mo | Bin 0 -> 1909 bytes .../locale/es/LC_MESSAGES/django.po | 85 +++++ .../locale/es_AR/LC_MESSAGES/django.mo | Bin 0 -> 1849 bytes .../locale/es_AR/LC_MESSAGES/django.po | 83 ++++ .../locale/fr/LC_MESSAGES/django.mo | Bin 0 -> 1883 bytes .../locale/fr/LC_MESSAGES/django.po | 81 ++++ .../locale/he/LC_MESSAGES/django.mo | Bin 0 -> 1896 bytes .../locale/he/LC_MESSAGES/django.po | 86 +++++ .../locale/is/LC_MESSAGES/django.mo | Bin 0 -> 1476 bytes .../locale/is/LC_MESSAGES/django.po | 74 ++++ .../locale/it/LC_MESSAGES/django.mo | Bin 0 -> 1864 bytes .../locale/it/LC_MESSAGES/django.po | 82 ++++ .../locale/ja/LC_MESSAGES/django.mo | Bin 0 -> 2035 bytes .../locale/ja/LC_MESSAGES/django.po | 78 ++++ .../locale/ko/LC_MESSAGES/django.mo | Bin 0 -> 1947 bytes .../locale/ko/LC_MESSAGES/django.po | 89 +++++ .../locale/nl/LC_MESSAGES/django.mo | Bin 0 -> 1898 bytes .../locale/nl/LC_MESSAGES/django.po | 77 ++++ .../locale/pl/LC_MESSAGES/django.mo | Bin 0 -> 1769 bytes .../locale/pl/LC_MESSAGES/django.po | 84 ++++ .../locale/pt_BR/LC_MESSAGES/django.mo | Bin 0 -> 1796 bytes .../locale/pt_BR/LC_MESSAGES/django.po | 81 ++++ .../locale/ru/LC_MESSAGES/django.mo | Bin 0 -> 2470 bytes .../locale/ru/LC_MESSAGES/django.po | 92 +++++ .../locale/sl/LC_MESSAGES/django.mo | Bin 0 -> 1903 bytes .../locale/sl/LC_MESSAGES/django.po | 87 +++++ .../locale/sr/LC_MESSAGES/django.mo | Bin 0 -> 1966 bytes .../locale/sr/LC_MESSAGES/django.po | 80 ++++ .../locale/sv/LC_MESSAGES/django.mo | Bin 0 -> 1687 bytes .../locale/sv/LC_MESSAGES/django.po | 81 ++++ .../locale/zh_CN/LC_MESSAGES/django.mo | Bin 0 -> 1669 bytes .../locale/zh_CN/LC_MESSAGES/django.po | 77 ++++ .../locale/zh_TW/LC_MESSAGES/django.mo | Bin 0 -> 1669 bytes .../locale/zh_TW/LC_MESSAGES/django.po | 77 ++++ thirdpart/registration/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../commands/cleanupregistration.py | 19 + thirdpart/registration/models.py | 258 +++++++++++++ thirdpart/registration/signals.py | 8 + thirdpart/registration/tests/__init__.py | 4 + thirdpart/registration/tests/backends.py | 361 ++++++++++++++++++ thirdpart/registration/tests/forms.py | 119 ++++++ thirdpart/registration/tests/models.py | 225 +++++++++++ thirdpart/registration/tests/urls.py | 82 ++++ thirdpart/registration/tests/views.py | 246 ++++++++++++ thirdpart/registration/urls.py | 15 + thirdpart/registration/views.py | 204 ++++++++++ urls.py | 14 +- views.py | 8 + 128 files changed, 4893 insertions(+), 6 deletions(-) rename {basic => base}/__init__.py (100%) create mode 100644 base/accounts.py rename {basic => base}/models.py (100%) rename {basic => base}/tests.py (100%) rename {basic => base}/views.py (100%) create mode 100644 media/avatars/default.png create mode 100644 profile/__init__.py create mode 100644 profile/admin.py create mode 100644 profile/forms.py create mode 100644 profile/models.py create mode 100644 profile/templates/profile/profile.html create mode 100644 profile/templates/profile/profile_base.html create mode 100644 profile/templates/profile/set_profile.html create mode 100644 profile/tests.py create mode 100644 profile/urls.py create mode 100644 profile/views.py create mode 100644 setenv.sh.template create mode 100644 templates/accounts.html create mode 100644 templates/avatar/base.html create mode 100644 templates/avatar/change.html create mode 100644 templates/avatar/confirm_delete.html create mode 100644 templates/myhome.html create mode 100644 templates/myhome_base.html create mode 100644 templates/registration/activation_complete.html create mode 100644 templates/registration/activation_email.txt create mode 100644 templates/registration/activation_email_subject.txt create mode 100644 templates/registration/login.html create mode 100644 templates/registration/logout.html create mode 100644 templates/registration/password_change_done.html create mode 100644 templates/registration/password_change_form.html create mode 100644 templates/registration/password_reset_complete.html create mode 100644 templates/registration/password_reset_confirm.html create mode 100644 templates/registration/password_reset_done.html create mode 100644 templates/registration/password_reset_email.html create mode 100644 templates/registration/password_reset_form.html create mode 100644 templates/registration/registration_closed.html create mode 100644 templates/registration/registration_complete.html create mode 100644 templates/registration/registration_form.html create mode 100644 thirdpart/avatar/__init__.py create mode 100644 thirdpart/avatar/admin.py create mode 100644 thirdpart/avatar/default.jpg create mode 100644 thirdpart/avatar/forms.py create mode 100644 thirdpart/avatar/management/__init__.py create mode 100644 thirdpart/avatar/management/commands/__init__.py create mode 100644 thirdpart/avatar/management/commands/rebuild_avatars.py create mode 100644 thirdpart/avatar/models.py create mode 100644 thirdpart/avatar/templates/avatar/base.html create mode 100644 thirdpart/avatar/templates/avatar/change.html create mode 100644 thirdpart/avatar/templates/avatar/confirm_delete.html create mode 100644 thirdpart/avatar/templates/notifications/avatar_friend_updated/full.txt create mode 100644 thirdpart/avatar/templates/notifications/avatar_friend_updated/notice.html create mode 100644 thirdpart/avatar/templates/notifications/avatar_updated/full.txt create mode 100644 thirdpart/avatar/templates/notifications/avatar_updated/notice.html create mode 100644 thirdpart/avatar/templatetags/__init__.py create mode 100644 thirdpart/avatar/templatetags/avatar_tags.py create mode 100644 thirdpart/avatar/urls.py create mode 100644 thirdpart/avatar/views.py create mode 100644 thirdpart/registration/__init__.py create mode 100644 thirdpart/registration/admin.py create mode 100644 thirdpart/registration/auth_urls.py create mode 100644 thirdpart/registration/backends/__init__.py create mode 100644 thirdpart/registration/backends/default/__init__.py create mode 100644 thirdpart/registration/backends/default/urls.py create mode 100644 thirdpart/registration/forms.py create mode 100644 thirdpart/registration/locale/ar/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/ar/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/bg/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/bg/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/da/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/da/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/de/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/de/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/el/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/el/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/en/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/en/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/es/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/es/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/es_AR/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/es_AR/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/fr/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/fr/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/he/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/he/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/is/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/is/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/it/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/it/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/ja/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/ja/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/ko/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/ko/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/nl/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/nl/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/pl/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/pl/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/pt_BR/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/pt_BR/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/ru/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/ru/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/sl/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/sl/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/sr/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/sr/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/sv/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/sv/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/zh_CN/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/zh_CN/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/locale/zh_TW/LC_MESSAGES/django.mo create mode 100644 thirdpart/registration/locale/zh_TW/LC_MESSAGES/django.po create mode 100644 thirdpart/registration/management/__init__.py create mode 100644 thirdpart/registration/management/commands/__init__.py create mode 100644 thirdpart/registration/management/commands/cleanupregistration.py create mode 100644 thirdpart/registration/models.py create mode 100644 thirdpart/registration/signals.py create mode 100644 thirdpart/registration/tests/__init__.py create mode 100644 thirdpart/registration/tests/backends.py create mode 100644 thirdpart/registration/tests/forms.py create mode 100644 thirdpart/registration/tests/models.py create mode 100644 thirdpart/registration/tests/urls.py create mode 100644 thirdpart/registration/tests/views.py create mode 100644 thirdpart/registration/urls.py create mode 100644 thirdpart/registration/views.py diff --git a/basic/__init__.py b/base/__init__.py similarity index 100% rename from basic/__init__.py rename to base/__init__.py diff --git a/base/accounts.py b/base/accounts.py new file mode 100644 index 0000000000..e6d96fcb76 --- /dev/null +++ b/base/accounts.py @@ -0,0 +1,21 @@ +from django.conf import settings +from django.contrib.auth.models import User + +class EmailOrUsernameModelBackend(object): + def authenticate(self, username=None, password=None): + if '@' in username: + kwargs = {'email': username} + else: + kwargs = {'username': username} + try: + user = User.objects.get(**kwargs) + if user.check_password(password): + return user + except User.DoesNotExist: + return None + + def get_user(self, user_id): + try: + return User.objects.get(pk=user_id) + except User.DoesNotExist: + return None diff --git a/basic/models.py b/base/models.py similarity index 100% rename from basic/models.py rename to base/models.py diff --git a/basic/tests.py b/base/tests.py similarity index 100% rename from basic/tests.py rename to base/tests.py diff --git a/basic/views.py b/base/views.py similarity index 100% rename from basic/views.py rename to base/views.py diff --git a/media/avatars/default.png b/media/avatars/default.png new file mode 100644 index 0000000000000000000000000000000000000000..cce2cb3debb118bbef73d0ec535b8d2665f4b875 GIT binary patch literal 7449 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 XS9wFF^Y`Tm00000NkvXXu0mjfYJDoD literal 0 HcmV?d00001 diff --git a/media/css/seahub.css b/media/css/seahub.css index eb2da09670..a4647b446d 100644 --- a/media/css/seahub.css +++ b/media/css/seahub.css @@ -29,6 +29,9 @@ table.default tr.first { background-color: #00FF00; } #right-panel { float:right; width:800px; } #footer { color:#999; padding-top:2px; margin:25px 0; border-top:1px solid #DDD; } /* #header */ +#header .top_operation { float:right; margin:2px 10px 0 0; color:#ddd; font-size:13px; } +#header .top_operation a { font-weight:normal; font-size:13px; margin-left:2px; text-decoration: none; } +#header .top_operation a:hover { background:#0A0; color:#FFF; } #logo { margin-top: 4px; } #header #ident { margin-top: 8px; float: right; font-size: 12px; } #header #ident p { color: #808; } @@ -42,7 +45,7 @@ div.nav-separator { clear: both; border-top: #FF8C00 1px solid; border-bottom: n /* footer */ #footer a { color:#333; text-decoration:none; } /* main */ -h2 { font-size:16px; color:#292; padding:4px 0 2px 0px; border-bottom: 2px solid #2B2; background-position:left 22px; margin:10px 0; } +h2 { font-size:16px; color:#292; padding:4px 0 2px 0px; margin:10px 0; } h3 { font-size: 14px; color: #808; margin: 10px 0px 4px 0; } p { line-height: 22px; } ol { margin:0; padding:0px 0 0 2em; list-style-position:inside; } diff --git a/profile/__init__.py b/profile/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/profile/admin.py b/profile/admin.py new file mode 100644 index 0000000000..7a7ad257fa --- /dev/null +++ b/profile/admin.py @@ -0,0 +1,4 @@ +from django.contrib import admin +from profile.models import UserProfile + +admin.site.register(UserProfile) \ No newline at end of file diff --git a/profile/forms.py b/profile/forms.py new file mode 100644 index 0000000000..905af9403e --- /dev/null +++ b/profile/forms.py @@ -0,0 +1,8 @@ +from django import forms + +class SetUserProfileForm(forms.Form): + + status = forms.CharField(max_length=140, required=False) + interests = forms.CharField(max_length=256, required=False) + + #signature = forms.CharField() diff --git a/profile/models.py b/profile/models.py new file mode 100644 index 0000000000..b220667fca --- /dev/null +++ b/profile/models.py @@ -0,0 +1,8 @@ +from django.db import models +from django.contrib.auth.models import User + + +class UserProfile(models.Model): + user = models.ForeignKey(User, unique=True) + status = models.CharField(max_length=40, blank=True) + diff --git a/profile/templates/profile/profile.html b/profile/templates/profile/profile.html new file mode 100644 index 0000000000..0e705bcc97 --- /dev/null +++ b/profile/templates/profile/profile.html @@ -0,0 +1,41 @@ +{% extends "profile/profile_base.html" %} + + +{% block left_panel %} + +{% endblock %} + + +{% block right_panel %} + +

当前头像

+{% load avatar_tags %} +{% avatar user 90 %} + +

基本信息

+

当前状态:{{ user.get_profile.status }}

+

我的兴趣:{{ user.get_profile.interests }}

+ +{% if peer %} +

Ccnet 当前设置

+

Ccnet ID: {{ peer.peer_id }}

+

Ccnet Name: {{ peer.name }}

+{% endif %} + +{% if groups %} +

加入的组

+
+
    + {% for group in groups %} +
  • {{ group.name }}
  • + {% endfor %} +
+
+{% endif %} + +{% endblock %} diff --git a/profile/templates/profile/profile_base.html b/profile/templates/profile/profile_base.html new file mode 100644 index 0000000000..a6bdda9cf7 --- /dev/null +++ b/profile/templates/profile/profile_base.html @@ -0,0 +1,7 @@ +{% extends "myhome_base.html" %} + +{% block title %}帐号设置{% endblock %} + +{% block subnav %} + +{% endblock %} diff --git a/profile/templates/profile/set_profile.html b/profile/templates/profile/set_profile.html new file mode 100644 index 0000000000..aec8765041 --- /dev/null +++ b/profile/templates/profile/set_profile.html @@ -0,0 +1,20 @@ +{% extends "profile/profile_base.html" %} + + +{% block content %} + +
+ 当前状态:
+ {{ profile_form.status }} + {% if profile_form.status.errors %} + {{ profile_form.status.errors }} + {% endif %}
+
+ {{ profile_form.interests }}
+ {% if profile_form.interests.errors %} + {{ profile_form.interests.errors }} + {% endif %}
+ + + +{% endblock %} diff --git a/profile/tests.py b/profile/tests.py new file mode 100644 index 0000000000..2247054b35 --- /dev/null +++ b/profile/tests.py @@ -0,0 +1,23 @@ +""" +This file demonstrates two different styles of tests (one doctest and one +unittest). These will both pass when you run "manage.py test". + +Replace these with more appropriate tests for your application. +""" + +from django.test import TestCase + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.failUnlessEqual(1 + 1, 2) + +__test__ = {"doctest": """ +Another way to test that 1 + 1 is equal to 2. + +>>> 1 + 1 == 2 +True +"""} + diff --git a/profile/urls.py b/profile/urls.py new file mode 100644 index 0000000000..9ac40c3832 --- /dev/null +++ b/profile/urls.py @@ -0,0 +1,6 @@ +from django.conf.urls.defaults import * + +urlpatterns = patterns('profile.views', + url(r'^$', 'show_profile'), + url(r'^edit/$', 'set_profile', name="profile_setting"), +) diff --git a/profile/views.py b/profile/views.py new file mode 100644 index 0000000000..1daa5f00e8 --- /dev/null +++ b/profile/views.py @@ -0,0 +1,47 @@ +from django.http import HttpResponse, HttpResponseRedirect +from django.shortcuts import render_to_response, get_object_or_404 +from django.template.loader import get_template +from django.template import Context, RequestContext +from django.contrib.auth.decorators import login_required +from django.core.urlresolvers import reverse + +import datetime + +from forms import SetUserProfileForm +from models import UserProfile + + +@login_required +def show_profile(request): + groups = [] + return render_to_response('profile/profile.html', + { 'groups': groups, }, + context_instance=RequestContext(request)) + + +@login_required +def set_profile(request): + if request.method == 'POST': + form = SetUserProfileForm(request.POST) + if form.is_valid(): + try: + profile = request.user.get_profile() + except UserProfile.DoesNotExist: + profile = UserProfile(user=request.user) + profile.save() # save the profile first, otherwise + # status.save() would fail. + + profile.save() + return HttpResponseRedirect(reverse(show_profile)) + else: + try: + profile = request.user.get_profile() + except UserProfile.DoesNotExist: + profile = UserProfile(user=request.user) + + profile_form = SetUserProfileForm(profile.__dict__) + + return render_to_response('profile/set_profile.html', + { 'profile_form': profile_form, }, + context_instance=RequestContext(request)) + diff --git a/setenv.sh.template b/setenv.sh.template new file mode 100644 index 0000000000..802b80b08d --- /dev/null +++ b/setenv.sh.template @@ -0,0 +1,2 @@ +export CCNET_CONF_DIR=/home/plt/dev/ccnet/seafile/tests/basic/conf1 +export PYTHONPATH=/opt/lib/python2.6/site-packages:thirdpart diff --git a/settings.py b/settings.py index c5e55c5096..ba869146d4 100644 --- a/settings.py +++ b/settings.py @@ -62,6 +62,8 @@ TEMPLATE_LOADERS = ( MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.middleware.csrf.CsrfResponseMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', ) @@ -94,9 +96,32 @@ INSTALLED_APPS = ( 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', - 'seahub.basic', + 'django.contrib.admin', + 'registration', + 'avatar', + 'seahub.base', + 'seahub.profile', ) +AUTHENTICATION_BACKENDS = ( + 'seahub.base.accounts.EmailOrUsernameModelBackend', + 'django.contrib.auth.backends.ModelBackend' +) + +ACCOUNT_ACTIVATION_DAYS = 7 + +#avatar +AVATAR_STORAGE_DIR = 'avatars' + +AVATAR_GRAVATAR_BACKUP = False +AVATAR_DEFAULT_URL = MEDIA_URL + '/avatars/default.png' + +LOGIN_URL = SITE_ROOT + 'accounts/login' + +# profile +AUTH_PROFILE_MODULE = "profile.UserProfile" + + try: import local_settings except ImportError: diff --git a/templates/accounts.html b/templates/accounts.html new file mode 100644 index 0000000000..d05d592b44 --- /dev/null +++ b/templates/accounts.html @@ -0,0 +1,3 @@ +{% extends "myhome_base.html" %} + +{% block title %}帐号设置{% endblock %} diff --git a/templates/avatar/base.html b/templates/avatar/base.html new file mode 100644 index 0000000000..f15b3aac49 --- /dev/null +++ b/templates/avatar/base.html @@ -0,0 +1,3 @@ +{% extends "accounts.html" %} +{% block title %}个人头像{% endblock %} + diff --git a/templates/avatar/change.html b/templates/avatar/change.html new file mode 100644 index 0000000000..80a8bf3d87 --- /dev/null +++ b/templates/avatar/change.html @@ -0,0 +1,31 @@ +{% extends "avatar/base.html" %} +{% load avatar_tags %} + +{% block content %} +
+

添加或修改头像

+
+

当前头像:

+ {% avatar user %} +
+
+ {% if not avatars %} +

您还没有自己的头像。

+ {% else %} +

从已有头像中选择:

+
+
    + {{ primary_avatar_form.as_ul }} +
+
+ +
+ {% endif %} +

上传新头像:

+
+
+ +
+
+
+{% endblock %} diff --git a/templates/avatar/confirm_delete.html b/templates/avatar/confirm_delete.html new file mode 100644 index 0000000000..024b8dd02c --- /dev/null +++ b/templates/avatar/confirm_delete.html @@ -0,0 +1,18 @@ +{% extends "avatar/base.html" %} + +{% block content %} +
+

删除头像

+ {% if not avatars %} +

您还没有上传自己的头像。现在 上传一个.

+ {% else %} +
+
    + {{ delete_avatar_form.as_ul }} +
+
+ +
+ {% endif %} +
+{% endblock %} diff --git a/templates/base.html b/templates/base.html index b84711d47c..4343f103f3 100644 --- a/templates/base.html +++ b/templates/base.html @@ -15,8 +15,24 @@
@@ -39,6 +56,7 @@ {% block right_panel %}{% endblock %}
+ {% block content %}{% endblock %}
diff --git a/templates/myhome.html b/templates/myhome.html new file mode 100644 index 0000000000..b1cdd71c3f --- /dev/null +++ b/templates/myhome.html @@ -0,0 +1 @@ +{% extends "myhome_base.html" %} diff --git a/templates/myhome_base.html b/templates/myhome_base.html new file mode 100644 index 0000000000..794e1591dc --- /dev/null +++ b/templates/myhome_base.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} +{% block nav %} + +{% endblock %} diff --git a/templates/registration/activation_complete.html b/templates/registration/activation_complete.html new file mode 100644 index 0000000000..153023af82 --- /dev/null +++ b/templates/registration/activation_complete.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block title %}Create an account{% endblock %} + +{% block content %} +你的帐号已经激活。 登录 +{% endblock %} + diff --git a/templates/registration/activation_email.txt b/templates/registration/activation_email.txt new file mode 100644 index 0000000000..b85e5eead2 --- /dev/null +++ b/templates/registration/activation_email.txt @@ -0,0 +1,4 @@ +您好, 感谢注册 {{ site.name }}。 + +请点击下面的链接激活您的帐号 +http://{{ site.name }}{{ SITE_ROOT }}accounts/activate/{{ activation_key }}/ diff --git a/templates/registration/activation_email_subject.txt b/templates/registration/activation_email_subject.txt new file mode 100644 index 0000000000..111f8d7101 --- /dev/null +++ b/templates/registration/activation_email_subject.txt @@ -0,0 +1 @@ +请激活你的帐号,完成注册 diff --git a/templates/registration/login.html b/templates/registration/login.html new file mode 100644 index 0000000000..9131e2ee2a --- /dev/null +++ b/templates/registration/login.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} +{% block title %}用户登录{% endblock %} +{% block content %} +

用户登录

+ + +{% endblock %} + diff --git a/templates/registration/logout.html b/templates/registration/logout.html new file mode 100644 index 0000000000..24097da4cc --- /dev/null +++ b/templates/registration/logout.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} + +{% block title %}退出{% endblock %} + +{% block content %} + +

感谢参与.

+ +

+ +{% endblock %} diff --git a/templates/registration/password_change_done.html b/templates/registration/password_change_done.html new file mode 100644 index 0000000000..83b2ea63d8 --- /dev/null +++ b/templates/registration/password_change_done.html @@ -0,0 +1,14 @@ +{% extends "accounts.html" %} +{% load i18n %} + +{% block title %}{% trans 'Password change successful' %}{% endblock %} + +{% block content %} + +

{% trans 'Password change successful' %}

+ +

{% trans 'Your password was changed.' %}

+ +{% endblock %} diff --git a/templates/registration/password_change_form.html b/templates/registration/password_change_form.html new file mode 100644 index 0000000000..8182552893 --- /dev/null +++ b/templates/registration/password_change_form.html @@ -0,0 +1,17 @@ +{% extends "accounts.html" %} +{% load i18n %} +{% block title %}{% trans 'Password change' %}{% endblock %} +{% block content %} +
+

{% trans 'Password change' %}

+
+{{ form.old_password.errors }} +

{{ form.old_password }}

+{{ form.new_password1.errors }} +

{{ form.new_password1 }}

+{{ form.new_password2.errors }} +

{{ form.new_password2 }}

+

+
+
+{% endblock %} diff --git a/templates/registration/password_reset_complete.html b/templates/registration/password_reset_complete.html new file mode 100644 index 0000000000..5a2d7f8382 --- /dev/null +++ b/templates/registration/password_reset_complete.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block breadcrumbs %}{% endblock %} + +{% block title %}{% trans 'Password reset complete' %}{% endblock %} + +{% block content %} + +

{% trans 'Password reset complete' %}

+ +

{% trans "Your password has been set. You may go ahead and log in now." %}

+ +

{% trans 'Log in' %}

+ +{% endblock %} diff --git a/templates/registration/password_reset_confirm.html b/templates/registration/password_reset_confirm.html new file mode 100644 index 0000000000..b17ec504db --- /dev/null +++ b/templates/registration/password_reset_confirm.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block breadcrumbs %}{% endblock %} + +{% block title %}{% trans 'Password reset' %}{% endblock %} + +{% block content %} + +{% if validlink %} + +

{% trans 'Enter new password' %}

+ +

{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}

+ +
+{{ form.new_password1.errors }} +

{{ form.new_password1 }}

+{{ form.new_password2.errors }} +

{{ form.new_password2 }}

+

+
+ +{% else %} + +

{% trans 'Password reset unsuccessful' %}

+ +

{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %} + +{% endif %} + +{% endblock %} diff --git a/templates/registration/password_reset_done.html b/templates/registration/password_reset_done.html new file mode 100644 index 0000000000..9c0f440f05 --- /dev/null +++ b/templates/registration/password_reset_done.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block breadcrumbs %}

{% endblock %} + +{% block title %}{% trans 'Password reset successful' %}{% endblock %} + +{% block content %} + +

{% trans 'Password reset successful' %}

+ +

{% trans "We've e-mailed you instructions for setting your password to the e-mail address you submitted. You should be receiving it shortly." %}

+ +{% endblock %} diff --git a/templates/registration/password_reset_email.html b/templates/registration/password_reset_email.html new file mode 100644 index 0000000000..4e4bd6d1b2 --- /dev/null +++ b/templates/registration/password_reset_email.html @@ -0,0 +1,15 @@ +{% load i18n %}{% autoescape off %} +{% trans "You're receiving this e-mail because you requested a password reset" %} +{% blocktrans %}for your user account at {{ site_name }}{% endblocktrans %}. + +{% trans "Please go to the following page and choose a new password:" %} +{% block reset_link %} +{{ protocol }}://{{ domain }}{% url django.contrib.auth.views.password_reset_confirm uidb36=uid, token=token %} +{% endblock %} +{% trans "Your username, in case you've forgotten:" %} {{ user.username }} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} + +{% endautoescape %} diff --git a/templates/registration/password_reset_form.html b/templates/registration/password_reset_form.html new file mode 100644 index 0000000000..d43e87c47f --- /dev/null +++ b/templates/registration/password_reset_form.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block breadcrumbs %}{% endblock %} +{% block title %}{% trans "Password reset" %}{% endblock %} +{% block content %} +

{% trans "Password reset" %}

+

请在下面输入您的 e-mail 地址,我们会把新密码设置说明通过邮件发送给您。

+
+ {{ form.email }} + +{{ form.email.errors }} +
+{% endblock %} diff --git a/templates/registration/registration_closed.html b/templates/registration/registration_closed.html new file mode 100644 index 0000000000..12580ea1ca --- /dev/null +++ b/templates/registration/registration_closed.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block title %}Create an account{% endblock %} + +{% block content %} +Closed. +{% endblock %} + diff --git a/templates/registration/registration_complete.html b/templates/registration/registration_complete.html new file mode 100644 index 0000000000..0972d2edb2 --- /dev/null +++ b/templates/registration/registration_complete.html @@ -0,0 +1,6 @@ +{% extends "base.html" %} +{% block title %}感谢注册{% endblock %} +{% block content %} +

感谢注册,激活邮件已发往您的邮箱,请查收。如果您在收件箱里没找到,请检查下是否被当成垃圾邮件了。

+{% endblock %} + diff --git a/templates/registration/registration_form.html b/templates/registration/registration_form.html new file mode 100644 index 0000000000..027c365de8 --- /dev/null +++ b/templates/registration/registration_form.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} +{% block title %}用户注册{% endblock %} +{% block content %} +

用户注册

+
+ + {{ form.username }} + {% if form.username.errors %} + {{ form.username.errors }} + {% endif %}
+ + {{ form.email }}(我们将给您发送帐号激活邮件.) + {% if form.email.errors %} + {{ form.email.errors }} + {% endif %}
+ + {{ form.password1 }} + {% if form.password1.errors %} + {{ form.password1.errors }} + {% endif %}
+ + {{ form.password2 }}
+ +
+{% endblock %} + diff --git a/thirdpart/avatar/__init__.py b/thirdpart/avatar/__init__.py new file mode 100644 index 0000000000..e2337eaaba --- /dev/null +++ b/thirdpart/avatar/__init__.py @@ -0,0 +1,27 @@ +import os.path + +from django.conf import settings + +try: + from PIL import Image +except ImportError: + import Image + +AUTO_GENERATE_AVATAR_SIZES = getattr(settings, 'AUTO_GENERATE_AVATAR_SIZES', (80,)) +AVATAR_RESIZE_METHOD = getattr(settings, 'AVATAR_RESIZE_METHOD', Image.ANTIALIAS) +AVATAR_STORAGE_DIR = getattr(settings, 'AVATAR_STORAGE_DIR', 'avatars') +AVATAR_GRAVATAR_BACKUP = getattr(settings, 'AVATAR_GRAVATAR_BACKUP', True) +AVATAR_GRAVATAR_DEFAULT = getattr(settings, 'AVATAR_GRAVATAR_DEFAULT', None) +AVATAR_DEFAULT_URL = getattr(settings, 'AVATAR_DEFAULT_URL', + settings.MEDIA_URL + os.path.join(os.path.dirname(__file__), 'default.jpg')) + +from django.db.models import signals +from django.contrib.auth.models import User +from avatar.models import Avatar + + +def create_default_thumbnails(instance=None, created=False, **kwargs): + if created: + for size in AUTO_GENERATE_AVATAR_SIZES: + instance.create_thumbnail(size) +signals.post_save.connect(create_default_thumbnails, sender=Avatar) diff --git a/thirdpart/avatar/admin.py b/thirdpart/avatar/admin.py new file mode 100644 index 0000000000..4af39fa913 --- /dev/null +++ b/thirdpart/avatar/admin.py @@ -0,0 +1,4 @@ +from django.contrib import admin +from avatar.models import Avatar + +admin.site.register(Avatar) \ No newline at end of file diff --git a/thirdpart/avatar/default.jpg b/thirdpart/avatar/default.jpg new file mode 100644 index 0000000000000000000000000000000000000000..37a6276a65a5a532fdb8a82e41d1584d5ba8886c GIT binary patch literal 3431 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""" % + (avatar.avatar_url(size), unicode(avatar), size, size)) + +class PrimaryAvatarForm(forms.Form): + + def __init__(self, *args, **kwargs): + user = kwargs.pop('user') + size = kwargs.pop('size', 80) + super(PrimaryAvatarForm, self).__init__(*args, **kwargs) + avatars = user.avatar_set.all() + self.fields['choice'] = forms.ChoiceField( + choices=[(c.id, avatar_img(c, size)) for c in user.avatar_set.all()], + widget=widgets.RadioSelect) + +class DeleteAvatarForm(forms.Form): + + def __init__(self, *args, **kwargs): + user = kwargs.pop('user') + size = kwargs.pop('size', 80) + super(DeleteAvatarForm, self).__init__(*args, **kwargs) + avatars = user.avatar_set.all() + self.fields['choices'] = forms.MultipleChoiceField( + choices=[(c.id, avatar_img(c, size)) for c in user.avatar_set.all()], + widget=widgets.CheckboxSelectMultiple) diff --git a/thirdpart/avatar/management/__init__.py b/thirdpart/avatar/management/__init__.py new file mode 100644 index 0000000000..ca2bd94c32 --- /dev/null +++ b/thirdpart/avatar/management/__init__.py @@ -0,0 +1,14 @@ +from django.conf import settings +from django.db.models import signals +from django.utils.translation import ugettext_noop as _ + +if "notification" in settings.INSTALLED_APPS: + from notification import models as notification + + def create_notice_types(app, created_models, verbosity, **kwargs): + notification.create_notice_type("avatar_updated", _("Avatar Updated"), _("avatar have been updated")) + notification.create_notice_type("avatar_friend_updated", _("Friend Updated Avatar"), _("a friend has updated his avatar")) + + signals.post_syncdb.connect(create_notice_types, sender=notification) +else: + print "Skipping creation of NoticeTypes as notification app not found" diff --git a/thirdpart/avatar/management/commands/__init__.py b/thirdpart/avatar/management/commands/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/thirdpart/avatar/management/commands/__init__.py @@ -0,0 +1 @@ + diff --git a/thirdpart/avatar/management/commands/rebuild_avatars.py b/thirdpart/avatar/management/commands/rebuild_avatars.py new file mode 100644 index 0000000000..28b64ad6d9 --- /dev/null +++ b/thirdpart/avatar/management/commands/rebuild_avatars.py @@ -0,0 +1,15 @@ +from django.core.management.base import NoArgsCommand +from django.conf import settings + +from avatar.models import Avatar +from avatar import AUTO_GENERATE_AVATAR_SIZES + +class Command(NoArgsCommand): + help = "Regenerates avatar thumbnails for the sizes specified in " + \ + "settings.AUTO_GENERATE_AVATAR_SIZES." + + def handle_noargs(self, **options): + for avatar in Avatar.objects.all(): + for size in AUTO_GENERATE_AVATAR_SIZES: + print "Rebuilding Avatar id=%s at size %s." % (avatar.id, size) + avatar.create_thumbnail(size) \ No newline at end of file diff --git a/thirdpart/avatar/models.py b/thirdpart/avatar/models.py new file mode 100644 index 0000000000..ada6c94a4b --- /dev/null +++ b/thirdpart/avatar/models.py @@ -0,0 +1,79 @@ +import datetime +import os.path + +from django.db import models +from django.contrib.auth.models import User +from django.core.files.base import ContentFile +from django.utils.translation import ugettext as _ + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +try: + from PIL import Image +except ImportError: + import Image + +from avatar import AVATAR_STORAGE_DIR, AVATAR_RESIZE_METHOD + +def avatar_file_path(instance=None, filename=None, user=None): + user = user or instance.user + return os.path.join(AVATAR_STORAGE_DIR, user.username, filename) + +class Avatar(models.Model): + user = models.ForeignKey(User) + primary = models.BooleanField(default=False) + avatar = models.ImageField(max_length=1024, upload_to=avatar_file_path, blank=True) + date_uploaded = models.DateTimeField(default=datetime.datetime.now) + + def __unicode__(self): + return _(u'Avatar for %s') % self.user + + def save(self, force_insert=False, force_update=False): + if self.primary: + avatars = Avatar.objects.filter(user=self.user, primary=True)\ + .exclude(id=self.id) + avatars.update(primary=False) + super(Avatar, self).save(force_insert, force_update) + + def thumbnail_exists(self, size): + return self.avatar.storage.exists(self.avatar_name(size)) + + def create_thumbnail(self, size): + try: + orig = self.avatar.storage.open(self.avatar.name, 'rb').read() + image = Image.open(StringIO(orig)) + except IOError: + return # What should we do here? Render a "sorry, didn't work" img? + (w, h) = image.size + if w != size or h != size: + if w > h: + diff = (w - h) / 2 + image = image.crop((diff, 0, w - diff, h)) + else: + diff = (h - w) / 2 + image = image.crop((0, diff, w, h - diff)) + image = image.resize((size, size), AVATAR_RESIZE_METHOD) + if image.mode != "RGB": + image = image.convert("RGB") + thumb = StringIO() + image.save(thumb, "JPEG", quality=95) + thumb_file = ContentFile(thumb.getvalue()) + else: + thumb_file = ContentFile(orig) + thumb = self.avatar.storage.save(self.avatar_name(size), thumb_file) + + def avatar_url(self, size): + return self.avatar.storage.url(self.avatar_name(size)) + + def avatar_name(self, size): + filename = os.path.basename(self.avatar.name) + idx = filename.rfind('.') + if idx != -1: + filename = filename[:idx] + '.jpg' + else: + filename += '.jpg' + return os.path.join(AVATAR_STORAGE_DIR, self.user.username, + 'resized', str(size), filename) diff --git a/thirdpart/avatar/templates/avatar/base.html b/thirdpart/avatar/templates/avatar/base.html new file mode 100644 index 0000000000..057fa22aec --- /dev/null +++ b/thirdpart/avatar/templates/avatar/base.html @@ -0,0 +1,10 @@ +{% extends "accounts.html" %} + + + + {% block title %}django-avatar{% endblock %} + + + {% block content %}{% endblock %} + + diff --git a/thirdpart/avatar/templates/avatar/change.html b/thirdpart/avatar/templates/avatar/change.html new file mode 100644 index 0000000000..555be3da0f --- /dev/null +++ b/thirdpart/avatar/templates/avatar/change.html @@ -0,0 +1,21 @@ +{% extends "avatar/base.html" %} +{% load avatar_tags %} + +{% block content %} +

Your current avatar:

+ {% avatar user %} + {% if not avatars %} +

You do not yet have an avatar. Please upload one now.

+ {% else %} +
+
    + {{ primary_avatar_form.as_ul }} +
+ +
+ {% endif %} +
+ + +
+{% endblock %} \ No newline at end of file diff --git a/thirdpart/avatar/templates/avatar/confirm_delete.html b/thirdpart/avatar/templates/avatar/confirm_delete.html new file mode 100644 index 0000000000..8bf9407a31 --- /dev/null +++ b/thirdpart/avatar/templates/avatar/confirm_delete.html @@ -0,0 +1,15 @@ +{% extends "avatar/base.html" %} + +{% block content %} +

Please select the avatars that you would like to delete.

+ {% if not avatars %} +

You have no avatars to delete. Please upload one now.

+ {% else %} +
+
    + {{ delete_avatar_form.as_ul }} +
+ +
+ {% endif %} +{% endblock %} \ No newline at end of file diff --git a/thirdpart/avatar/templates/notifications/avatar_friend_updated/full.txt b/thirdpart/avatar/templates/notifications/avatar_friend_updated/full.txt new file mode 100644 index 0000000000..a3baba1a39 --- /dev/null +++ b/thirdpart/avatar/templates/notifications/avatar_friend_updated/full.txt @@ -0,0 +1,4 @@ +{% load i18n %}{% blocktrans with user as avatar_creator and avatar.get_absolute_url as avatar_url %}{{ avatar_creator }} has updated his avatar {{ avatar }}. + +http://{{ current_site }}{{ avatar_url }} +{% endblocktrans %} diff --git a/thirdpart/avatar/templates/notifications/avatar_friend_updated/notice.html b/thirdpart/avatar/templates/notifications/avatar_friend_updated/notice.html new file mode 100644 index 0000000000..bbe9a1ac19 --- /dev/null +++ b/thirdpart/avatar/templates/notifications/avatar_friend_updated/notice.html @@ -0,0 +1,2 @@ +{% load i18n %}{% url profile_detail username=user.username as user_url %} +{% blocktrans with user as avatar_creator and avatar.get_absolute_url as avatar_url %}{{ avatar_creator }} has updated his avatar {{ avatar }}.{% endblocktrans %} diff --git a/thirdpart/avatar/templates/notifications/avatar_updated/full.txt b/thirdpart/avatar/templates/notifications/avatar_updated/full.txt new file mode 100644 index 0000000000..c11977ca1c --- /dev/null +++ b/thirdpart/avatar/templates/notifications/avatar_updated/full.txt @@ -0,0 +1,4 @@ +{% load i18n %}{% blocktrans with avatar.get_absolute_url as avatar_url %}Avatar {{ avatar }} has been created. + +http://{{ current_site }}{{ avatar_url }} +{% endblocktrans %} diff --git a/thirdpart/avatar/templates/notifications/avatar_updated/notice.html b/thirdpart/avatar/templates/notifications/avatar_updated/notice.html new file mode 100644 index 0000000000..22fd1f1084 --- /dev/null +++ b/thirdpart/avatar/templates/notifications/avatar_updated/notice.html @@ -0,0 +1,2 @@ +{% load i18n %} +{% blocktrans with avatar.get_absolute_url as avatar_url %}A new tribe {{ avatar }} has been created.{% endblocktrans %} diff --git a/thirdpart/avatar/templatetags/__init__.py b/thirdpart/avatar/templatetags/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/thirdpart/avatar/templatetags/avatar_tags.py b/thirdpart/avatar/templatetags/avatar_tags.py new file mode 100644 index 0000000000..217c6bffa6 --- /dev/null +++ b/thirdpart/avatar/templatetags/avatar_tags.py @@ -0,0 +1,63 @@ +import urllib + +from django import template +from django.contrib.auth.models import User +from django.utils.translation import ugettext as _ +from django.utils.hashcompat import md5_constructor + +from avatar import AVATAR_DEFAULT_URL, AVATAR_GRAVATAR_BACKUP, AVATAR_GRAVATAR_DEFAULT + +register = template.Library() + +def avatar_url(user, size=80): + if not isinstance(user, User): + try: + user = User.objects.get(username=user) + except User.DoesNotExist: + return AVATAR_DEFAULT_URL + avatars = user.avatar_set.order_by('-date_uploaded') + primary = avatars.filter(primary=True) + if primary.count() > 0: + avatar = primary[0] + elif avatars.count() > 0: + avatar = avatars[0] + else: + avatar = None + if avatar is not None: + if not avatar.thumbnail_exists(size): + avatar.create_thumbnail(size) + return avatar.avatar_url(size) + else: + if AVATAR_GRAVATAR_BACKUP: + params = {'s': str(size)} + if AVATAR_GRAVATAR_DEFAULT: + params['d'] = AVATAR_GRAVATAR_DEFAULT + return "http://www.gravatar.com/avatar/%s/?%s" % ( + md5_constructor(user.email).hexdigest(), + urllib.urlencode(params)) + else: + return AVATAR_DEFAULT_URL +register.simple_tag(avatar_url) + +def avatar(user, size=80): + if not isinstance(user, User): + try: + user = User.objects.get(username=user) + alt = unicode(user) + url = avatar_url(user, size) + except User.DoesNotExist: + url = AVATAR_DEFAULT_URL + alt = _("Default Avatar") + else: + alt = unicode(user) + url = avatar_url(user, size) + return """%s""" % (url, alt, + size, size) +register.simple_tag(avatar) + +def render_avatar(avatar, size=80): + if not avatar.thumbnail_exists(size): + avatar.create_thumbnail(size) + return """%s""" % ( + avatar.avatar_url(size), str(avatar), size, size) +register.simple_tag(render_avatar) diff --git a/thirdpart/avatar/urls.py b/thirdpart/avatar/urls.py new file mode 100644 index 0000000000..0ccbfe8075 --- /dev/null +++ b/thirdpart/avatar/urls.py @@ -0,0 +1,6 @@ +from django.conf.urls.defaults import patterns, url + +urlpatterns = patterns('avatar.views', + url('^change/$', 'change', name='avatar_change'), + url('^delete/$', 'delete', name='avatar_delete'), +) diff --git a/thirdpart/avatar/views.py b/thirdpart/avatar/views.py new file mode 100644 index 0000000000..220dca1c26 --- /dev/null +++ b/thirdpart/avatar/views.py @@ -0,0 +1,129 @@ +import os.path + +from avatar.models import Avatar, avatar_file_path +from avatar.forms import PrimaryAvatarForm, DeleteAvatarForm +from django.http import HttpResponseRedirect +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.contrib.auth.decorators import login_required +from django.utils.translation import ugettext as _ + +from django.db.models import get_app +from django.core.exceptions import ImproperlyConfigured +from django.conf import settings + +try: + notification = get_app('notification') +except ImproperlyConfigured: + notification = None + +friends = False +if 'friends' in settings.INSTALLED_APPS: + friends = True + from friends.models import Friendship + +def _get_next(request): + """ + The part that's the least straightforward about views in this module is how they + determine their redirects after they have finished computation. + + In short, they will try and determine the next place to go in the following order: + + 1. If there is a variable named ``next`` in the *POST* parameters, the view will + redirect to that variable's value. + 2. If there is a variable named ``next`` in the *GET* parameters, the view will + redirect to that variable's value. + 3. If Django can determine the previous page from the HTTP headers, the view will + redirect to that previous page. + """ + next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', None))) + if not next: + next = request.path + return next + +def change(request, extra_context={}, next_override=None): + avatars = Avatar.objects.filter(user=request.user).order_by('-primary') + if avatars.count() > 0: + avatar = avatars[0] + kwargs = {'initial': {'choice': avatar.id}} + else: + avatar = None + kwargs = {} + primary_avatar_form = PrimaryAvatarForm(request.POST or None, user=request.user, **kwargs) + if request.method == "POST": + updated = False + if 'avatar' in request.FILES: + path = avatar_file_path(user=request.user, + filename=request.FILES['avatar'].name) + avatar = Avatar( + user = request.user, + primary = True, + avatar = path, + ) + new_file = avatar.avatar.storage.save(path, request.FILES['avatar']) + avatar.save() + updated = True + request.user.message_set.create( + message=_("Successfully uploaded a new avatar.")) + if 'choice' in request.POST and primary_avatar_form.is_valid(): + avatar = Avatar.objects.get(id= + primary_avatar_form.cleaned_data['choice']) + avatar.primary = True + avatar.save() + updated = True + request.user.message_set.create( + message=_("Successfully updated your avatar.")) + if updated and notification: + notification.send([request.user], "avatar_updated", {"user": request.user, "avatar": avatar}) + if friends: + notification.send((x['friend'] for x in Friendship.objects.friends_for_user(request.user)), "avatar_friend_updated", {"user": request.user, "avatar": avatar}) + return HttpResponseRedirect(next_override or _get_next(request)) + return render_to_response( + 'avatar/change.html', + extra_context, + context_instance = RequestContext( + request, + { 'avatar': avatar, + 'avatars': avatars, + 'primary_avatar_form': primary_avatar_form, + 'next': next_override or _get_next(request), } + ) + ) +change = login_required(change) + +def delete(request, extra_context={}, next_override=None): + avatars = Avatar.objects.filter(user=request.user).order_by('-primary') + if avatars.count() > 0: + avatar = avatars[0] + else: + avatar = None + delete_avatar_form = DeleteAvatarForm(request.POST or None, user=request.user) + if request.method == 'POST': + if delete_avatar_form.is_valid(): + ids = delete_avatar_form.cleaned_data['choices'] + if unicode(avatar.id) in ids and avatars.count() > len(ids): + for a in avatars: + if unicode(a.id) not in ids: + a.primary = True + a.save() + if notification: + notification.send([request.user], "avatar_updated", {"user": request.user, "avatar": a}) + if friends: + notification.send((x['friend'] for x in Friendship.objects.friends_for_user(request.user)), "avatar_friend_updated", {"user": request.user, "avatar": a}) + break + Avatar.objects.filter(id__in=ids).delete() + request.user.message_set.create( + message=_("Successfully deleted the requested avatars.")) + return HttpResponseRedirect(next_override or _get_next(request)) + return render_to_response( + 'avatar/confirm_delete.html', + extra_context, + context_instance = RequestContext( + request, + { 'avatar': avatar, + 'avatars': avatars, + 'delete_avatar_form': delete_avatar_form, + 'next': next_override or _get_next(request), } + ) + ) +delete = login_required(delete) diff --git a/thirdpart/registration/__init__.py b/thirdpart/registration/__init__.py new file mode 100644 index 0000000000..bd4ba7f41f --- /dev/null +++ b/thirdpart/registration/__init__.py @@ -0,0 +1,12 @@ +VERSION = (0, 8, 0, 'alpha', 1) + +def get_version(): + version = '%s.%s' % (VERSION[0], VERSION[1]) + if VERSION[2]: + version = '%s.%s' % (version, VERSION[2]) + if VERSION[3:] == ('alpha', 0): + version = '%s pre-alpha' % version + else: + if VERSION[3] != 'final': + version = '%s %s %s' % (version, VERSION[3], VERSION[4]) + return version diff --git a/thirdpart/registration/admin.py b/thirdpart/registration/admin.py new file mode 100644 index 0000000000..d7626d750d --- /dev/null +++ b/thirdpart/registration/admin.py @@ -0,0 +1,46 @@ +from django.contrib import admin +from django.contrib.sites.models import RequestSite +from django.contrib.sites.models import Site +from django.utils.translation import ugettext_lazy as _ + +from registration.models import RegistrationProfile + + +class RegistrationAdmin(admin.ModelAdmin): + actions = ['activate_users', 'resend_activation_email'] + list_display = ('user', 'activation_key_expired') + raw_id_fields = ['user'] + search_fields = ('user__username', 'user__first_name') + + def activate_users(self, request, queryset): + """ + Activates the selected users, if they are not alrady + activated. + + """ + for profile in queryset: + RegistrationProfile.objects.activate_user(profile.activation_key) + activate_users.short_description = _("Activate users") + + def resend_activation_email(self, request, queryset): + """ + Re-sends activation emails for the selected users. + + Note that this will *only* send activation emails for users + who are eligible to activate; emails will not be sent to users + whose activation keys have expired or who have already + activated. + + """ + if Site._meta.installed: + site = Site.objects.get_current() + else: + site = RequestSite(request) + + for profile in queryset: + if not profile.activation_key_expired(): + profile.send_activation_email(site) + resend_activation_email.short_description = _("Re-send activation emails") + + +admin.site.register(RegistrationProfile, RegistrationAdmin) diff --git a/thirdpart/registration/auth_urls.py b/thirdpart/registration/auth_urls.py new file mode 100644 index 0000000000..9bb1bc3cbd --- /dev/null +++ b/thirdpart/registration/auth_urls.py @@ -0,0 +1,58 @@ +""" +URL patterns for the views included in ``django.contrib.auth``. + +Including these URLs (via the ``include()`` directive) will set up the +following patterns based at whatever URL prefix they are included +under: + +* User login at ``login/``. + +* User logout at ``logout/``. + +* The two-step password change at ``password/change/`` and + ``password/change/done/``. + +* The four-step password reset at ``password/reset/``, + ``password/reset/confirm/``, ``password/reset/complete/`` and + ``password/reset/done/``. + +The default registration backend already has an ``include()`` for +these URLs, so under the default setup it is not necessary to manually +include these views. Other backends may or may not include them; +consult a specific backend's documentation for details. + +""" + +from django.conf.urls.defaults import * + +from django.contrib.auth import views as auth_views + + +urlpatterns = patterns('', + url(r'^login/$', + auth_views.login, + {'template_name': 'registration/login.html'}, + name='auth_login'), + url(r'^logout/$', + auth_views.logout, + {'template_name': 'registration/logout.html'}, + name='auth_logout'), + url(r'^password/change/$', + auth_views.password_change, + name='auth_password_change'), + url(r'^password/change/done/$', + auth_views.password_change_done, + name='auth_password_change_done'), + url(r'^password/reset/$', + auth_views.password_reset, + name='auth_password_reset'), + url(r'^password/reset/confirm/(?P[0-9A-Za-z]+)-(?P.+)/$', + auth_views.password_reset_confirm, + name='auth_password_reset_confirm'), + url(r'^password/reset/complete/$', + auth_views.password_reset_complete, + name='auth_password_reset_complete'), + url(r'^password/reset/done/$', + auth_views.password_reset_done, + name='auth_password_reset_done'), +) diff --git a/thirdpart/registration/backends/__init__.py b/thirdpart/registration/backends/__init__.py new file mode 100644 index 0000000000..0f2ec4bab4 --- /dev/null +++ b/thirdpart/registration/backends/__init__.py @@ -0,0 +1,32 @@ +from django.core.exceptions import ImproperlyConfigured + + +# Python 2.7 has an importlib with import_module; for older Pythons, +# Django's bundled copy provides it. +try: + from importlib import import_module +except ImportError: + from django.utils.importlib import import_module + +def get_backend(path): + """ + Return an instance of a registration backend, given the dotted + Python import path (as a string) to the backend class. + + If the backend cannot be located (e.g., because no such module + exists, or because the module does not contain a class of the + appropriate name), ``django.core.exceptions.ImproperlyConfigured`` + is raised. + + """ + i = path.rfind('.') + module, attr = path[:i], path[i+1:] + try: + mod = import_module(module) + except ImportError, e: + raise ImproperlyConfigured('Error loading registration backend %s: "%s"' % (module, e)) + try: + backend_class = getattr(mod, attr) + except AttributeError: + raise ImproperlyConfigured('Module "%s" does not define a registration backend named "%s"' % (module, attr)) + return backend_class() diff --git a/thirdpart/registration/backends/default/__init__.py b/thirdpart/registration/backends/default/__init__.py new file mode 100644 index 0000000000..59b3da776d --- /dev/null +++ b/thirdpart/registration/backends/default/__init__.py @@ -0,0 +1,139 @@ +from django.conf import settings +from django.contrib.sites.models import RequestSite +from django.contrib.sites.models import Site + +from registration import signals +from registration.forms import RegistrationForm +from registration.models import RegistrationProfile + + +class DefaultBackend(object): + """ + A registration backend which follows a simple workflow: + + 1. User signs up, inactive account is created. + + 2. Email is sent to user with activation link. + + 3. User clicks activation link, account is now active. + + Using this backend requires that + + * ``registration`` be listed in the ``INSTALLED_APPS`` setting + (since this backend makes use of models defined in this + application). + + * The setting ``ACCOUNT_ACTIVATION_DAYS`` be supplied, specifying + (as an integer) the number of days from registration during + which a user may activate their account (after that period + expires, activation will be disallowed). + + * The creation of the templates + ``registration/activation_email_subject.txt`` and + ``registration/activation_email.txt``, which will be used for + the activation email. See the notes for this backends + ``register`` method for details regarding these templates. + + Additionally, registration can be temporarily closed by adding the + setting ``REGISTRATION_OPEN`` and setting it to + ``False``. Omitting this setting, or setting it to ``True``, will + be interpreted as meaning that registration is currently open and + permitted. + + Internally, this is accomplished via storing an activation key in + an instance of ``registration.models.RegistrationProfile``. See + that model and its custom manager for full documentation of its + fields and supported operations. + + """ + def register(self, request, **kwargs): + """ + Given a username, email address and password, register a new + user account, which will initially be inactive. + + Along with the new ``User`` object, a new + ``registration.models.RegistrationProfile`` will be created, + tied to that ``User``, containing the activation key which + will be used for this account. + + An email will be sent to the supplied email address; this + email should contain an activation link. The email will be + rendered using two templates. See the documentation for + ``RegistrationProfile.send_activation_email()`` for + information about these templates and the contexts provided to + them. + + After the ``User`` and ``RegistrationProfile`` are created and + the activation email is sent, the signal + ``registration.signals.user_registered`` will be sent, with + the new ``User`` as the keyword argument ``user`` and the + class of this backend as the sender. + + """ + username, email, password = kwargs['username'], kwargs['email'], kwargs['password1'] + if Site._meta.installed: + site = Site.objects.get_current() + else: + site = RequestSite(request) + new_user = RegistrationProfile.objects.create_inactive_user(username, email, + password, site) + signals.user_registered.send(sender=self.__class__, + user=new_user, + request=request) + return new_user + + def activate(self, request, activation_key): + """ + Given an an activation key, look up and activate the user + account corresponding to that key (if possible). + + After successful activation, the signal + ``registration.signals.user_activated`` will be sent, with the + newly activated ``User`` as the keyword argument ``user`` and + the class of this backend as the sender. + + """ + activated = RegistrationProfile.objects.activate_user(activation_key) + if activated: + signals.user_activated.send(sender=self.__class__, + user=activated, + request=request) + return activated + + def registration_allowed(self, request): + """ + Indicate whether account registration is currently permitted, + based on the value of the setting ``REGISTRATION_OPEN``. This + is determined as follows: + + * If ``REGISTRATION_OPEN`` is not specified in settings, or is + set to ``True``, registration is permitted. + + * If ``REGISTRATION_OPEN`` is both specified and set to + ``False``, registration is not permitted. + + """ + return getattr(settings, 'REGISTRATION_OPEN', True) + + def get_form_class(self, request): + """ + Return the default form class used for user registration. + + """ + return RegistrationForm + + def post_registration_redirect(self, request, user): + """ + Return the name of the URL to redirect to after successful + user registration. + + """ + return ('registration_complete', (), {}) + + def post_activation_redirect(self, request, user): + """ + Return the name of the URL to redirect to after successful + account activation. + + """ + return ('registration_activation_complete', (), {}) diff --git a/thirdpart/registration/backends/default/urls.py b/thirdpart/registration/backends/default/urls.py new file mode 100644 index 0000000000..9e992c7668 --- /dev/null +++ b/thirdpart/registration/backends/default/urls.py @@ -0,0 +1,54 @@ +""" +URLconf for registration and activation, using django-registration's +default backend. + +If the default behavior of these views is acceptable to you, simply +use a line like this in your root URLconf to set up the default URLs +for registration:: + + (r'^accounts/', include('registration.backends.default.urls')), + +This will also automatically set up the views in +``django.contrib.auth`` at sensible default locations. + +If you'd like to customize the behavior (e.g., by passing extra +arguments to the various views) or split up the URLs, feel free to set +up your own URL patterns for these views instead. + +""" + + +from django.conf.urls.defaults import * +from django.views.generic.simple import direct_to_template + +from registration.views import activate +from registration.views import register + + +urlpatterns = patterns('', + url(r'^activate/complete/$', + direct_to_template, + { 'template': 'registration/activation_complete.html' }, + name='registration_activation_complete'), + # Activation keys get matched by \w+ instead of the more specific + # [a-fA-F0-9]{40} because a bad activation key should still get to the view; + # that way it can return a sensible "invalid key" message instead of a + # confusing 404. + url(r'^activate/(?P\w+)/$', + activate, + { 'backend': 'registration.backends.default.DefaultBackend' }, + name='registration_activate'), + url(r'^register/$', + register, + { 'backend': 'registration.backends.default.DefaultBackend' }, + name='registration_register'), + url(r'^register/complete/$', + direct_to_template, + { 'template': 'registration/registration_complete.html' }, + name='registration_complete'), + url(r'^register/closed/$', + direct_to_template, + { 'template': 'registration/registration_closed.html' }, + name='registration_disallowed'), + (r'', include('registration.auth_urls')), + ) diff --git a/thirdpart/registration/forms.py b/thirdpart/registration/forms.py new file mode 100644 index 0000000000..64dbb803ea --- /dev/null +++ b/thirdpart/registration/forms.py @@ -0,0 +1,131 @@ +""" +Forms and validation code for user registration. + +""" + + +from django.contrib.auth.models import User +from django import forms +from django.utils.translation import ugettext_lazy as _ + + +# I put this on all required fields, because it's easier to pick up +# on them with CSS or JavaScript if they have a class of "required" +# in the HTML. Your mileage may vary. If/when Django ticket #3515 +# lands in trunk, this will no longer be necessary. +attrs_dict = { 'class': 'required' } + + +class RegistrationForm(forms.Form): + """ + Form for registering a new user account. + + Validates that the requested username is not already in use, and + requires the password to be entered twice to catch typos. + + Subclasses should feel free to add any additional validation they + need, but should avoid defining a ``save()`` method -- the actual + saving of collected user data is delegated to the active + registration backend. + + """ + username = forms.RegexField(regex=r'^\w+$', + max_length=30, + widget=forms.TextInput(attrs=attrs_dict), + label=_("Username"), + error_messages={ 'invalid': _("This value must contain only letters, numbers and underscores.") }) + email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict, + maxlength=75)), + label=_("Email address")) + password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False), + label=_("Password")) + password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False), + label=_("Password (again)")) + + def clean_username(self): + """ + Validate that the username is alphanumeric and is not already + in use. + + """ + try: + user = User.objects.get(username__iexact=self.cleaned_data['username']) + except User.DoesNotExist: + return self.cleaned_data['username'] + raise forms.ValidationError(_("A user with that username already exists.")) + + def clean_email(self): + try: + user = User.objects.get(email__iexact=self.cleaned_data['email']) + except User.DoesNotExist: + return self.cleaned_data['email'] + + raise forms.ValidationError(_("A user with this email alread")) + + 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. + + """ + if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data: + if self.cleaned_data['password1'] != self.cleaned_data['password2']: + raise forms.ValidationError(_("The two password fields didn't match.")) + return self.cleaned_data + + +class RegistrationFormTermsOfService(RegistrationForm): + """ + Subclass of ``RegistrationForm`` which adds a required checkbox + for agreeing to a site's Terms of Service. + + """ + tos = forms.BooleanField(widget=forms.CheckboxInput(attrs=attrs_dict), + label=_(u'I have read and agree to the Terms of Service'), + error_messages={ 'required': _("You must agree to the terms to register") }) + + +class RegistrationFormUniqueEmail(RegistrationForm): + """ + Subclass of ``RegistrationForm`` which enforces uniqueness of + email addresses. + + """ + 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'] + + +class RegistrationFormNoFreeEmail(RegistrationForm): + """ + Subclass of ``RegistrationForm`` which disallows registration with + email addresses from popular free webmail services; moderately + useful for preventing automated spam registrations. + + To change the list of banned domains, subclass this form and + override the attribute ``bad_domains``. + + """ + bad_domains = ['aim.com', 'aol.com', 'email.com', 'gmail.com', + 'googlemail.com', 'hotmail.com', 'hushmail.com', + 'msn.com', 'mail.ru', 'mailinator.com', 'live.com', + 'yahoo.com'] + + def clean_email(self): + """ + Check the supplied email address against a list of known free + webmail domains. + + """ + email_domain = self.cleaned_data['email'].split('@')[1] + if email_domain in self.bad_domains: + raise forms.ValidationError(_("Registration using free email addresses is prohibited. Please supply a different email address.")) + return self.cleaned_data['email'] diff --git a/thirdpart/registration/locale/ar/LC_MESSAGES/django.mo b/thirdpart/registration/locale/ar/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..07bc79f1242872c701e463e947846ef4e77f8ceb GIT binary patch literal 2135 zcmb`GO>Y}j6oxO*QffYhKrCQ!#Dcy6I+Tvl^ALK%-lJj?>YYCZ?1GEo`4N|6c3G!#`-*whp((Yy+m z-Kr90Rd=-srEvqF+S>K&w8ZwRDP50btrjY+l_pnHD-0U$E!U`8n&vzuwIbbawY(LQ zRCAY>RH%G2>X;Ul20TZCaVY6=c`M{{PW8Cq;1c?>sfHsNd0Y8MfvSxlKzY&+&~%s< z3w;kwRq0dUqvmSh8;Sg$GDd~^BKhs+Ej+}Uwf!0%s{xu9H-k1c+uDo@2_FavRl`_7 z6&~A}l@|1d*qUo>Nv*#O!Wt=AZIE%BO312lmlMTrs}(V#ju_YN1^9MlBqu` z3}eal)QN{$utM<<%OvK);0skXPNC*3qAg51OLMuYPjWZ%w3si?7iMQtWz`Bo)NRVSxU{#R-7r!ir2%LmPTdiQYQ1hlbLYFCTMIT`)+OY?*SF{c@gpRgwgbFt8Js8F>X~IztA(6go@3S{MAyi!7zXaki z`?ib9SJPtHfr}r`&fN@Gz>eI+M)JVgbpEe0pa?4w3p@}92mg_=6F0I0Ju$062%PoO ziTdj-=q`RB&d+EWA|2cDEQeAJX4!#dFIu(VvSp8Z~sGVt;dyNyAm~cwR)@sXJ^ro7AAbs!, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-19 19:30-0500\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:38 +msgid "username" +msgstr "اسم المستخدم" + +#: forms.py:41 +msgid "email address" +msgstr "عنوان البريد الالكتروني" + +#: forms.py:43 +msgid "password" +msgstr "كلمة المرور" + +#: forms.py:45 +msgid "password (again)" +msgstr "تأكيد كلمة المرور" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "يمكن أن يحتوي اسم المستخدم على احرف، ارقام وشرطات سطرية فقط" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "اسم المستخدم مسجل مسبقا. يرجى اختيار اسم اخر." + +#: forms.py:68 +msgid "You must type the same password each time" +msgstr "يجب ادخال كلمة المرور مطابقة كل مرة" + +#: forms.py:96 +msgid "I have read and agree to the Terms of Service" +msgstr "أقر بقراءة والموافقة على شروط الخدمة" + +#: forms.py:105 +msgid "You must agree to the terms to register" +msgstr "يجب الموافقة على الشروط للتسجيل" + +#: forms.py:124 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "عنوان البريد الالكتروني مسجل مسبقا. يرجى تزويد عنوان بريد الكتروني مختلف." + +#: forms.py:149 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "يمنع التسجيل باستخدام عناوين بريد الكترونية مجانية. يرجى تزويد عنوان بريد الكتروني مختلف." + +#: models.py:188 +msgid "user" +msgstr "مستخدم" + +#: models.py:189 +msgid "activation key" +msgstr "رمز التفعيل" + +#: models.py:194 +msgid "registration profile" +msgstr "ملف التسجيل الشخصي" + +#: models.py:195 +msgid "registration profiles" +msgstr "ملفات التسجيل الشخصية" diff --git a/thirdpart/registration/locale/bg/LC_MESSAGES/django.mo b/thirdpart/registration/locale/bg/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..be9adf4d3defce5ee8274e2cb996d8c63100a637 GIT binary patch literal 2302 zcmb7D&u<$=6doW@Fi;QzaR9^<5~875V>f~(ZmF7vP(`Gvl{yh{sV4TsUSjWByJOP` zA(6DAr3iwAdZ{XbxNzX&{4lZNB)uWH&7P6C@E7nG!1reTBZ0IME3dzqH*eni-uK@A z@yx(2f#+$wFXFv}_jz7FfImD}fDUj8cp3O3a2j~zK_T7(&H+yWzXHAn{0%q={0n## z_`*X%JPCXq_$Y7$$hpRV&jM$F&jE|T0pO=V_FDml$n_;~9{ay>_!DscVQ>K+eME>4 zfj5Ak0RIG*fR~TNe0~KI_5%p^67W^v6Torc%fKv)zd)B%POgAO_ox=SUXd2qUsa@ACRr#1DhyRfZb;R@FS+xsR)r*G z%Swid!dkUjULr{acVR&V%F_ogCPl7F<(e>{w z=$I^m=%9!*by|gHEVyNLs4*0*ko{wsL^kk0QhDu67MwYhg-&NEJ2w8_*!c;Xo0z#Y zd2u>1qpE(OoeN>nEjZ_D#n8$58A@a?=A3c#ipRrwM{1R!6RFfmCpGG%M=3p;8BRH= zH&ZD%Iy0*7a`3+2h?5$|F{MvrhEE<(;bvk=hT6#m(hJK{`$2~0%Cg{MQ>WAQ?lM_% zwO>D5(==_0mZO_yhfJ%xvW5-QHjQW*9=otp zbQSBi*)!{=6|F`qra@+l|C(kSCwJ{xgYD7h>>QV38zq2l?C4Dw%aKUWaiL?lvNOZGl6}*0j4XSdV5K z{@a$z9s=$%_q)*G3)2whn_g;I?Gc3ew?7xxyJH!)Yzg25TxXv5*S$l>O^8Fs#qL1Y4YX*5 zplBl=gU#qB!u}jv*Fb!mTR%j$z1MJrCgBGOQ5XgZ#X%qVY@3!aKVZ8GYEY#YGj2UP z*BveH`u#>L=E4KR`t)b1g*{JA6GeW@XVDF0<=(`!7=lZ!h414IQ&Y_AwX~$h^OT_-o)f, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-19 19:30-0500\n" +"PO-Revision-Date: 2008-03-05 12:37+0200\n" +"Last-Translator: Vladislav \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Bookmarks: -1,-1,-1,-1,10,-1,-1,-1,-1,-1\n" + +#: forms.py:38 +msgid "username" +msgstr "Потребителско име " + +#: forms.py:41 +msgid "email address" +msgstr "Електронна поща" + +#: forms.py:43 +msgid "password" +msgstr "Парола" + +#: forms.py:45 +msgid "password (again)" +msgstr "Парола (проверка)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "Потребителските имена могат да съдържат букви, цифри и подчертавки" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "Потребителското име е заето. Моля изберето друго." + +#: forms.py:68 +msgid "You must type the same password each time" +msgstr "Грешка при проверка на паролата." + +#: forms.py:96 +msgid "I have read and agree to the Terms of Service" +msgstr "Прочел съм и съм съгласен с условията за експлоатация" + +#: forms.py:105 +msgid "You must agree to the terms to register" +msgstr "Трябва да сте съгласни с условията за да се регистрирате." + +#: forms.py:124 +msgid "This email address is already in use. Please supply a different email address." +msgstr "Адреса на електронната поща е използван. Моля въведете друг адрес." + +#: forms.py:149 +msgid "Registration using free email addresses is prohibited. Please supply a different email address." +msgstr "Регистрациите с безплатни адреси е забранен. Моля въведете различен адрес за електронна поща" + +#: models.py:188 +msgid "user" +msgstr "Потребител" + +#: models.py:189 +msgid "activation key" +msgstr "Ключ за активация" + +#: models.py:194 +msgid "registration profile" +msgstr "регистрационен профил" + +#: models.py:195 +msgid "registration profiles" +msgstr "регистрационни профили" + diff --git a/thirdpart/registration/locale/da/LC_MESSAGES/django.mo b/thirdpart/registration/locale/da/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..9478680094ac7936d9bc9c4ee8fc684061f52a87 GIT binary patch literal 1803 zcmb7^&u$z=5XKuY5SIT591tMY1!60iu}Ln*gvfDhkfj(bvx!75sCTD!wzECmtL~n4 z_P`s!5y6cYKnSiJf@DrS0*VxIL*jzOi6dXl?5?wdOIn`&&GdA4ee+e7ADmtJN@1ME z^#-o*aJ`A^%47J!_yJr4e*|9u?}N{Se}WnK4|oQA`*EdS0@uOUz-{m|@HY4s_!IaM z_&azNTz{h7=fLxLJ_hCdZ^2i<@4;8WU%(acH}DK%JpeD``43FK2)_PgtLFl^isyGh z(bEH821lUi`5e3rehm`%H@FR+drGNIunTs;FToA)8&LND3Z4V+gQDv%5PxdrX{8`X zJ&Q}cdk)up$al;KmMKVXJd-)`NCv_$#xuB+STbNwGbD!Sk#CV99?18qYg9)LbZpX) z(om=7(dmjw+rYY*65lm3#avzMCo|EBn-x**RoYn6MG-hgwNFDmVUiuB9j-DkQ}Q^I zsmq~?9`%o-yXjPZmxHWaeS!CJY1~c9fOQ481C3FOxWUh|Jl{AnT z&Fi@ij$|T@gCClnNnGS~Xjw<5SdYhcN}38Y7;s>hmN(|A3!&-Q(|D$h28L}Bv7vD1 z6ID9(hq4OiFNKtVbE}Lf<3|dL-4ktVrmBvK`rak@K;A*D<&-$Yi{$F6hnvRO+7-C% zd$^vfn@FAHN`30<_JAedu^YZ438C?hgL=r;5l>Y+Kl2o>4UFagos3GVL>&fyhx;kp zFS1)uflO>saYwt-XG=$INer}uAmKgc#u*GYVoOSb3wzCg)s z(I)Duqjn);V2}#Qy+u70C`3oSP?va9aY2P7Z#Lod#G!vi=pVdo7qQyQq_B0 zAbWBTRp3k1f1k(5pRDJE$ZRFooG)5taYL&mzDbvB>O*#I^R$t$D=s$=2WcCTS4z@n zl{}LW2*J!~B&BFgnR2>ya?h4DVTVV^!Uv+Y@!K3a7rx7MY0)$Twh+BI2zqjO<&e3D z_sIK&ZKg3KryX|F^xt4PDaZ(gLEP%(3mVmq#Q34N@Vw_C4F7n-MM~O=ai{%}tDPnt x;=bCgDUPsW;p?)kH1Lp=7LCI5Y>9{N, 2007-2009. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.8 \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: Rune Bromer \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:23 +msgid "Activate users" +msgstr "Aktiver brugere" + +#: admin.py:43 +msgid "Re-send activation emails" +msgstr "Gensend aktiveringsemails" + +#: forms.py:35 +msgid "Username" +msgstr "Brugernavn" + +#: forms.py:36 +msgid "This value must contain only letters, numbers and underscores." +msgstr "V¾rdien mŒ kun indeholde bogstaver, tal og underscore." + +#: forms.py:39 +msgid "Email address" +msgstr "E-mailadresse" + +#: forms.py:41 +msgid "Password" +msgstr "Password" + +#: forms.py:43 +msgid "Password (again)" +msgstr "Password (gentag)" + +#: forms.py:55 +msgid "A user with that username already exists." +msgstr "Der findes allerede en bruger med dette brugernavn." + +#: forms.py:67 +msgid "The two password fields didn't match." +msgstr "De 2 passwordfelter er ikke ens." + +#: forms.py:78 +msgid "I have read and agree to the Terms of Service" +msgstr "I har l¾st og accepterer betingelserne." + +#: forms.py:79 +msgid "You must agree to the terms to register" +msgstr "Du skal acceptere betingelserne for at registere" + +#: forms.py:95 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Denne emailadresse er allerede i brug. Benyt venligst en anden. " + +#: forms.py:122 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Registrering med gratis emailadresser er ikke muligt. V¾lg venligst en " +"anden emailadresse" + +#: models.py:165 +msgid "user" +msgstr "bruger" + +#: models.py:166 +msgid "activation key" +msgstr "Aktiveringsn¿gle" + +#: models.py:171 +msgid "registration profile" +msgstr "Registreringsprofil" + +#: models.py:172 +msgid "registration profiles" +msgstr "Registreringprofiler" diff --git a/thirdpart/registration/locale/de/LC_MESSAGES/django.mo b/thirdpart/registration/locale/de/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..b272d4463c4efb5dec112c20a9fec3364013adfc GIT binary patch literal 1999 zcmbW1O>Z1E7{?8ix8=RONJt<(97Jg6dO_ZqXJ_2ZcviM2 z+vLnAK;p!KFMz6Y0SR%7#F0BfsyO$=kt6?Sb~kCNT)>j|XV2Px{(fHk;Ov=i71~+! zchG-8e;0l63H+el2QPv@f-i%Af-iv&K@I)`J_o-4q*AYfv*6p{GWZpE8$1X80$vCI z0++zqr^fpncpmc+DCd6%EA={f1C)IUcp3Dd=(z{J z1^x`a2mTI5@NaMi}0M zCMLP8!ucmmiovO?tSICEWE8viOi?qHb%>NyF2WB~IfyMdM)qNbTwV4tXdGL+6vL!~ z*V}3bp_5ptFROZdzzM%&H~fwu_{Mka)gx}*<$X2&KJh8MHn0Vs`Y|ZU61C;4JDfzl zk?Jp?0+HxaddIk|(kG7Uv=^wg&BMw^y%{o_>eV_6x>t3nwRNMXuOLq(j(QFG)TQ}O z=aQc5=(z=&pX+`&|3L>|uuyOFo)s&{>psy~I)91gKJI?h8Lzu$LexEPTqsPee3w2q z&cUtM*rr_2BJ^RpTr|_Rb%ScDbq$(pld@1;Ih$=-&Piq9fmpv%><>x=cxKeBk)$PVcH-I9?~3kZgM0B~^-q?a~6WvZdB6 zOUpIuu`myp>-@mWb+wXQc&5PGF4JXRafotr)*QJJpe zz}jU2C$M#W<1wL(3)n+yqOL-;C2HH|o(}41`Ng9FT^LzLg5_1gsK%9vRXI|R?Gp6D zTO?HqF}Mm%Dii9&T4_;Q*5Q7;Re-;WN^GAL-Q2c@H+?W!Y}2Ymrb8$0OgC|&?3ZlE zEgs)#;1x02mvB02UXsI~JdPr~2`M?7, 2007-2009. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.8 \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2009-10-18 21:32+0200\n" +"PO-Revision-Date: 2007-09-29 16:50+0200\n" +"Last-Translator: Jannis Leidel \n" +"Language-Team: Deutsch \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:23 +msgid "Activate users" +msgstr "Benutzer aktivieren" + +#: admin.py:43 +msgid "Re-send activation emails" +msgstr "Aktivierungs-E-Mail erneut senden" + +#: forms.py:35 +msgid "Username" +msgstr "Benutzername" + +#: forms.py:36 +msgid "This value must contain only letters, numbers and underscores." +msgstr "Dieser Wert darf nur Buchstaben, Ziffern und Unterstriche enthalten." + +#: forms.py:39 +msgid "Email address" +msgstr "E-Mail-Adresse" + +#: forms.py:41 +msgid "Password" +msgstr "Passwort" + +#: forms.py:43 +msgid "Password (again)" +msgstr "Passwort (wiederholen)" + +#: forms.py:55 +msgid "A user with that username already exists." +msgstr "Dieser Benutzername ist bereits vergeben." + +#: forms.py:67 +msgid "The two password fields didn't match." +msgstr "Die beiden Passwörter sind nicht identisch." + +#: forms.py:78 +msgid "I have read and agree to the Terms of Service" +msgstr "Ich habe die Nutzungsvereinbarung gelesen und stimme ihr zu" + +#: forms.py:79 +msgid "You must agree to the terms to register" +msgstr "Sie müssen der Nutzungsvereinbarung zustimmen, um sich zu registrieren" + +#: forms.py:95 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Diese E-Mail-Adresse wird schon genutzt. Bitte geben Sie eine andere E-Mail-" +"Adresse an." + +#: forms.py:122 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Die Registrierung mit einer kostenlosen E-Mail-Adresse ist untersagt. Bitte " +"geben Sie eine andere E-Mail-Adresse an." + +#: models.py:165 +msgid "user" +msgstr "Benutzer" + +#: models.py:166 +msgid "activation key" +msgstr "Aktivierungsschlüssel" + +#: models.py:171 +msgid "registration profile" +msgstr "Registrierungsprofil" + +#: models.py:172 +msgid "registration profiles" +msgstr "Registrierungsprofile" diff --git a/thirdpart/registration/locale/el/LC_MESSAGES/django.mo b/thirdpart/registration/locale/el/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..acc972683e0c03dee9cf816301d9653f1f8bccc3 GIT binary patch literal 2424 zcmbtUO>Y}j6n#LUU_Mn55CWvjiner&-4aOLQq?4G72%{Iae}&n7yHE?YR_0Rr3E=*#C^fI`E}Oh1drE3j7@S z^Ay7hpLKr003n)neQLJQGD1v#e3Q!+==!JGG#qQIq~ zpeUofl27g^4JvQU&MGmW^0ps%GO%5TD!%RHX_Wm{N!mq{xtynbU-@MFRQBA0Jz@tc z7p4B9lD;CpQZ5(ANm9-p9aWxkg2NZ1B2&Q4L%}$dEHb@uvN@-E&~UioIkKbm(g!D+h>kt>HD9$UXGN^7AUYwO2}+rk2N}eU5$%F z-4XZvTMIfV^B{UkG%__ug<*`^MRlan7u+H9!)+4%p8JW)23Akb8b(=|bb|VmUGFEa zq-Z!bFxYdoFEXIYt`}Ine%{Vm?^W`?m2ndk>A#w>x-cu2ht(|um7ue+*gICN(~5Ue zyfbkwX2sfLF*sTSYRu-~2mRu4EB+RpjVIb;Z^Upjl9qm8WjyKlMH#qWf-VoG)6|#j zP0@u^Z?Y$SDcRjUkQyAk2v;Xxk$Gihlq@AEo$R|Z#FQ_j)0c{N)b;WgBfUMnsr{nj z(O9GlbA=AA45pBvK-~!5EEmzg^OP+}&sV{_Lz&A~$G#tPfp)EwlXY{@e}Xz7@`%vi z=q+8ovVa;dD5wcf+}GUf07V(z|+-!Wq36&W2N@7sAQr61Ey!eheqD znADpHvWiesLf<64sn_*7tZU(%Ug03ih&xYuBb;Ynq^)Cv{F}&jCtO6XX{?cxbWQKT zV>_JTT)GaYZRA{J7Hi=b@ZT^8AclnCMKDi*`#LtvV-YW0^;VShEu4XcscnbL*$9`w zWClgSR>i$N#7FT}B;C=qD6=;$SjRcDn8Z2ggGcQi<3%u@HH=|;V74e>5*?d1arc@G zP)dU{=@s;)_TQ*~g(1y^3uayvE`akMxUZ2(xnk;p-79&Gyq~(CEY~Z!T!`R$l`<%-gBm1RRIfs7F)Qt z%HLPrG>tXS8eTPu!UrjuU)BH8s4!o^KUm$zOeghSOs=l0r$jhmW;+K$JAeBZ0Jr;| PvNps)m9q2E`W1fx4LxjP literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/el/LC_MESSAGES/django.po b/thirdpart/registration/locale/el/LC_MESSAGES/django.po new file mode 100644 index 0000000000..cd38eb1b46 --- /dev/null +++ b/thirdpart/registration/locale/el/LC_MESSAGES/django.po @@ -0,0 +1,84 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Panos Laganakos , 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-19 19:30-0500\n" +"PO-Revision-Date: 2007-11-14 21:50+0200\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:38 +msgid "username" +msgstr "όνομα χρήστη" + +#: forms.py:41 +msgid "email address" +msgstr "διεύθυνση ηλεκτρονικού ταχυδρομείου" + +#: forms.py:43 +msgid "password" +msgstr "συνθηματικό" + +#: forms.py:45 +msgid "password (again)" +msgstr "συνθηματικό (ξανά)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "Τα ονόματα χρηστών μπορούν να περιλαμβάνουν μόνο γράμματα, αριθμούς και υπογραμμίσεις" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "Αυτό το όνομα χρήστη χρησιμοποίειται ήδη. Παρακαλώ διαλέξτε ένα άλλο." + +#: forms.py:68 +msgid "You must type the same password each time" +msgstr "Πρέπει να εισάγετε το ίδιο συνθηματικό κάθε φορά" + +#: forms.py:96 +msgid "I have read and agree to the Terms of Service" +msgstr "Διάβασα και συμφωνώ με τους Όρους της Υπηρεσίας" + +#: forms.py:105 +msgid "You must agree to the terms to register" +msgstr "Πρέπει να συμφωνείται με τους όρους για να εγγραφείτε" + +#: forms.py:124 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Η συγκεκριμένη διεύθυνση ηλεκτρονικού ταχυδρομείου χρησιμοποιείται ήδη. " +"Παρακαλώ δώστε κάποια άλλη." + +#: forms.py:149 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Η εγγραφή μέσω δωρεάν διευθύνσεων ηλεκτρονικού ταχυδρομείου απαγορεύεται. ""Παρακαλώ δώστε κάποια άλλη." + +#: models.py:188 +msgid "user" +msgstr "χρήστης" + +#: models.py:189 +msgid "activation key" +msgstr "κλειδί ενεργοποίησης" + +#: models.py:194 +msgid "registration profile" +msgstr "προφίλ εγγραφής" + +#: models.py:195 +msgid "registration profiles" +msgstr "προφίλ εγγραφών" diff --git a/thirdpart/registration/locale/en/LC_MESSAGES/django.mo b/thirdpart/registration/locale/en/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..87b6226b0474fd7c3200c643a899749a0a765975 GIT binary patch literal 367 zcmYL^K~KUk7=|%=+R?Lz9=z#?9gzeo5-KaW*llDC61`QZa|YX`D@K2af6w3Ix5UVs zJZY0Y{rdLj^yIsPIYQ2mbL1GgMA{6HJ{}HnZJqxtoPE<95Ahb(+BKCmj4b%{fzJh5 zi+mYpX^`vMIuA)xR$I8+mkkt_kzkP(Fm+mayabW*nvou*LkKB^JrZ6s+|xd#C_=73 zs&@FF_b7M{Nd^%o_Zds#i>E9VfmDr$!YR}2l(&wN*-A>1@Rm@;;@Y*gf+JP_|yRA&g$l$s1LFX3&>0-11 literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/en/LC_MESSAGES/django.po b/thirdpart/registration/locale/en/LC_MESSAGES/django.po new file mode 100644 index 0000000000..e357a4cef3 --- /dev/null +++ b/thirdpart/registration/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,89 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2009-10-12 14:09-0500\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:23 +msgid "Activate users" +msgstr "" + +#: admin.py:43 +msgid "Re-send activation emails" +msgstr "" + +#: forms.py:35 +msgid "username" +msgstr "" + +#: forms.py:36 +msgid "This value must contain only letters, numbers and underscores." +msgstr "" + +#: forms.py:39 +msgid "Email address" +msgstr "" + +#: forms.py:41 +msgid "Password" +msgstr "" + +#: forms.py:43 +msgid "Password (again)" +msgstr "" + +#: forms.py:55 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:67 +msgid "The two password fields didn't match." +msgstr "" + +#: forms.py:78 +msgid "I have read and agree to the Terms of Service" +msgstr "" + +#: forms.py:79 +msgid "You must agree to the terms to register" +msgstr "" + +#: forms.py:95 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" + +#: forms.py:122 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" + +#: models.py:165 +msgid "user" +msgstr "" + +#: models.py:166 +msgid "activation key" +msgstr "" + +#: models.py:171 +msgid "registration profile" +msgstr "" + +#: models.py:172 +msgid "registration profiles" +msgstr "" diff --git a/thirdpart/registration/locale/es/LC_MESSAGES/django.mo b/thirdpart/registration/locale/es/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..3872adf0a0eed30b1ec9fa6410addb3c6db915cf GIT binary patch literal 1909 zcmb7^zmFU>6vqt&0tegqGU+h77t!JFWZ;78!eM}>GBybfLfzX4wZe+4(dzrkn0R~{4M6!<#$ zB)A1eU+;l0fCu1F{1NXF zZN>rf0-<&z#=jiVCZrGuv(uvoK7}cEXe=j8j%7j8V3v-Vf~7EK%GuSP?1-+in`p)2 zfJ^OzlR;ZUt=FccQQXh9)D_91aO^#M(w-V;$NEqQE>haBSb8SkHcd4pNrfJb*s%%g z2UC%cadORAG$pGT-jsCo$^X|JjyNN0UbPhDEjITIsj;;PPa2C$yHs3TobaTiA!`t` zvL;CQuQ&vD{ydqsKE$HatThFe%Hs0k6WdbV`min}&Ok`0>N)}09q$X%26{tmA7gB! z_n%o;5KA?tplcRV1wEN7ev7AKO&zh^e{VtOWQjmG#N5*&RY=B2SNyLZy@(2Rk0_Ja zclL-?NOp?kIT=Udyi}TgUrigedJ~Xp}9`9s}A!5A?)` zjMAC?Tv$T=D{c^ZWl<5cpIogCRs{;6Ycg%cuv*lKvnY6UC17ILI95u}?wYt8<>uHD zSICvax2QuI(~So5VfOv90SlY%TSp@~!4q|Wd?~0c$)b-}Y^Gv&_4U78Oy$D7*W3l> zWg*c?OBWCIF!X`R*!s|MwBPzxIt@vdHmzzKiGYvI!I`op$6&44g--L+onDLMp8V`? zWvOYoVAFLf8+Nf(os6A2`=w@wqf;td+#Ekohw{jJk>dt<`Lspr$COB%S<6Wyq0qAC zUEtLd+90B$RT_sR)!Nsxa}Ad~yMr}kp$UI3R26?;aoc$xcli?!r1HK@iuil8Ftxg- fb6wV8cSAJn(9_ZKB1F6RggdQ%T*xFI&|UlkAoEAZ literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/es/LC_MESSAGES/django.po b/thirdpart/registration/locale/es/LC_MESSAGES/django.po new file mode 100644 index 0000000000..ba0384fe54 --- /dev/null +++ b/thirdpart/registration/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,85 @@ +# Spanish translation for django-registration. +# Copyright (C) 2007, James Bennet +# This file is distributed under the same license as the registration package. +# Ernesto Rico Schmidt , 2008. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.3 \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-03-11 00:19-0400\n" +"PO-Revision-Date: 2008-03-11 00:19-0400\n" +"Last-Translator: Ernesto Rico Schmidt \n" +"Language-Team: Español \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:38 +msgid "username" +msgstr "nombre de usuario" + +#: forms.py:41 +msgid "email address" +msgstr "dirección de coreo electrónico" + +#: forms.py:43 +msgid "password" +msgstr "contraseña" + +#: forms.py:45 +msgid "password (again)" +msgstr "contraseña (otra vez)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "Los nombres de usuarios sólo pueden contener letras, números y guiones bajos" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "Este nombre de usuario ya está ocupado. Por favor escoge otro" + +#: forms.py:71 +msgid "You must type the same password each time" +msgstr "Tienes que introducir la misma contraseña cada vez" + +#: forms.py:100 +msgid "I have read and agree to the Terms of Service" +msgstr "He leído y acepto los términos de servicio" + +#: forms.py:109 +msgid "You must agree to the terms to register" +msgstr "Tienes que aceptar los términos para registrarte" + +#: forms.py:128 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"La dirección de correo electrónico ya está siendo usada. Por favor" +"proporciona otra dirección." + +#: forms.py:153 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"El registro usando una dirección de correo electrónico gratis está prohibido." +"Por favor proporciona otra dirección." + +#: models.py:188 +msgid "user" +msgstr "usuario" + +#: models.py:189 +msgid "activation key" +msgstr "clave de activación" + +#: models.py:194 +msgid "registration profile" +msgstr "perfil de registro" + +#: models.py:195 +msgid "registration profiles" +msgstr "perfiles de registro" diff --git a/thirdpart/registration/locale/es_AR/LC_MESSAGES/django.mo b/thirdpart/registration/locale/es_AR/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..ce8b4e56c8250021193dd307b05b803d3fbba025 GIT binary patch literal 1849 zcmb7^&2Jk;7{&)E6wFttKp=s{dus!%aX^K{O{18^ZLQi#&=%4NF8bRH{P9j=ACEW*AFgV_)4H% zLVpeYYxI}H_z^tN?t>9H13v=41vkNqj|%Y~xCdSbzXY#3R>7aZ9{4A?2HtpL zzW--n(EqOHFW}So{X6&y_yD{N{#Ac(T*R61`*l2C0Iz`9zG@->N8%jtXlgYF~LynzWsLqYE6<1pDVc@vW~9QN{IGIZVztcqw33(iSRV_;6Jrpb)CPyPO|e_Aa1={TMkD6f zXfLsU#@@4aZ;>wZeGDqkN}XUB8ysHTH6`Vx^Jk638W;&zZDk&g zAw8lE>A6ksv|8^+t>tKOnHHDZ@3x}WwN?ugqajaK5U=Om?RJLIU@O|_(Cw}Ep!ep7 zTbtcRUpgO+tkf=(-q<$X-0k;ivoq+@N_Wud_18KZ8^i9-&MKzrw3I1FBbIra`kl>N zyCLM2et#`fO=Hv5#-KOoo*Zhi*=nrgUXh2<2)EEC&-eY?MF#(_Q#_H@vA?l9x*08< z%nPv~ccZS3O@jPy(-M-rA#O8r%_1!R);_8YC)-qqpGe_++wMaJrF+p=BTm~i3B#5#&U`$T)&pt9gPPzbxnGihtQH{sFN^CdB{% literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/es_AR/LC_MESSAGES/django.po b/thirdpart/registration/locale/es_AR/LC_MESSAGES/django.po new file mode 100644 index 0000000000..fb746b5b96 --- /dev/null +++ b/thirdpart/registration/locale/es_AR/LC_MESSAGES/django.po @@ -0,0 +1,83 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2008 Leonardo Manuel Rocha +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-19 19:30-0500\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:38 +msgid "username" +msgstr "nombre de usuario" + +#: forms.py:41 +msgid "email address" +msgstr "dirección de e-mail" + +#: forms.py:43 +msgid "password" +msgstr "contraseña" + +#: forms.py:45 +msgid "password (again)" +msgstr "contraseña (nuevamente)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "El nombre de usuario solo puede contener letras, números y guiones bajos" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "Ese nombre de usuario ya está asignado. Por favor elija otro." + +#: forms.py:68 +msgid "You must type the same password each time" +msgstr "Debe tipear la misma contraseña cada vez" + +#: forms.py:96 +msgid "I have read and agree to the Terms of Service" +msgstr "He leído y estoy de acuerdo con las Condiciones de Servicio" + +#: forms.py:105 +msgid "You must agree to the terms to register" +msgstr "Debe estar de acuerdo con las Condiciones para poder registrarse" + +#: forms.py:124 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Esa dirección de e-mail ya está en uso. Por favor provea otra " +"dirección." + +#: forms.py:149 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "La registración con un e-mail gratuito está prohibida. Por favor " +"de una dirección de e-mail diferente." + +#: models.py:188 +msgid "user" +msgstr "usuario" + +#: models.py:189 +msgid "activation key" +msgstr "clave de activación" + +#: models.py:194 +msgid "registration profile" +msgstr "perfil de registro" + +#: models.py:195 +msgid "registration profiles" +msgstr "perfiles de registro" diff --git a/thirdpart/registration/locale/fr/LC_MESSAGES/django.mo b/thirdpart/registration/locale/fr/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..f8911c7ec65960e02435383ea87d2d79adc420db GIT binary patch literal 1883 zcmb7^%WfP+6oxAh2n_cK#DXB5-NcX{+KvEwVxk0-fRPQBGq%JERl3gfl+#_+)McHPwJu=c?WJODojAAz5MmmU-19dH-C0e%bi!C%1z@GtOr@U_Q@$I z%iuQn3Rr>{!LPt{?rU%zvAzLU@%=mgSb~?Iz&`j8O#8opTi}H!g;)c(!LzhzY*T(Ov^(8Tx<|4}E7>5`cBTA} z7u*O)A&_RPM-O}&k65RPoH99VPCEJsz`T6Xp#EVl;u}oU`Og zRw=y$(kUkYUw1HZM%H}RQ;-kXoH!~b)*?M=EH3Q^V&}-hQy~pmgPaR%f<*j^Lty8x zk%{$vytJA{qwrc-TwZ);Bh}G|c_Co~A)%^k1!Q-6E*vz_8)EyEV)gq)_{lWD;A>?r{;ab(QVn zTB!7p)Sfh@&E|aOgX`4W=Ek~^ZTeEH?EP5!Y-ESj+xm2rt)XbCPqGhW;2|yL`MX)Z zk}a;#;>z%Pp5<@nISyvqJk=@6xpPZ7E#^4)X1<7X8`6htD=f?2$x6t&ZKTN)N0nZ}v9VH}9NYba9aP z)=+EoW;Q})h7@=|ywz0D^ad3Z={$$`c1Cxz<&Fs+0w5|F(L08(hs)>!hHokDWX=e>HT2M4-(r(0# zhD7zFL2uSgU@B4Ts4-G#K8+hV#K5rZa@1AD4~Y%7mHr~K>paEacp%nNN1t<>T&UUM z-t0$;ZA!Dl|Ky1p&r#Vng>&}viD2ST^OOh4E{zTJerJqJHXK7ca%^x28IyuOY4$K; z*H8xh|G--kwqgva!;H*+a+o0-D_ug-f!MVXlJXQc@5INolggOnWAUNhso>Pu=xCa( zI^i9Q6YWlfL%4eOGX}M`kf@tiWQPW5ohy#}sY~j{i=slgRsVE+#a!*gSr2m`bajfB g&|_V+ov4mQ|9dWdoy5{We@r5`#(djAv0Y}F-M*si- literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/fr/LC_MESSAGES/django.po b/thirdpart/registration/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000000..34b520b8be --- /dev/null +++ b/thirdpart/registration/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,81 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Samuel Adam , 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.3 \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-19 19:30-0500\n" +"PO-Revision-Date: 2007-09-20 10:30+0100\n" +"Last-Translator: Samuel Adam \n" +"Language-Team: Français \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:38 +msgid "username" +msgstr "pseudo" + +#: forms.py:41 +msgid "email address" +msgstr "adresse email" + +#: forms.py:43 +msgid "password" +msgstr "mot de passe" + +#: forms.py:45 +msgid "password (again)" +msgstr "mot de passe (vérification)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "Le pseudo ne peut contenir que des lettres, chiffres et le caractère souligné." + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "Ce pseudo est déjà utilisé. Veuillez en choisir un autre." + +#: forms.py:68 +msgid "You must type the same password each time" +msgstr "Veuillez indiquer le même mot de passe dans les deux champs" + +#: forms.py:96 +msgid "I have read and agree to the Terms of Service" +msgstr "J'ai lu et accepté les Conditions Générales d'Utilisation" + +#: forms.py:105 +msgid "You must agree to the terms to register" +msgstr "Vous devez accepter les conditions d'utilisation pour vous inscrire" + +#: forms.py:124 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Cette adresse email est déjà utilisée. Veuillez en indiquer une autre." + +#: forms.py:149 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "L'inscription avec une adresse email d'un compte gratuit est interdite. Veuillez en indiquer une autre." + +#: models.py:188 +msgid "user" +msgstr "utilisateur" + +#: models.py:189 +msgid "activation key" +msgstr "clé d'activation" + +#: models.py:194 +msgid "registration profile" +msgstr "profil d'inscription" + +#: models.py:195 +msgid "registration profiles" +msgstr "profils d'inscription" diff --git a/thirdpart/registration/locale/he/LC_MESSAGES/django.mo b/thirdpart/registration/locale/he/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..be936503238a6df8192233f1fe242e7e60d41895 GIT binary patch literal 1896 zcmb7C&u<$=6ds@uFh9i)Ayf|UMpL@R2?7Nd5KxMUs38h&MGqC4>^`rT*t^!uIIRy{ zxFRG+wEM2 z%$G5Tm@mru0sJs-00UqL_$BZF_$hGdL8aaVUICs5eg}L7_#1E;_!sag;OU2ydJ=dR z_$Y7 z1HOfA5BMDLci>CFd%yzl@9Fu{(xlGQK)HV%DEAfM)4(r)2%ith{|QXVPwtL})baRq z16qYt^C9^k4bUh^p^#>-hs1acQ>{~7?=TtG5$O~&Hq7KQa&@LEo2I3#MxV3UNkUee z94FQp?UF2|woTHQYGTh#ois>Ckzs4uQevrPvU;+WIF5?6(O_+vY`fKJbVyT_)M{)v zb&HNgRjtEwflR1$L(=O|A~E^@(!s@~y2Aryg*v4y zMb2TCI*s@Zc8-lbM`^pcg+;DeJB_dmGZe3`W^HP=ty>flJ`fVBnp{9OGdtI5L2rm{ zCC8Sw_IhR_Vjb4WB~4a3bji*{@ony?1$ESs{qcg%=opDEt4XA3tI&*E(%=&htrCUu zpU9*(O!gIrE?AF(D<}({E>F{*kQFw$l^GXQY@9^F2kqDf)vQc~jnAsV1vEF-e^p z3BBFK?!D%=&1$X~X2qoOZsC$n0{n9e7#UvuUwp!vQjJ+E})xueL)r4 zm&x%B_gbp~)y`8`*T%AY=W_L7t=6{@aoI7hNrFkfj{(JKlTIiec$tU z{Vl&Y*$x4BaEF_oANc{!`+lUbImBj!&293#{%8L^4#p%-*kW-LS6zQd_zl7TBd+^k z0?!XHxdWbom~|1X?+=S4Q3$4Y{k}gSBrwK#&eVkmxQlB3ur~|PyFbJjBz;izglPA_ z>E{wXiZha;dPs4EEby1Ia>D!Qc99#>mpb5#6i2y0C~VGofF$}TZ6LHm5Z}UbNGJ-0 zc5x-5;eU?u>P09J^r2cGU+y&qy$AvU{cQ!gh3%&an{;bReR8kRr-|i3?sijx=F&@K zNF)s(VD8CgALP=TJkB1r2rVXZj@;Eu_kT53%~, 2008. +# , fuzzy +# <>, 2008. +# +# +msgid "" +msgstr "" +"Project-Id-Version: registration\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-02-10 02:01+0200\n" +"PO-Revision-Date: 2008-02-10 02:05+0200\n" +"Last-Translator: Meir Kriheli \n" +"Language-Team: Hebrew\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit" + +#: forms.py:38 +msgid "username" +msgstr "שם משתמש" + +#: forms.py:41 +msgid "email address" +msgstr "דואר אלקטרוני" + +#: forms.py:43 +msgid "password" +msgstr "סיסמה" + +#: forms.py:45 +msgid "password (again)" +msgstr "סיסמה (שוב)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "שמות משתמש יכולים להכיל רק אותיות, ספרות וקווים תחתונים" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "שם המשתמש תפוס כבר. נא לבחור אחר." + +#: forms.py:64 +msgid "You must type the same password each time" +msgstr "יש להקליד את אותה הסיסמה פעמיים" + +#: forms.py:93 +msgid "I have read and agree to the Terms of Service" +msgstr "קראתי והסכמתי לתנאי השימוש" + +#: forms.py:102 +msgid "You must agree to the terms to register" +msgstr "עליך להסכים לתנאי השימוש" + +#: forms.py:121 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"כתובת הדואר האלקטרוני תפוסה כבר. נא לספק כתובת דואר אחרת." + +#: forms.py:146 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"הרישום בעזרת תיבת דואר אלקטרוני חינמית אסור. נא לספק כתובת אחרת." + +#: models.py:188 +msgid "user" +msgstr "משתמש" + +#: models.py:189 +msgid "activation key" +msgstr "מפתח הפעלה" + +#: models.py:194 +msgid "registration profile" +msgstr "פרופיל רישום" + +#: models.py:195 +msgid "registration profiles" +msgstr "פרופילי רישום" + diff --git a/thirdpart/registration/locale/is/LC_MESSAGES/django.mo b/thirdpart/registration/locale/is/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..bccb71f63a9f5c116e21763deb0bd560da518ba5 GIT binary patch literal 1476 zcmb7@&yN&E6vs2W<_Fy*35q(O+uT6>*k1@m@S> zPmQyszM+F;U0SPzy^tR_O*JK^Oy{{cG2vifR}D)<9!M5baTUv(l8$@I%cet!GhEBF znF5c*JWZ6A)}lN%7EQaZ`qqxA9{WIH+F%wKgpctJdwl0lYiit{*yKCN9^x_ar# zGHoueZw#(q>#R#-T}W1ap|j-jxbVr)_NlXWeV8m^j`8S{&pAk+7J9vNNpC(`SfKfZ z{wL=??#=gla7@-^qGR&^^;=;dlA&YcD-PE6>GD>4&l$SvAQ5f6heSTTv1OeZIeoE+ z3hAb{{;adYrWkV}$xyiN(;$@!%F}6Qb+Ed;-`9M%*IBYAKqko$2hpb>KZG+)g%O^o zv}ETcT=;tUMY6c>7t4XPl4XTQVt7ImOzmBqZUZG633+?Z6kXaxhg?@uU@h~}-$BNC7`r(&Yr$a3>>AhJ&D%Q&e9lXDDvl9n{DiX> F)PM38!N~vs literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/is/LC_MESSAGES/django.po b/thirdpart/registration/locale/is/LC_MESSAGES/django.po new file mode 100644 index 0000000000..479e792a92 --- /dev/null +++ b/thirdpart/registration/locale/is/LC_MESSAGES/django.po @@ -0,0 +1,74 @@ +# Icelandic translation of django-registration +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the django-registration +# package. +# Björn Kristinsson , 2009. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2009-01-22 12:49+0100\n" +"PO-Revision-Date: 2009-01-22 12:49+0100\n" +"Last-Translator: Björn Kristinsson \n" +"Language-Team: Icelandic\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:36 +msgid "username" +msgstr "notandanafn" + +#: forms.py:39 +msgid "email address" +msgstr "netfang" + +#: forms.py:41 +msgid "password" +msgstr "lykilorð" + +#: forms.py:43 +msgid "password (again)" +msgstr "lykilorð (aftur)" + +#: forms.py:55 +msgid "This username is already taken. Please choose another." +msgstr "Þetta notendanafn er þegar á skrá. Vinsamlega reyndu annað." + +#: forms.py:67 +msgid "You must type the same password each time" +msgstr "Lykilorðin verða að vera eins " + +#: forms.py:90 +msgid "I have read and agree to the Terms of Service" +msgstr "Ég hef lesið og samþykki skilmálana" + +#: forms.py:107 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Þetta netfang er þegar á skrá. Vinsamlegast notaðu annað netfang." + +#: forms.py:133 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "Óheimilt er að nota ókeypis netföng. Vinsamlegast notaðu annað netfang." + +#: models.py:218 +msgid "user" +msgstr "notandi" + +#: models.py:219 +msgid "activation key" +msgstr "einkennislykill" + +#: models.py:224 +msgid "registration profile" +msgstr "skráningarprófíll" + +#: models.py:225 +msgid "registration profiles" +msgstr "skráningarprófílar" diff --git a/thirdpart/registration/locale/it/LC_MESSAGES/django.mo b/thirdpart/registration/locale/it/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..83ec9ddb1646693415fb7b3a37786f84d642c655 GIT binary patch literal 1864 zcmb7^OLH7G5XS`x1eRyDM_5DFa>`A zqut-Z7r;NkX!im5GWa+6I(X*Xgcm@3Qp?~o;8hUUw=MerEPC`W+M2caj<`3rcowd< zE#5P0Fry%af}5=#9q?&%wM7FS2|3|{*r1nAgo33o5bBE?da?&}OWa83qV}ZJJ~$5A z8fv^YB^|{5GGtv57ljk=#gq0_J3G+(I!KYyZYAu6d{fuen3xKEa3D@hIDIfx{Q*v% zG8Rp772O+?j(5tVn!^!icqk_=1wIrrHh4w3;=hz%aLXy!yg6G;Dl0EhNrBNT}*s0mU8f3*#DkLu?;?tl9gott&`4 zA5hRkQJe=onkarKV|7X$HQPTQ&;plmbWu%inuZF&IM9{+??UOUr-PNSG zO1;(Woo>=y?sl;;*^`luHjnqMU@cj?LcQgzyL_>Wlbvn$A?Z6dzT#kAMmKeCE2eFB z)-zo-n0^rCFn&&rg!gd5lns}X^o55RZ3V70;@R2R+SxpQMlbDlt|5}hyrd7^GYWDq zT&gQ5wnq7YotLnFyMH5DIo=oDK(;2ECbtDLKBE<+b?5WsBQXMfqko@l@V->kOMAWa za%Z<{99PK=i=byjrf%nceQAv*!}SG2Z>&>qajm1S>wKW`E))f!5?z~OmZY862;7LB z;3L8DLLWgo9hcgQ#7u4TG^rfNMTx13N-4C{M@M>EOl8aiPXDfRtdCPir<$&|6k^d?3@=4KiXB~!YUOIc~W zV&+eZe<_#txIX^5-);$-iBbKDGrT8RzulYNg%0dUwc~` zCO#=Kq?<~tHm2*kRVYS)$%m-gagD-&rje}zs~AK4jm8*;Sgvg-)I*xhi*!Dq1)MOA d9oa=SsnIqMW{ys*OcvUxpI(?qqE5)E{sRa2Dzg9p literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/it/LC_MESSAGES/django.po b/thirdpart/registration/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..00129b044e --- /dev/null +++ b/thirdpart/registration/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,82 @@ +# translation of django.po to Italiano +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Nicola Larosa , 2008. +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-19 19:30-0500\n" +"PO-Revision-Date: 2008-05-27 15:05+0200\n" +"Last-Translator: Nicola Larosa \n" +"Language-Team: Italiano\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.11.4\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: forms.py:38 +msgid "username" +msgstr "nome utente" + +#: forms.py:41 +msgid "email address" +msgstr "indirizzo email" + +#: forms.py:43 +msgid "password" +msgstr "password" + +#: forms.py:45 +msgid "password (again)" +msgstr "password (di nuovo)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "I nomi utente possono contenere solo lettere, numeri e sottolineature" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "Questo nome utente è già usato. Scegline un altro." + +#: forms.py:68 +msgid "You must type the same password each time" +msgstr "Bisogna inserire la stessa password ogni volta" + +#: forms.py:96 +msgid "I have read and agree to the Terms of Service" +msgstr "Dichiaro di aver letto e di approvare le Condizioni di Servizio" + +#: forms.py:105 +msgid "You must agree to the terms to register" +msgstr "Per registrarsi bisogna approvare le condizioni" + +#: forms.py:124 +msgid "This email address is already in use. Please supply a different email " +"address." +msgstr "Questo indirizzo email è già in uso. Inserisci un altro indirizzo email." + +#: forms.py:149 +msgid "Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "La registrazione con indirizzi email gratis non è permessa. " +"Inserisci un altro indirizzo email." + +#: models.py:188 +msgid "user" +msgstr "utente" + +#: models.py:189 +msgid "activation key" +msgstr "chiave di attivazione" + +#: models.py:194 +msgid "registration profile" +msgstr "profilo di registrazione" + +#: models.py:195 +msgid "registration profiles" +msgstr "profili di registrazione" + diff --git a/thirdpart/registration/locale/ja/LC_MESSAGES/django.mo b/thirdpart/registration/locale/ja/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..e0332b0e7cce033c22f9b774b117cf5d78e6becd GIT binary patch literal 2035 zcmbu8%WoS+9LEPJl$!Do0)YgF;Y0(hS+_hAH&lg^Ng zr0i}=X&!2uCWHV|X@I08O+zW~0y!WdapjD-O#I-;f53OPb{s=fff#B0*`578zrXSC z_ivjaSodK+iv0ohL+be!{9p}(8dw5f0~f(p!R@ya@*H>++zU>EkAuH}ZQ!5aJ>Vm^ z5pp;91b8Rd4ys%)f)9ZG;DewEZUf&0m0bWHM6PjA<@psK$G{!86H)}H!3-F?gOC*X zI@ksN40eJ$w-fRh*bS=qgW$cO4?YZ@t#hf)pTHeB{|(#;RzReWma1Q!srpom#!`LU zRewQRf>NWU>TfKBA&`Qg+(?fYsPd3*nq@G%+A%Bj09(To4)Gp?a;~~y6`(i77OKVq6XFfWO;gT{EcL8BC(Q7yq3end@)BuLhcxUr9_TJ+qR=*S zJyU&D7)Hj*@f#irq7;gLluV@0bq2WMY29hoC;%`H-cu zH0|0oJ)!MM(1f1ctv{)Ez^;c0PfNMX7CGiQZjv6(T6T%iLxY$a?HnE!St0h97$zAv zocKUtA6#s+$V{%KILjyLE3Ck5jxl%k^z6@BamO|H#d^DYyP91l;(F`=291lUr7)Hx z_4u&&bRh>p_fjLvT*18;j-(E1?M*vX6E3sXWgAW!e~2V)$E}YM8GI?j?`3#S2H%$7 z{4fc1WICZ{zRkiMd_oy#_$^R zn3DlIIxhns5+QVb^8RvT;L4!9cA~Zb0}L#X!B{g2jHLgr49}{8hpW*oIEBfVPkd4y zdtcp?n>ec?1NgipgO6l57Zv}t3>RgH`d9EuZg>@N+LG???{rsn{=3@JWE-jX7|zNN X;V}0LGWb}A%Uget4XF{dDLVNJiZWT^ literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/ja/LC_MESSAGES/django.po b/thirdpart/registration/locale/ja/LC_MESSAGES/django.po new file mode 100644 index 0000000000..afaaf948b9 --- /dev/null +++ b/thirdpart/registration/locale/ja/LC_MESSAGES/django.po @@ -0,0 +1,78 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Shinya Okano , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.4 \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-19 19:30-0500\n" +"PO-Revision-Date: 2008-01-31 10:20+0900\n" +"Last-Translator: Shinya Okano \n" +"Language-Team: Japanese \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:38 +msgid "username" +msgstr "ユーザ名" + +#: forms.py:41 +msgid "email address" +msgstr "メールアドレス" + +#: forms.py:43 +msgid "password" +msgstr "パスワード" + +#: forms.py:45 +msgid "password (again)" +msgstr "パスワード (確認)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "ユーザ名には半角英数とアンダースコアのみが使用できます。" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "このユーザ名は既に使用されています。他のユーザ名を指定してください。" + +#: forms.py:68 +msgid "You must type the same password each time" +msgstr "同じパスワードを入力する必要があります。" + +#: forms.py:96 +msgid "I have read and agree to the Terms of Service" +msgstr "サービス利用規約を読み、同意します。" + +#: forms.py:105 +msgid "You must agree to the terms to register" +msgstr "登録するためには規約に同意する必要があります。" + +#: forms.py:124 +msgid "This email address is already in use. Please supply a different email address." +msgstr "このメールアドレスは既に使用されています。他のメールアドレスを指定して下さい。" + +#: forms.py:149 +msgid "Registration using free email addresses is prohibited. Please supply a different email address." +msgstr "自由なメールアドレスを使用した登録は禁止されています。他のメールアドレスを指定してください。" + +#: models.py:188 +msgid "user" +msgstr "ユーザ" + +#: models.py:189 +msgid "activation key" +msgstr "アクティベーションキー" + +#: models.py:194 +msgid "registration profile" +msgstr "登録プロファイル" + +#: models.py:195 +msgid "registration profiles" +msgstr "登録プロファイル" + diff --git a/thirdpart/registration/locale/ko/LC_MESSAGES/django.mo b/thirdpart/registration/locale/ko/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..a29c7c3fb6f8ea2e27496d03402814559d11bb77 GIT binary patch literal 1947 zcmb7?%WoS+9LEPJFY_)Bfke^o0!>8LaUzh42~phCgor$>)Tz{7+O2o&EVXyd&N^uh zNFj1VlvpBAyG=?QC2FD)5s5@`MX1vMfCCp!NEH&|;vL_SxbPj@u@k8W7-{x1GduHp ze1E?O$Bz{l#&OIyF}E;JV0Jx%55_jw3hsc--1E#Pp}1i>&aSvec;R3kJf2|PhMMlN7|O2FP7TOY9SDc{FjelVHcAVn6AMvB zaEB&LcZ}RI-K}mdJ;g~+I$SpO6{opCe9Swt6N!gh?tf?D*1Zd$EuglmGU_cdpxZ4p1y`uiXbF31#=cI+|pvgvZePmv_o1t*b7r<0SUlVQf=+~Jmc z^r4?c5vV(1Q@S1-#Z8_x1RfaHNtaT(8yi!%VE>3qIh3Q;)RM;i}q0E)6Srcg47uZe4up%w2n^dcrS=`;Qc@V7qt;S zZYtyTd*{QU5p8f-I}@T$hJ%Cs?_L-l3i}3h;c8Jww?tBRZ6`<&13l3lbuNvQtu$Lv%$-dKr@p5R)P>!~0OgogS?RqR~@13gL8Z##)@-(de>xS3stKj7;{ zQP412bTW;oU4GSVPbX2O4=FaLJA%6(jYdD#PBrhTpwMJm*oxT(8Z}6#(2YJOizV;J zJt}W6$>J2rLRKB*^WNGr$>}Y5Yg4W)l((nIyT0SC6}^Q;d80&fWmeAJ_2y^2`E@_5 zEG>IqFEy`MZEogeai8SveL0h3vT(m#nv%sV$>M!^bGE#@O68yS$eS)H4>uPG24r!u zQrJ8&+Hg*l-1l;BU4EDIt}nC08)}g&TXN-^x4O})e3Dmpx{BCWvvOvQd0Qp#b`D-k z^?&8vtXwU~<$aZ4EvHIhQO>WEoLwY&bEcUWAvtejulZQTfA9nvAhqnCyp=x^lafki zm#gv>_vOz>X;I!$!p=1z^@Ead3P5|dUJWC@&l^Y zMi<*iE`I54p}c!jIPvb}Q0OUdZsV}fwc0dlKq}&Ed0E(3*AHDpnPqmTvXrOF^g62v YL6v+KxmI#HRQsW4jXJW%s@Y}iU&H$5Jpcdz literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/ko/LC_MESSAGES/django.po b/thirdpart/registration/locale/ko/LC_MESSAGES/django.po new file mode 100644 index 0000000000..d466420d7e --- /dev/null +++ b/thirdpart/registration/locale/ko/LC_MESSAGES/django.po @@ -0,0 +1,89 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Young Gyu Park , 2009. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2009-10-12 14:09-0500\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Young Gyu Park \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:23 +msgid "Activate users" +msgstr "활동 사용자" + +#: admin.py:43 +msgid "Re-send activation emails" +msgstr "이 메일 제 전송" + +#: forms.py:35 +msgid "username" +msgstr "사용자 아이디" + +#: forms.py:36 +msgid "This value must contain only letters, numbers and underscores." +msgstr "이 곳에는 숫자, _, 영문 글자만 가능합니다." + +#: forms.py:39 +msgid "Email address" +msgstr "이메일 주소" + +#: forms.py:41 +msgid "Password" +msgstr "사용자 패스워드" + +#: forms.py:43 +msgid "Password (again)" +msgstr "패스워드 (재입력)" + +#: forms.py:55 +msgid "A user with that username already exists." +msgstr "이미 같은 아이디로 사용자가 등록되어 있습니다." + +#: forms.py:67 +msgid "The two password fields didn't match." +msgstr "패스워드가 서로 일치하지 않습니다." + +#: forms.py:78 +msgid "I have read and agree to the Terms of Service" +msgstr "약관을 읽었고 그 내용에 동의합니다." + +#: forms.py:79 +msgid "You must agree to the terms to register" +msgstr "약관에 동의 하셔야만 합니다." + +#: forms.py:95 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "이메일이 이미 사용중입니다. 다른 이메일을 등록해 주세요." + +#: forms.py:122 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "무료 이메일 계정으로 등록하실 수 없습니다. 다른 이메일을 등록해 주세요" + +#: models.py:165 +msgid "user" +msgstr "사용자" + +#: models.py:166 +msgid "activation key" +msgstr "활성화 키" + +#: models.py:171 +msgid "registration profile" +msgstr "등록 프로파일" + +#: models.py:172 +msgid "registration profiles" +msgstr "등록 프로파일" diff --git a/thirdpart/registration/locale/nl/LC_MESSAGES/django.mo b/thirdpart/registration/locale/nl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..9e84eb3ed60d06c9e37b1d48fa0edf3b5410f230 GIT binary patch literal 1898 zcmbW1&u<$=6vqcBl$xKaia5aGAr2(aHBJgB+9XxeG>rmINrO{_IH2+F^Lpalnbpj! zT~~qw2QG*sH}3o$9QhA8@h5<|bKpC>c5I^xsf@h-%*?)d^XB{J?eAAF{V330#q%bf zpYXgM`cL5x?I9R}Q}8SBXYd|)`Dr0O0v~|e;1A$C;IH61_!syx_{K9ryac`rJ`Zkz zVXr;#8h8r624>(T@M|#GeG4Yo>wEA9#(&~vAAI{+A?}0UfuDoFfyZFyIU#O?-+}8h91s_1Sm_yn^w4FvR^Oi0$hZ`~)8%zE|p*<}aM%h1nSH z1wyS`h=1PTMnDRIFl)bc4B_Pw4ds~0vP?;8Jeg%CZ^#dsdTa|vW{aF2q<$=O)Ic z#4;I@R|N~1cr|VmAMsSIs3Yd~7adxY83J7w%}%paAsGXe^J6ES2nyAIK_+ox%{eDN zI!dDlI2JM;&%$n;wNAM-)ai?h>h9e;Jli`wpq*~_R<7E{W;a{MN5=OK~e{RH0%j(u(~&BcY0@ zz?g$|ND%5LnpGq2W1Buv4ACkSk@HnQS3GEpQS5A2ZN(jhcT1~7$)LPb6m$ryZ!0x= zLXSAfd?hZEma_OCPXFzqEe@MtYL^9$4rw$X^E?Q#T%|=gC8kuCF^)|5d@+~j_(=5` zWrfrSX$u1lXHr9TlzkBO@%b?Yd!g#4mYo, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: registration\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-08-14 13:25+0200\n" +"PO-Revision-Date: 2008-08-14 13:25+0200\n" +"Last-Translator: Joost Cassee \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: forms.py:38 +msgid "username" +msgstr "gebruikersnaam" + +#: forms.py:41 +msgid "email address" +msgstr "e-mail adres" + +#: forms.py:43 +msgid "password" +msgstr "wachtwoord" + +#: forms.py:45 +msgid "password (again)" +msgstr "wachtwoord (opnieuw)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "Gebruikersnamen kunnen alleen letters, nummer en liggende streepjes bevatten." + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "Deze gebruikersnaam is reeds in gebruik. Kiest u alstublieft een andere gebruikersnaam." + +#: forms.py:71 +msgid "You must type the same password each time" +msgstr "U moet twee maal hetzelfde wachtwoord typen." + +#: forms.py:100 +msgid "I have read and agree to the Terms of Service" +msgstr "Ik heb de servicevoorwaarden gelezen en ga akkoord." + +#: forms.py:109 +msgid "You must agree to the terms to register" +msgstr "U moet akkoord gaan met de servicevoorwaarden om u te registreren." + +#: forms.py:125 +msgid "This email address is already in use. Please supply a different email address." +msgstr "Dit e-mail adres is reeds in gebruik. Kiest u alstublieft een ander e-mail adres." + +#: forms.py:151 +msgid "Registration using free email addresses is prohibited. Please supply a different email address." +msgstr "U kunt u niet registreren met een gratis e-mail adres. Kiest u alstublieft een ander e-mail adres." + +#: models.py:191 +msgid "user" +msgstr "gebruiker" + +#: models.py:192 +msgid "activation key" +msgstr "activatiecode" + +#: models.py:197 +msgid "registration profile" +msgstr "registratieprofiel" + +#: models.py:198 +msgid "registration profiles" +msgstr "registratieprofielen" diff --git a/thirdpart/registration/locale/pl/LC_MESSAGES/django.mo b/thirdpart/registration/locale/pl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..1f2a228861c28105aae351cdeded9976e68bbd76 GIT binary patch literal 1769 zcmb7^&u=3&6vquLEE|3=AP#VNH@46TN!hY(cByt*id58=s-%nHf}EL`iIW*SvOUet zNN}h?NFWXzIH2Xmf&YM0xits)12}Ty58yxG>q+`cMMzk2J|53|&(Ggy&!1N>eJ{|S zMSlhTNA#D%_#ynE-3KGE1V06T2Dib>4-4@Ycn`b@eha<|{su09e}hkhFFzv0Q{Zdh zf+=_j{0t0s2Vfg(eFKKLzv5+B|1WS2Tz^!E8{ik z!t)*QIWPmC06z!u6JO8gzXPx0`6uvs@E0)H{XU=n1H|%m3;92Z9`bvpo|*lHeFUG- zU@nkq-9rAy?J@?CLLj|bj|O-JU2M=m?lW1I2}zBfT4wTw{D7&;Hg{zD^dZ}Q6|>mo zR5@>@S4LCeluoH1>^YYzBbg+YonuGJ(b$@S>M74jlXfzej>#3{aaNL~MD_b@S^M*j zP0<~|^IWhHN@gLwl2lle|8F?BSS@ot8_CNN>ytq7z!;<_wZW!sQ|!$>9K}+T(a1SA z+DojTvG;7IgX`s z6Jrx%84t*-oP~_N+Mg*t;!>PbM;zOq4CtCnk?4Y$tu!wciqTgY|Ldd^L81CD$Ru{G z8FK8SjU>8 zwN|6{Dz#TTZ?>Y=a;pWyXqWd@2zb`6y%e>VslC*>zWjQt4ZBV0eAKm4yG(jxJM_M^ zJfcrhlSsNX3=hpJJlsyhscXh2UxSlQ3z>4%Wtn$q$7IS4=+@@u?MyX|P1hP*8(ZtA z8)`ROjk~BR&S2C<1v=#UzJFt!LA#q252SVM-`?xK7p&c{ zC{}S?m0nH{I1eQ*GMVGBD3YU?$G#XI{XnS^+lncdl$+^crY2_kAXcy*a=20PP~pl{ zB^A@dGFAm8(&lEuslm0v!766gqDgyrKg3ng7hZ`j>zVQV3WdltM8G=eMC$Ut-6d&R zO=NBDM`ohcNYZJLN@5>gHtD0Xr`T2@iK8!@V*8>~*dkLUC$N)8Uy(1fk)aG<16wXq zT=p$)GgVrSuxpcyEKeU~td$g7_^P-Ht~2S(B)FeQx=6BDl2g#L!a=B|t4)smCS&F5 z1x>h51|K&!JuoyO(<`u2hFc{@SrJz#jV~uME{D1lwZb#e#LQz3x;8dC2*C?tc8AUu UY(5pfo+m{Vp?-7m#6>y9KORXKn*aa+ literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/pl/LC_MESSAGES/django.po b/thirdpart/registration/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..498fd5b6b3 --- /dev/null +++ b/thirdpart/registration/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,84 @@ +# Polish translation for django-registration. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the django-registration package. +# Jarek Zgoda , 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: 0.4\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-19 19:30-0500\n" +"PO-Revision-Date: 2007-12-15 12:45+0100\n" +"Last-Translator: Jarek Zgoda \n" +"Language-Team: Polish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:38 +msgid "username" +msgstr "nazwa użytkownika" + +#: forms.py:41 +msgid "email address" +msgstr "adres email" + +#: forms.py:43 +msgid "password" +msgstr "hasło" + +#: forms.py:45 +msgid "password (again)" +msgstr "hasło (ponownie)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "" +"Nazwa użytkownika może zawierać tylko litery, cyfry i znaki podkreślenia" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "Ta nazwa użytkownika jest już zajęta. Wybierz inną." + +#: forms.py:68 +msgid "You must type the same password each time" +msgstr "Musisz wpisać to samo hasło w obu polach" + +#: forms.py:96 +msgid "I have read and agree to the Terms of Service" +msgstr "Przeczytałem regulamin i akceptuję go" + +#: forms.py:105 +msgid "You must agree to the terms to register" +msgstr "Musisz zaakceptować regulamin, aby się zarejestrować" + +#: forms.py:124 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Ten adres email jest już używany. Użyj innego adresu email." + +#: forms.py:149 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Nie ma możliwości rejestracji przy użyciu darmowego adresu email. Użyj " +"innego adresu email." + +#: models.py:188 +msgid "user" +msgstr "użytkownik" + +#: models.py:189 +msgid "activation key" +msgstr "klucz aktywacyjny" + +#: models.py:194 +msgid "registration profile" +msgstr "profil rejestracji" + +#: models.py:195 +msgid "registration profiles" +msgstr "profile rejestracji" diff --git a/thirdpart/registration/locale/pt_BR/LC_MESSAGES/django.mo b/thirdpart/registration/locale/pt_BR/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..e81b6207be3808b1553a40605d4c5f7ea270a843 GIT binary patch literal 1796 zcmbu9O>Y}T7{>=FZ*HMbKuCzgb0ZO5;}i;wTdJC5Qw)BI96M5xS~T5#yqdFT=>u0aT1jaVx-yMdOXj|{O5W0w{xey5*TMO zU&s6s^Ht0pMqb5J@7B^U2x}dAufZTgKvO8g6rV#;Pc?=Cxmzod<}d7 zTmxSQH^EoH9(W2&!Dm1PUW9K8Ucm2%_)~+wfH%R9o?OIx2yWx|AK*LS`cpz|fVaSx zz%Rga;J4t5;1A37pTQ9SS1`o;bIHF!gssLIJWpeWypE5cQ8-f=*e;M_HG-eR@ibm= zbb++0bJxIUF-4mua>is?rX)3HW|_$w@)M>Z+rp6<(;nNIN>~gyQ_frImC;l>r863b z{ai?ulT1^~&aorqXll(w-BzB{I_>5x9g{1k)BJ!WrD{B8%i5nbtc&3UmM5GAS27Rj z9gqq!`MS z+jyxoD|L$3#NhDahAF8ioj)lgY#=05wUvNukM{Y4DfEWeF620s&V6QVN-UEJc~!8G ziC43Q;&=H#oKQ!s_OHnd`K^mZfaMM04#q0ye_eDU2va?RI%3zFJDm8aoksgO4KBDz zyREH{Ti0XSj|Y40ey=v*sj)ulx=f|f2W94>p=nZWw?B-wa7CdiqHXE9Nf#Q8_oBvT zw6RGWo6WZyQR8BxfsN6CXDT>f?z<7U22r;kZMW#dezV(t^OJrru63mI(a=ima_Nn2 z($1*Up!XnJl}_hcuIk2SS8LsN zH$FPlM!iwn!gZqjq9Md@lIMH=t!WPZE>kj**0FzoG~9_U9qkLbpk$+1CniPNH|Y{; zyC&k2^7;3~p+HaosliJ@##OBYq)a1c2l}g?}#4 zR{zJlHb?}qOIObCTV<&7eGgxX(5y!}GkPSSNHV3jrY?FgtB$?0lT+3bpAM5EryvX4 z&~a9VhMWIXur+u?pjE?(eUr?;LvVb5!y1j+&ta7GmQOwJ$7zcJ!bQN_Y|O`9273hA$lWeWz=Wow$l Qwd(Av#X}L$kqZ^YKLzI-!vFvP literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/pt_BR/LC_MESSAGES/django.po b/thirdpart/registration/locale/pt_BR/LC_MESSAGES/django.po new file mode 100644 index 0000000000..9e8addb6f8 --- /dev/null +++ b/thirdpart/registration/locale/pt_BR/LC_MESSAGES/django.po @@ -0,0 +1,81 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-19 19:30-0500\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:38 +msgid "username" +msgstr "usuário" + +#: forms.py:41 +msgid "email address" +msgstr "endereço de email" + +#: forms.py:43 +msgid "password" +msgstr "" + +#: forms.py:45 +msgid "password (again)" +msgstr "senha (novamente)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "Nomes de usuário apenas podem conter letras, números, e underscore" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "Este nome de usuário já existe. Por favor, escolha outro." + +#: forms.py:68 +msgid "You must type the same password each time" +msgstr "Você deve escrever a mesma senha nos dois campos" + +#: forms.py:96 +msgid "I have read and agree to the Terms of Service" +msgstr "Eu lí e concordo com os Termos de Uso do serviço" + +#: forms.py:105 +msgid "You must agree to the terms to register" +msgstr "Você deve concordar com os termos para registrar-se" + +#: forms.py:124 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Este endereço de email já está em uso. Por favor, informe um endereço de email diferente." + +#: forms.py:149 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "Registrar-se com contas de email gratuitos está proibido. Por favor, informe um endereço de email diferente." + +#: models.py:188 +msgid "user" +msgstr "usuário" + +#: models.py:189 +msgid "activation key" +msgstr "chave de ativação" + +#: models.py:194 +msgid "registration profile" +msgstr "profile de registro" + +#: models.py:195 +msgid "registration profiles" +msgstr "profiles de registro" diff --git a/thirdpart/registration/locale/ru/LC_MESSAGES/django.mo b/thirdpart/registration/locale/ru/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..1635b4ed687d4d256776033f8b02f48ae394b644 GIT binary patch literal 2470 zcmbtTOK%%h6uv-tnRitr5FieV(nifVPE=58(kLczON5<7iBqW+2u4>gu;k&qN}4q80wlI~mI$e^<`=N!J9q4~iBgG*E8Y3-wAJ9rJt4H!yo1!3U!bYy&O>p9lT{d=B_G&;tGgd;S$B>;rxR zJO?}s+yo8*{{r>`J06SPxxgdXPXXEg67Uf4N8roAUw{XIzXRVvth>Mw><>LI#Brbm zwgbNbehmB#_$=`76A_Ot;0f$Az(0YPfZf32lR{hvE&)B@aU}63@H^l~z+Zu{0pEH$ z;&-x%68H+vF9ShGJcG&Fam?1>JX!48;0h&7luEa~t9po_sG(`-PwdKJY zyugbT)s*yU$_XYYn6Lx0b?u5IyX;H5FirA;qXHEdseIr}+JQ6{N~9~cQzpAm@TF2> zkS6R&N&JFr7qjF`Nr4BSl5*0oDDsLlCjCh#FU5$h)RgBJb{2HhF4>OTCPt;DB)^Y> zIi5?B11a2;z{9r#KC3CmEm4tUnClh~smM`O^}Pw_yc5VmoJPvhR+3b$S}jkLO$Db| zl)iL>-4Ej;2cp50N7W`bDmt=UPdx`M?@1SZBZjl}OS^s{g~(=`@{5kB$uv z55-1h)$;=@t4dD6dcRguR?bUOY-BiR^`kwweAWQkC`nz3#0jf2VRdz4ne6O3mUuUj zfT1-iCmnX)v^$$ljau1ZYam4*3@5XL$IcB8rDGXe1y;_tT~)RN&ri~+@l1w>QrR^1 zq_e5POkZkXU^G28)(ca&RI^Lc%1OJDq)ck)^f*)Q$z=ML*FL;;9?u|HidKgre23dI2V4Y*MYNo1&8b5Y+UHC z^d|NjaJCU%Y&m6zbKyLj=;R5#TV;0FlrmgS_6C&2EDL_5E1g|*aT%)HAp}$4URZiTb zXl%fXIj|V}u#QBZLEwg7#$00w!!LF;VNT|N(*gGt^TlCPWE`z?p+KP?hg7h?2|`;a z*)3DNdOP6}p20OMit3;oxL$=Rw6Fj7>YxFxH9c{>X07kEG-WZ+L{SM=kStu|?uzN0 z9}v0`&OTJURx|F_E^0>PcJ=?|i);)jBQ^IWp+{If^l_BKn$a3^nl|7BOwVoV2I*zc z*yPf*6E{NmIW%411~f%pGrz+{^Mj$KAPd@4=o|Vrcj=xs;G)1W?yoZtmvnCvv14>4 zYR4o%65>ZBTfL6jhKsC<>9$?JOw{|$F5ahy1$g4WFcq5T^#dsEwZq@GFIDk?3>U?} Dr?6*Q literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/ru/LC_MESSAGES/django.po b/thirdpart/registration/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000000..072e146a0d --- /dev/null +++ b/thirdpart/registration/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,92 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2009-10-21 20:12+0600\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:23 +msgid "Activate users" +msgstr "Активировать учетные записи" + +#: admin.py:43 +msgid "Re-send activation emails" +msgstr "Выслать ключи активации заново" + +#: forms.py:35 +msgid "Username" +msgstr "Имя пользователя" + +#: forms.py:36 +msgid "This value must contain only letters, numbers and underscores." +msgstr "Это поле может содержать только буквы, цифры и подчеркивания" + +#: forms.py:39 +msgid "Email address" +msgstr "Адрес электронной почты" + +#: forms.py:41 +msgid "Password" +msgstr "Пароль" + +#: forms.py:43 +msgid "Password (again)" +msgstr "Пароль (снова)" + +#: forms.py:55 +msgid "A user with that username already exists." +msgstr "Пользователь с таким именем уже существует." + +#: forms.py:67 +msgid "The two password fields didn't match." +msgstr "Введенные пароли не совпадают." + +#: forms.py:78 +msgid "I have read and agree to the Terms of Service" +msgstr "Я прочитал Правила Использования и согласен с ними" + +#: forms.py:79 +msgid "You must agree to the terms to register" +msgstr "Для регистрации Вы должны согласиться с Правилами" + +#: forms.py:95 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Этот адрес электронной почты уже используется. Пожалуйста, введите другой " +"адрес." + +#: forms.py:122 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Регистрация с использованием свободных почтовых серверов запрещена. " +"Пожалуйста, введите другой адрес электронной почты." + +#: models.py:165 +msgid "user" +msgstr "пользователь" + +#: models.py:166 +msgid "activation key" +msgstr "ключ активации" + +#: models.py:171 +msgid "registration profile" +msgstr "карточка регистрации" + +#: models.py:172 +msgid "registration profiles" +msgstr "карточки регистрации" diff --git a/thirdpart/registration/locale/sl/LC_MESSAGES/django.mo b/thirdpart/registration/locale/sl/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..036163f3fe0788878d1bc028b88e0f5413b77e0e GIT binary patch literal 1903 zcmb7^&2Jk;7{&)EU*@|7BqWet2|*f>b?lIe)U;JYYN3dvwGtDxJ%GmE*X!}_&R8?E zn>q*ngPaiDd!Q!{aP6Td=fa5#7my;(`~{p4&)Cja>4lNUzunn)=6&YbdH3-Ax$gwp zdGvSDe?)%|{mN7LL;DGw2Y&`%1^)nF0sjIc@Ne)1@WZEtcpY2>-vO_KUxRnS55R}u zP4G|f8o2n35Obgg!@A#tZ-PI7Z-EcNbKtLFSpPRLN3KJ#j{A*gr}5u_LHoC0(ER|! zPmJ;R8u$zNKKMI$2mA*N`@H>}5H+v>KLHPsMZ=k=7e$;|LM-5>Dl8;-^6dGh4x3F$lGc?#UX^3I_2L^GEvBN@k*ofGTSmnBn}K~ke9mYKZ4 znoMoBxg*n~ZMLQAvS>-?c8!gXFX*C7q|)URwdXWFQzIEx)7 z#CpzCE`7HjqOks%lp*1fRVpas|CJQ8OPLi+dEq>DjrQ-3A1`C<0lR}5e~=XFB}QcvSz?t zAFapH4piVxG^kcxsn$E}Wo3(p#`0_#2YAj#=s8&bZhIBq^Td7?py|E3tVRF{= zg*kX6EnSJj-SwEus#Dhxtdl|}9JN{I4cg93$y!PMc--dtX7glI^=hrMhIfH;617J| zNcMcsFAg)joKLCSm)5brTKHbHauOF5;cP`s-8C`JXMEIEQ={PS35+$0VM3t3; zEh66Q))9WTGNZk z64{_28FI<48n|>;gnV417?q?IHM)tZ^zgRBCpp zT8eFvoQbI-dj^Snlt7ybvNgU#0~Dgiwwser(SRprl(UVfh43Bt&$^so!j9!w8`N7S z`zi7|?qD%;5+cU?HsuBCDtuiCr8qP(hTtz(#a%h8DH}F!f{q;T2$Jz-3Xb}q@r<85 jeJN9;6s53kynos#m#QEXHTdLFb#e~GsS{wr0TBNJ-VQEw literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/sl/LC_MESSAGES/django.po b/thirdpart/registration/locale/sl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..c587c70e77 --- /dev/null +++ b/thirdpart/registration/locale/sl/LC_MESSAGES/django.po @@ -0,0 +1,87 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.8.1beta\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2009-10-12 14:09-0500\n" +"PO-Revision-Date: 2009-10-23 15:49+0100\n" +"Last-Translator: Domen Kožar \n" +"Language-Team: Slovenian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Slovenian\n" +"X-Poedit-Country: SLOVENIA\n" + +#: admin.py:23 +msgid "Activate users" +msgstr "Aktiviraj uporabnike" + +#: admin.py:43 +msgid "Re-send activation emails" +msgstr "Ponovno pošlju aktivacijske emaile" + +#: forms.py:35 +msgid "username" +msgstr "uporabniško ime" + +#: forms.py:36 +msgid "This value must contain only letters, numbers and underscores." +msgstr "Vrednost lahko vsebuje samo črke, cifre in podčrtaje." + +#: forms.py:39 +msgid "Email address" +msgstr "Elektronska pošta" + +#: forms.py:41 +msgid "Password" +msgstr "Geslo" + +#: forms.py:43 +msgid "Password (again)" +msgstr "Geslo (ponovno)" + +#: forms.py:55 +msgid "A user with that username already exists." +msgstr "Uporabnik z tem uporabniškim imenom že obstaja." + +#: forms.py:67 +msgid "The two password fields didn't match." +msgstr "Polji z gesli se ne ujemata." + +#: forms.py:78 +msgid "I have read and agree to the Terms of Service" +msgstr "Strinjam se z pogoji uporable" + +#: forms.py:79 +msgid "You must agree to the terms to register" +msgstr "Za registracijo se morate strinjati z pogoji uporabe" + +#: forms.py:95 +msgid "This email address is already in use. Please supply a different email address." +msgstr "Email je že v uporabi, prosimo vnesite drugega." + +#: forms.py:122 +msgid "Registration using free email addresses is prohibited. Please supply a different email address." +msgstr "Registracija ni mogoča z brezplačnimi email naslovi. Prosimo vnesite drug email naslov." + +#: models.py:165 +msgid "user" +msgstr "Uporabnik" + +#: models.py:166 +msgid "activation key" +msgstr "Aktivacijski ključ" + +#: models.py:171 +msgid "registration profile" +msgstr "Registracijski profil" + +#: models.py:172 +msgid "registration profiles" +msgstr "Registracijski profili" + diff --git a/thirdpart/registration/locale/sr/LC_MESSAGES/django.mo b/thirdpart/registration/locale/sr/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..1699326553efe848408441bb04e452d14f341774 GIT binary patch literal 1966 zcmb7^O>Y}T7{>=F6wIqC5USwtP>GVzu30B7ik!L-`huc1ZIn0_(-_#C_gu7dmEC*YspBKR-(7WlzSLc9rn3cd=i zfFai!_%65wz6WOD3GjO`*!>9pgIqs@%lOtW3z34qfj7XDuL#isx4{kYC-55h7kC;x zeNu>1;4=6Icpb!$*aqJQzXe1791Qt?07KqK)A3(Hq_113Kg0<21>ac<^LTyw5n`j% zx`jBihA;wB2$Wsx(Ewk=Bi5-eM@*JwN>by=EHimSzR%QUTRJkkw9R&;5*AyWDd(;9 z%4n*b(i!ardoHCaNT#V}=h%^QG_!UzP-x!o9wZWuqQ|wGV93@hd(Wp5w+Dqgw z*n76_Jn5?Jp;K#C=@i|>VDjRwsi>@+pBEB75E81|T0pkPc78mB-Vob`8i&%k?~F}} zWzr|FN)|HlYBW)Nz+*9|j+ogWb!b6mD0ERwGEJ8Xi?OQ;e&$anf#H8yW|qbpdouu-C`(sP@Z z;&>&BFGTT0YAv@fw$8=)1H)*GM=GRwhTVmB9G{C@u)8UpkGfWBS4eMcn{IK>o6ahkanxm5qOOhgjcdo#YBl4=6|5|FV${Vtw8`^* z|JksBf~%DDrFHB(JKgKi%CTLj0lPQ4rW2Fm1hi=d+q|(^R8|(zb%T{|lOEQeuCu&K zlWs?U+=@G$7M(pyp&NhHX|?DJiV4>EzT8=w4PEM72;GI+>{2VH2M;DLu!$Gp(}K^^ zYU7(|({QSMG@E5R9MqnY`grJyskFD_Hr?I%`u6tCwL9x;jj+TA`^rlbHXSK~riXuU zN#Tr2oUy`;R4Hj-tU7#r@Q`&bkB)w#^0^VX_1Hl&uT@4IO>Y961xY#6p6o%eoV9Gy zhQW4Zsx*5=sjaeV&c14j+ao#ms}3>ZgNIa6H8kFGPaVYx`l}lR`2xlIN)x4hxFLd<-M~4j(Jrvtdw&a9FKM$U(Rl y5*!>wgCegE_K)s{Aik)5W}t, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: django-registration trunk\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-04-05 13:51+0200\n" +"PO-Revision-Date: 2008-04-05 14:00+0100\n" +"Last-Translator: Nebojsa Djordjevic \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Poedit-Language: Serbian\n" +"X-Poedit-Country: YUGOSLAVIA\n" + +#: forms.py:38 +msgid "username" +msgstr "korisničko ime" + +#: forms.py:41 +msgid "email address" +msgstr "email adresa" + +#: forms.py:43 +msgid "password" +msgstr "šifra" + +#: forms.py:45 +msgid "password (again)" +msgstr "šifra (ponovo)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "Korisničko ime može da se sastoji samo od slova, brojeva i donje crte (\"_\")" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "Korisničko ime je već zauzeto. Izaberite drugo." + +#: forms.py:71 +msgid "You must type the same password each time" +msgstr "Unete šifre se ne slažu" + +#: forms.py:100 +msgid "I have read and agree to the Terms of Service" +msgstr "Pročitao sam i slažem se sa uslovima korišćenja" + +#: forms.py:109 +msgid "You must agree to the terms to register" +msgstr "Morate se složiti sa uslovima korišćenja da bi ste se registrovali" + +#: forms.py:128 +msgid "This email address is already in use. Please supply a different email address." +msgstr "Ova e-mail adresa je već u upotrebi. Morate koristiti drugu e-mail adresu." + +#: forms.py:153 +msgid "Registration using free email addresses is prohibited. Please supply a different email address." +msgstr "Registracija korišćenjem besplatnig e-mail adresa je zabranjena. Morate uneti drugu e-mail adresu." + +#: models.py:188 +msgid "user" +msgstr "korisnik" + +#: models.py:189 +msgid "activation key" +msgstr "aktivacioni ključ" + +#: models.py:194 +msgid "registration profile" +msgstr "registracioni profil" + +#: models.py:195 +msgid "registration profiles" +msgstr "registracioni profili" + diff --git a/thirdpart/registration/locale/sv/LC_MESSAGES/django.mo b/thirdpart/registration/locale/sv/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..50eca67e21301f5081b881b054ec648a67e25509 GIT binary patch literal 1687 zcmb7^&2A$_5XT!>STLWbWfg3^`kvMRS#T(=W=EO_Dl`C(+zsF7-qrEW7%&%u$RbBnBs{Hx-jb8+=*Kog! z`#0Qg`}I?J;Cc)O;1v8C{2e?5Z$2%=C*Ub~7yKD~AN&Jc1OEYE0pEE>h?l_+z~{j# z==Z9FZ-QO$EieXefZu`M?g#Kw?DZ4)9^P~OxC8zT-T~L26=Dlya3A~?+y?&w34G%@ zA#Q;mfiHpk;0xd}==c8$#Gg=j`2EIU3H%=P`ThuE`{MHNK8DZl)#A(i;p4oxc!#+_ zs>S8wU0ye_fD{606?*tRd>*1n136(bEF+Q%cWju+X>tRm9-GXP?$a@wNfNT?a-3La zq)W7-u}xG=eQ(d1Oj43jWY}7^lvo-WJxI3eCnU!ZOr2ym+X`l#Q)h6%sxW5~`X)KsHM|HyuH5h;4n2BWdlo z+C;=M9FR*g7BY0nWUly-r(#7NacO_GpxZJ=qHAKl)1p);Mn6gUi65=-3Kb78lQ=T^ z8xCF2jDk}f3ze?XQGNHz`ay$E8{Ok(=dje}kv1-9+c=4WopEe~o~}{psM8B}Q7hjb z!Jc$nqpeD%8dSD}t!>(@);_+oUfHZvU>J0HlK9{YyN8W>H)wZ)y*ho~skNKyk2;5q zQcGGF^o&$Cl}?))H8S+sv15fU%r7#!$Jv9S8a=QBfR(nw^hc#zN1RMMFqT9=j|U)6b{E84ibH z4?~g9EKYc($)zL+dI5c~<)UMu7yd!SVke(P5^7#pR(>KD0VaO?G#N@!A7U`rpafP; k!y%`laZOgyy7XMG=yn2y)_jX&92P+DU!9VdORtak7w$#!*8l(j literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/sv/LC_MESSAGES/django.po b/thirdpart/registration/locale/sv/LC_MESSAGES/django.po new file mode 100644 index 0000000000..dec76e273b --- /dev/null +++ b/thirdpart/registration/locale/sv/LC_MESSAGES/django.po @@ -0,0 +1,81 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2008-03-23 18:59+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Emil Stenström \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .\forms.py:38 +msgid "username" +msgstr "Användarnamn" + +#: .\forms.py:41 +msgid "email address" +msgstr "E-postadress" + +#: .\forms.py:43 +msgid "password" +msgstr "Lösenord" + +#: .\forms.py:45 +msgid "password (again)" +msgstr "Lösenord (igen)" + +#: .\forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "Användarnamn får bara innehålla bokstäver, siffror och understreck" + +#: .\forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "Det användarnamnet är upptaget. Prova ett annat." + +#: .\forms.py:71 +msgid "You must type the same password each time" +msgstr "Båda lösenord måste vara lika" + +#: .\forms.py:100 +msgid "I have read and agree to the Terms of Service" +msgstr "Jag har läst och accepterar avtalet" + +#: .\forms.py:109 +msgid "You must agree to the terms to register" +msgstr "Du måste acceptera avtalet för att registrera dig" + +#: .\forms.py:128 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Den e-postadressen är upptagen, använd an annan adress." + +#: .\forms.py:153 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "Gratis e-postadresser är inte tillåtna, använd en annan adress." + +#: .\models.py:188 +msgid "user" +msgstr "Användare" + +#: .\models.py:189 +msgid "activation key" +msgstr "Aktiveringsnyckel" + +#: .\models.py:194 +msgid "registration profile" +msgstr "Profil" + +#: .\models.py:195 +msgid "registration profiles" +msgstr "Profiler" diff --git a/thirdpart/registration/locale/zh_CN/LC_MESSAGES/django.mo b/thirdpart/registration/locale/zh_CN/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..ece5cc97b10d0ace9a2ba33ce927e95d8e48ec49 GIT binary patch literal 1669 zcmb7@?@t^>7{{ktt)4&AXnav$%nPGdyXOK?;iwIWNKl}J9whq4O!tPpwRgLhojEKq zCI=P-%LPQMMri^KRMOg9qeLiBc;yTK3E%W)x$_P2Kkz%Z2iHq{)5-39W@evxetc*4 z@8d_W5VU9UzKC}L?+bc-1RdH%&;UolPrzTmKJeH>guDd~fvsR3dCj!2cH86z~@05JPLjRYP)f;1+gxJFJb;CKK6tEfFFRZkA!(mf+pr` z;0aKH+WyF+gggnpTxAo8hqR$P4t7=7Kd#aS@eqmbaqx2x;VVt&uYDmjd8V=wcDjxy zs&l;;saBfK|DYYi08$X7Rna2?YX790CfNw5K4%HaT)ei=Ih7ujNlxS3PYdb|({tP( zaV$;-xa|n(GwFCP%?ii0>9DrvY38ITOC)?Q1Q*m1G~;_o=e#3%B1-#HoC!`vHj_z> zQbrTb@G$qeE9*B#Nj!<2bMEfeYYy-&F%jqZdogtBnbG1}kW@nL&cCmre>IM;o)FEYS0 zo-d7FVLJ)q-K;H)xEG_5{)%3X~ccziWHNcAEU{v%tD_uJ$*20v-RDJT3-4L z%w0RnY;MFkOUGzWdtcX}PWyCEPg}}~dcJ)o(%aqJSu3m|YDPM6wz!K%948T@l3$c> zWKz(um0C&W3ohRojK6O**X(pYxW7iHYk3L$12Ni+iyk4t)HiDVxAKn@rNWlF^M{(7 z2`RRsQpl%H*1Pjx}&CM{z1G++1!PL5% z%fn6S+D(;T#7gJ`DU@^5VLCc+&2;ug>C3xnWpCBgWr+MOsbZH3B&rB-D*{8rleR!yuFH, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-19 19:30-0500\n" +"PO-Revision-Date: 2008-03-20 23:22+0800\n" +"Last-Translator: hutuworm \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:38 +msgid "username" +msgstr "用户名" + +#: forms.py:41 +msgid "email address" +msgstr "Email 地址" + +#: forms.py:43 +msgid "password" +msgstr "密码" + +#: forms.py:45 +msgid "password (again)" +msgstr "密码(重复)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "用户名只能包含字母、数字和下划线" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "该用户名已被占用,请另选一个。" + +#: forms.py:68 +msgid "You must type the same password each time" +msgstr "您必须输入两遍同样的密码" + +#: forms.py:96 +msgid "I have read and agree to the Terms of Service" +msgstr "我已阅读并同意该服务条款" + +#: forms.py:105 +msgid "You must agree to the terms to register" +msgstr "您必须同意注册条款" + +#: forms.py:124 +msgid "This email address is already in use. Please supply a different email address." +msgstr "该 Email 地址已有人使用,请提供一个另外的 Email 地址。" + +#: forms.py:149 +msgid "Registration using free email addresses is prohibited. Please supply a different email address." +msgstr "禁止使用免费 Email 地址注册,请提供一个另外的 Email 地址。" + +#: models.py:188 +msgid "user" +msgstr "用户" + +#: models.py:189 +msgid "activation key" +msgstr "激活密钥" + +#: models.py:194 +msgid "registration profile" +msgstr "注册信息" + +#: models.py:195 +msgid "registration profiles" +msgstr "注册信息" + diff --git a/thirdpart/registration/locale/zh_TW/LC_MESSAGES/django.mo b/thirdpart/registration/locale/zh_TW/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..24a3534958925d49ab1e1032f3cd2d50e28cd3e8 GIT binary patch literal 1669 zcmb7@?@t^>7{{ktt)4&AXnav$%nPGd-E)DcaMT7wBq&fq4-$Q2rhCKQ+PmG$&K#B) zlYz5_j$aFc%Reb1L)AsfCe}YehmHu4uFRqAmmMO1Z)TM;7j0N;8E~j@JaB< zgM>T*z5+f1wt_lVC-@9F1U?Je;6d!XO5#cEY;dMsv6N14oH{xtzh8IQ;=3%(@AV|vY3}YjvXY*M^qA|R zXg^AZYdZwBm`gnuIa{798RDn7l-w7uQ8$}Dg+W!btee2l@=!ea(96(#nu?Ipz zRlh1A_v?0YJOjNUw$M3dm=K?Oeu8plC8>1MoG?o|W1-@w`8a7%hwRz!59rIxMxsYa z7^!wtIE+yz#s7Cv5Uo(P(=w4k-}{7H(&$YXBPa_e9ixMtT_1GzbkmXUp_9EQ1|mZ| z$H#c^>w72sOQ_qBmKSo-SxtnqGqHEXN$XN#BmZaD)|}t zdL{)8+o_dgzTooh;rM$-Yu!%ggZpcAyOx*0KM

xabiQ%v@BrHY!(sEB~4+6_(Y) zTySo_yt)xA=GE-i!P2$hmo2ihJP}-9Ds67>Tv}06`SQjc+Fkv7)Zz_wEf@U0qNXqG z7U#-qv%&mAY3rL(A*XV)YJOS$aAoJ}xd!XIxk-Wm4QogtSUg|4eLIYCpRSNqF!P66 z$iq!}X|1$*2`ixwq)?eCgz4zOb<_Eu%V#&$+>}~aQ`f%=*7J9B6T#&I#_G$tQejqI zIJ>6+nG?Au+*9?``^mNf|~kKO|4cY^4f0YdlaqKr+1c$B-qXcH}7Cl pxp;%zs~V{pgxD?4RKCcot<~M)c@jn`ZGRU`tT#@={Oe_q{{ZHmUoijx literal 0 HcmV?d00001 diff --git a/thirdpart/registration/locale/zh_TW/LC_MESSAGES/django.po b/thirdpart/registration/locale/zh_TW/LC_MESSAGES/django.po new file mode 100644 index 0000000000..7cc090d08c --- /dev/null +++ b/thirdpart/registration/locale/zh_TW/LC_MESSAGES/django.po @@ -0,0 +1,77 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-09-19 19:30-0500\n" +"PO-Revision-Date: 2008-03-20 23:22+0800\n" +"Last-Translator: hutuworm \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:38 +msgid "username" +msgstr "用戶名" + +#: forms.py:41 +msgid "email address" +msgstr "Email 地址" + +#: forms.py:43 +msgid "password" +msgstr "密碼" + +#: forms.py:45 +msgid "password (again)" +msgstr "密碼(重復)" + +#: forms.py:54 +msgid "Usernames can only contain letters, numbers and underscores" +msgstr "用戶名只能包含字母、數字和下劃線" + +#: forms.py:59 +msgid "This username is already taken. Please choose another." +msgstr "該用戶名已被佔用,請另選一個。" + +#: forms.py:68 +msgid "You must type the same password each time" +msgstr "您必須輸入兩遍同樣的密碼" + +#: forms.py:96 +msgid "I have read and agree to the Terms of Service" +msgstr "我已閱讀並同意該服務條款" + +#: forms.py:105 +msgid "You must agree to the terms to register" +msgstr "您必須同意注冊條款" + +#: forms.py:124 +msgid "This email address is already in use. Please supply a different email address." +msgstr "該 Email 地址已有人使用,請提供一個另外的 Email 地址。" + +#: forms.py:149 +msgid "Registration using free email addresses is prohibited. Please supply a different email address." +msgstr "禁止使用免費 Email 地址注冊,請提供一個另外的 Email 地址。" + +#: models.py:188 +msgid "user" +msgstr "用戶" + +#: models.py:189 +msgid "activation key" +msgstr "激活密鑰" + +#: models.py:194 +msgid "registration profile" +msgstr "注冊信息" + +#: models.py:195 +msgid "registration profiles" +msgstr "注冊信息" + diff --git a/thirdpart/registration/management/__init__.py b/thirdpart/registration/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/thirdpart/registration/management/commands/__init__.py b/thirdpart/registration/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/thirdpart/registration/management/commands/cleanupregistration.py b/thirdpart/registration/management/commands/cleanupregistration.py new file mode 100644 index 0000000000..abec5aed3c --- /dev/null +++ b/thirdpart/registration/management/commands/cleanupregistration.py @@ -0,0 +1,19 @@ +""" +A management command which deletes expired accounts (e.g., +accounts which signed up but never activated) from the database. + +Calls ``RegistrationProfile.objects.delete_expired_users()``, which +contains the actual logic for determining which accounts are deleted. + +""" + +from django.core.management.base import NoArgsCommand + +from registration.models import RegistrationProfile + + +class Command(NoArgsCommand): + help = "Delete expired user registrations from the database" + + def handle_noargs(self, **options): + RegistrationProfile.objects.delete_expired_users() diff --git a/thirdpart/registration/models.py b/thirdpart/registration/models.py new file mode 100644 index 0000000000..a2a04ae125 --- /dev/null +++ b/thirdpart/registration/models.py @@ -0,0 +1,258 @@ +import datetime +import random +import re + +from django.conf import settings +from django.contrib.auth.models import User +from django.db import models +from django.db import transaction +from django.template.loader import render_to_string +from django.utils.hashcompat import sha_constructor +from django.utils.translation import ugettext_lazy as _ + + +SHA1_RE = re.compile('^[a-f0-9]{40}$') + + +class RegistrationManager(models.Manager): + """ + Custom manager for the ``RegistrationProfile`` model. + + The methods defined here provide shortcuts for account creation + and activation (including generation and emailing of activation + keys), and for cleaning out expired inactive accounts. + + """ + def activate_user(self, activation_key): + """ + Validate an activation key and activate the corresponding + ``User`` if valid. + + If the key is valid and has not expired, return the ``User`` + after activating. + + If the key is not valid or has expired, return ``False``. + + If the key is valid but the ``User`` is already active, + return ``False``. + + To prevent reactivation of an account which has been + deactivated by site administrators, the activation key is + reset to the string constant ``RegistrationProfile.ACTIVATED`` + after successful activation. + + """ + # Make sure the key we're trying conforms to the pattern of a + # SHA1 hash; if it doesn't, no point trying to look it up in + # the database. + if SHA1_RE.search(activation_key): + try: + profile = self.get(activation_key=activation_key) + except self.model.DoesNotExist: + return False + if not profile.activation_key_expired(): + user = profile.user + user.is_active = True + user.save() + profile.activation_key = self.model.ACTIVATED + profile.save() + return user + return False + + def create_inactive_user(self, username, email, password, + site, send_email=True): + """ + Create a new, inactive ``User``, generate a + ``RegistrationProfile`` and email its activation key to the + ``User``, returning the new ``User``. + + By default, an activation email will be sent to the new + user. To disable this, pass ``send_email=False``. + + """ + new_user = User.objects.create_user(username, email, password) + new_user.is_active = False + new_user.save() + + registration_profile = self.create_profile(new_user) + + if send_email: + registration_profile.send_activation_email(site) + + return new_user + create_inactive_user = transaction.commit_on_success(create_inactive_user) + + def create_profile(self, user): + """ + Create a ``RegistrationProfile`` for a given + ``User``, and return the ``RegistrationProfile``. + + The activation key for the ``RegistrationProfile`` will be a + SHA1 hash, generated from a combination of the ``User``'s + username and a random salt. + + """ + salt = sha_constructor(str(random.random())).hexdigest()[:5] + username = user.username + if isinstance(username, unicode): + username = username.encode('utf-8') + activation_key = sha_constructor(salt+username).hexdigest() + return self.create(user=user, + activation_key=activation_key) + + def delete_expired_users(self): + """ + Remove expired instances of ``RegistrationProfile`` and their + associated ``User``s. + + Accounts to be deleted are identified by searching for + instances of ``RegistrationProfile`` with expired activation + keys, and then checking to see if their associated ``User`` + instances have the field ``is_active`` set to ``False``; any + ``User`` who is both inactive and has an expired activation + key will be deleted. + + It is recommended that this method be executed regularly as + part of your routine site maintenance; this application + provides a custom management command which will call this + method, accessible as ``manage.py cleanupregistration``. + + Regularly clearing out accounts which have never been + activated serves two useful purposes: + + 1. It alleviates the ocasional need to reset a + ``RegistrationProfile`` and/or re-send an activation email + when a user does not receive or does not act upon the + initial activation email; since the account will be + deleted, the user will be able to simply re-register and + receive a new activation key. + + 2. It prevents the possibility of a malicious user registering + one or more accounts and never activating them (thus + denying the use of those usernames to anyone else); since + those accounts will be deleted, the usernames will become + available for use again. + + If you have a troublesome ``User`` and wish to disable their + account while keeping it in the database, simply delete the + associated ``RegistrationProfile``; an inactive ``User`` which + does not have an associated ``RegistrationProfile`` will not + be deleted. + + """ + for profile in self.all(): + if profile.activation_key_expired(): + user = profile.user + if not user.is_active: + user.delete() + + +class RegistrationProfile(models.Model): + """ + A simple profile which stores an activation key for use during + user account registration. + + Generally, you will not want to interact directly with instances + of this model; the provided manager includes methods + for creating and activating new accounts, as well as for cleaning + out accounts which have never been activated. + + While it is possible to use this model as the value of the + ``AUTH_PROFILE_MODULE`` setting, it's not recommended that you do + so. This model's sole purpose is to store data temporarily during + account registration and activation. + + """ + ACTIVATED = u"ALREADY_ACTIVATED" + + user = models.ForeignKey(User, unique=True, verbose_name=_('user')) + activation_key = models.CharField(_('activation key'), max_length=40) + + objects = RegistrationManager() + + class Meta: + verbose_name = _('registration profile') + verbose_name_plural = _('registration profiles') + + def __unicode__(self): + return u"Registration information for %s" % self.user + + def activation_key_expired(self): + """ + Determine whether this ``RegistrationProfile``'s activation + key has expired, returning a boolean -- ``True`` if the key + has expired. + + Key expiration is determined by a two-step process: + + 1. If the user has already activated, the key will have been + reset to the string constant ``ACTIVATED``. Re-activating + is not permitted, and so this method returns ``True`` in + this case. + + 2. Otherwise, the date the user signed up is incremented by + the number of days specified in the setting + ``ACCOUNT_ACTIVATION_DAYS`` (which should be the number of + days after signup during which a user is allowed to + activate their account); if the result is less than or + equal to the current date, the key has expired and this + method returns ``True``. + + """ + expiration_date = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS) + return self.activation_key == self.ACTIVATED or \ + (self.user.date_joined + expiration_date <= datetime.datetime.now()) + activation_key_expired.boolean = True + + def send_activation_email(self, site): + """ + Send an activation email to the user associated with this + ``RegistrationProfile``. + + The activation email will make use of two templates: + + ``registration/activation_email_subject.txt`` + This template will be used for the subject line of the + email. Because it is used as the subject line of an email, + this template's output **must** be only a single line of + text; output longer than one line will be forcibly joined + into only a single line. + + ``registration/activation_email.txt`` + This template will be used for the body of the email. + + These templates will each receive the following context + variables: + + ``activation_key`` + The activation key for the new account. + + ``expiration_days`` + The number of days remaining during which the account may + be activated. + + ``site`` + An object representing the site on which the user + registered; depending on whether ``django.contrib.sites`` + is installed, this may be an instance of either + ``django.contrib.sites.models.Site`` (if the sites + application is installed) or + ``django.contrib.sites.models.RequestSite`` (if + not). Consult the documentation for the Django sites + framework for details regarding these objects' interfaces. + + """ + ctx_dict = { 'activation_key': self.activation_key, + 'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS, + 'site': site, + 'SITE_ROOT': settings.SITE_ROOT } + subject = render_to_string('registration/activation_email_subject.txt', + ctx_dict) + # Email subject *must not* contain newlines + subject = ''.join(subject.splitlines()) + + message = render_to_string('registration/activation_email.txt', + ctx_dict) + + self.user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL) + diff --git a/thirdpart/registration/signals.py b/thirdpart/registration/signals.py new file mode 100644 index 0000000000..343e3a5551 --- /dev/null +++ b/thirdpart/registration/signals.py @@ -0,0 +1,8 @@ +from django.dispatch import Signal + + +# A new user has registered. +user_registered = Signal(providing_args=["user", "request"]) + +# A user has activated his or her account. +user_activated = Signal(providing_args=["user", "request"]) diff --git a/thirdpart/registration/tests/__init__.py b/thirdpart/registration/tests/__init__.py new file mode 100644 index 0000000000..e23e2d165d --- /dev/null +++ b/thirdpart/registration/tests/__init__.py @@ -0,0 +1,4 @@ +from registration.tests.backends import * +from registration.tests.forms import * +from registration.tests.models import * +from registration.tests.views import * diff --git a/thirdpart/registration/tests/backends.py b/thirdpart/registration/tests/backends.py new file mode 100644 index 0000000000..ee26823d3f --- /dev/null +++ b/thirdpart/registration/tests/backends.py @@ -0,0 +1,361 @@ +import datetime + +from django.conf import settings +from django.contrib import admin +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.core import mail +from django.core.exceptions import ImproperlyConfigured +from django.core.handlers.wsgi import WSGIRequest +from django.test import Client +from django.test import TestCase + +from registration import forms +from registration import signals +from registration.admin import RegistrationAdmin +from registration.backends import get_backend +from registration.backends.default import DefaultBackend +from registration.models import RegistrationProfile + + +class _MockRequestClient(Client): + """ + A ``django.test.Client`` subclass which can return mock + ``HttpRequest`` objects. + + """ + def request(self, **request): + """ + Rather than issuing a request and returning the response, this + simply constructs an ``HttpRequest`` object and returns it. + + """ + environ = { + 'HTTP_COOKIE': self.cookies, + 'PATH_INFO': '/', + 'QUERY_STRING': '', + 'REMOTE_ADDR': '127.0.0.1', + 'REQUEST_METHOD': 'GET', + 'SCRIPT_NAME': '', + 'SERVER_NAME': 'testserver', + 'SERVER_PORT': '80', + 'SERVER_PROTOCOL': 'HTTP/1.1', + 'wsgi.version': (1,0), + 'wsgi.url_scheme': 'http', + 'wsgi.errors': self.errors, + 'wsgi.multiprocess': True, + 'wsgi.multithread': False, + 'wsgi.run_once': False, + } + environ.update(self.defaults) + environ.update(request) + return WSGIRequest(environ) + + +def _mock_request(): + """ + Construct and return a mock ``HttpRequest`` object; this is used + in testing backend methods which expect an ``HttpRequest`` but + which are not being called from views. + + """ + return _MockRequestClient().request() + + +class BackendRetrievalTests(TestCase): + """ + Test that utilities for retrieving the active backend work + properly. + + """ + def test_get_backend(self): + """ + Verify that ``get_backend()`` returns the correct value when + passed a valid backend. + + """ + self.failUnless(isinstance(get_backend('registration.backends.default.DefaultBackend'), + DefaultBackend)) + + def test_backend_error_invalid(self): + """ + Test that a nonexistent/unimportable backend raises the + correct exception. + + """ + self.assertRaises(ImproperlyConfigured, get_backend, + 'registration.backends.doesnotexist.NonExistentBackend') + + def test_backend_attribute_error(self): + """ + Test that a backend module which exists but does not have a + class of the specified name raises the correct exception. + + """ + self.assertRaises(ImproperlyConfigured, get_backend, + 'registration.backends.default.NonexistentBackend') + + +class DefaultRegistrationBackendTests(TestCase): + """ + Test the default registration backend. + + Running these tests successfull will require two templates to be + created for the sending of activation emails; details on these + templates and their contexts may be found in the documentation for + the default backend. + + """ + def setUp(self): + """ + Create an instance of the default backend for use in testing, + and set ``ACCOUNT_ACTIVATION_DAYS`` if it's not set already. + + """ + from registration.backends.default import DefaultBackend + self.backend = DefaultBackend() + self.old_activation = getattr(settings, 'ACCOUNT_ACTIVATION_DAYS', None) + if self.old_activation is None: + settings.ACCOUNT_ACTIVATION_DAYS = 7 + + def tearDown(self): + """ + Yank out ``ACCOUNT_ACTIVATION_DAYS`` back out if it wasn't + originally set. + + """ + if self.old_activation is None: + settings.ACCOUNT_ACTIVATION_DAYS = self.old_activation + + def test_registration(self): + """ + Test the registration process: registration creates a new + inactive account and a new profile with activation key, + populates the correct account data and sends an activation + email. + + """ + new_user = self.backend.register(_mock_request(), + username='bob', + email='bob@example.com', + password1='secret') + + # Details of the returned user must match what went in. + self.assertEqual(new_user.username, 'bob') + self.failUnless(new_user.check_password('secret')) + self.assertEqual(new_user.email, 'bob@example.com') + + # New user must not be active. + self.failIf(new_user.is_active) + + # A registration profile was created, and an activation email + # was sent. + self.assertEqual(RegistrationProfile.objects.count(), 1) + self.assertEqual(len(mail.outbox), 1) + + def test_registration_no_sites(self): + """ + Test that registration still functions properly when + ``django.contrib.sites`` is not installed; the fallback will + be a ``RequestSite`` instance. + + """ + Site._meta.installed = False + + new_user = self.backend.register(_mock_request(), + username='bob', + email='bob@example.com', + password1='secret') + + self.assertEqual(new_user.username, 'bob') + self.failUnless(new_user.check_password('secret')) + self.assertEqual(new_user.email, 'bob@example.com') + + self.failIf(new_user.is_active) + + self.assertEqual(RegistrationProfile.objects.count(), 1) + self.assertEqual(len(mail.outbox), 1) + + Site._meta.installed = True + + def test_valid_activation(self): + """ + Test the activation process: activating within the permitted + window sets the account's ``is_active`` field to ``True`` and + resets the activation key. + + """ + valid_user = self.backend.register(_mock_request(), + username='alice', + email='alice@example.com', + password1='swordfish') + + valid_profile = RegistrationProfile.objects.get(user=valid_user) + activated = self.backend.activate(_mock_request(), + valid_profile.activation_key) + self.assertEqual(activated.username, valid_user.username) + self.failUnless(activated.is_active) + + # Fetch the profile again to verify its activation key has + # been reset. + valid_profile = RegistrationProfile.objects.get(user=valid_user) + self.assertEqual(valid_profile.activation_key, + RegistrationProfile.ACTIVATED) + + def test_invalid_activation(self): + """ + Test the activation process: trying to activate outside the + permitted window fails, and leaves the account inactive. + + """ + expired_user = self.backend.register(_mock_request(), + username='bob', + email='bob@example.com', + password1='secret') + + expired_user.date_joined = expired_user.date_joined - datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS) + expired_user.save() + expired_profile = RegistrationProfile.objects.get(user=expired_user) + self.failIf(self.backend.activate(_mock_request(), + expired_profile.activation_key)) + self.failUnless(expired_profile.activation_key_expired()) + + def test_allow(self): + """ + Test that the setting ``REGISTRATION_OPEN`` appropriately + controls whether registration is permitted. + + """ + old_allowed = getattr(settings, 'REGISTRATION_OPEN', True) + settings.REGISTRATION_OPEN = True + self.failUnless(self.backend.registration_allowed(_mock_request())) + + settings.REGISTRATION_OPEN = False + self.failIf(self.backend.registration_allowed(_mock_request())) + settings.REGISTRATION_OPEN = old_allowed + + def test_form_class(self): + """ + Test that the default form class returned is + ``registration.forms.RegistrationForm``. + + """ + self.failUnless(self.backend.get_form_class(_mock_request()) is forms.RegistrationForm) + + def test_post_registration_redirect(self): + """ + Test that the default post-registration redirect is the named + pattern ``registration_complete``. + + """ + self.assertEqual(self.backend.post_registration_redirect(_mock_request(), User()), + ('registration_complete', (), {})) + + def test_registration_signal(self): + """ + Test that registering a user sends the ``user_registered`` + signal. + + """ + def receiver(sender, **kwargs): + self.failUnless('user' in kwargs) + self.assertEqual(kwargs['user'].username, 'bob') + self.failUnless('request' in kwargs) + self.failUnless(isinstance(kwargs['request'], WSGIRequest)) + received_signals.append(kwargs.get('signal')) + + received_signals = [] + signals.user_registered.connect(receiver, sender=self.backend.__class__) + + self.backend.register(_mock_request(), + username='bob', + email='bob@example.com', + password1='secret') + + self.assertEqual(len(received_signals), 1) + self.assertEqual(received_signals, [signals.user_registered]) + + def test_activation_signal_success(self): + """ + Test that successfully activating a user sends the + ``user_activated`` signal. + + """ + def receiver(sender, **kwargs): + self.failUnless('user' in kwargs) + self.assertEqual(kwargs['user'].username, 'bob') + self.failUnless('request' in kwargs) + self.failUnless(isinstance(kwargs['request'], WSGIRequest)) + received_signals.append(kwargs.get('signal')) + + received_signals = [] + signals.user_activated.connect(receiver, sender=self.backend.__class__) + + new_user = self.backend.register(_mock_request(), + username='bob', + email='bob@example.com', + password1='secret') + profile = RegistrationProfile.objects.get(user=new_user) + self.backend.activate(_mock_request(), profile.activation_key) + + self.assertEqual(len(received_signals), 1) + self.assertEqual(received_signals, [signals.user_activated]) + + def test_activation_signal_failure(self): + """ + Test that an unsuccessful activation attempt does not send the + ``user_activated`` signal. + + """ + receiver = lambda sender, **kwargs: received_signals.append(kwargs.get('signal')) + + received_signals = [] + signals.user_activated.connect(receiver, sender=self.backend.__class__) + + new_user = self.backend.register(_mock_request(), + username='bob', + email='bob@example.com', + password1='secret') + new_user.date_joined -= datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + new_user.save() + profile = RegistrationProfile.objects.get(user=new_user) + self.backend.activate(_mock_request(), profile.activation_key) + + self.assertEqual(len(received_signals), 0) + + def test_email_send_action(self): + """ + Test re-sending of activation emails via admin action. + + """ + admin_class = RegistrationAdmin(RegistrationProfile, admin.site) + + alice = self.backend.register(_mock_request(), + username='alice', + email='alice@example.com', + password1='swordfish') + + admin_class.resend_activation_email(_mock_request(), + RegistrationProfile.objects.all()) + self.assertEqual(len(mail.outbox), 2) # One on registering, one more on the resend. + + RegistrationProfile.objects.filter(user=alice).update(activation_key=RegistrationProfile.ACTIVATED) + admin_class.resend_activation_email(_mock_request(), + RegistrationProfile.objects.all()) + self.assertEqual(len(mail.outbox), 2) # No additional email because the account has activated. + + def test_activation_action(self): + """ + Test manual activation of users view admin action. + + """ + admin_class = RegistrationAdmin(RegistrationProfile, admin.site) + + alice = self.backend.register(_mock_request(), + username='alice', + email='alice@example.com', + password1='swordfish') + + admin_class.activate_users(_mock_request(), + RegistrationProfile.objects.all()) + self.failUnless(User.objects.get(username='alice').is_active) diff --git a/thirdpart/registration/tests/forms.py b/thirdpart/registration/tests/forms.py new file mode 100644 index 0000000000..505374fde9 --- /dev/null +++ b/thirdpart/registration/tests/forms.py @@ -0,0 +1,119 @@ +from django.contrib.auth.models import User +from django.test import TestCase + +from registration import forms + + +class RegistrationFormTests(TestCase): + """ + Test the default registration forms. + + """ + def test_registration_form(self): + """ + Test that ``RegistrationForm`` enforces username constraints + and matching passwords. + + """ + # Create a user so we can verify that duplicate usernames aren't + # permitted. + User.objects.create_user('alice', 'alice@example.com', 'secret') + + invalid_data_dicts = [ + # Non-alphanumeric username. + {'data': {'username': 'foo/bar', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo'}, + 'error': ('username', [u"This value must contain only letters, numbers and underscores."])}, + # Already-existing username. + {'data': {'username': 'alice', + 'email': 'alice@example.com', + 'password1': 'secret', + 'password2': 'secret'}, + 'error': ('username', [u"A user with that username already exists."])}, + # Mismatched passwords. + {'data': {'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'bar'}, + 'error': ('__all__', [u"The two password fields didn't match."])}, + ] + + for invalid_dict in invalid_data_dicts: + form = forms.RegistrationForm(data=invalid_dict['data']) + self.failIf(form.is_valid()) + self.assertEqual(form.errors[invalid_dict['error'][0]], + invalid_dict['error'][1]) + + form = forms.RegistrationForm(data={'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo'}) + self.failUnless(form.is_valid()) + + def test_registration_form_tos(self): + """ + Test that ``RegistrationFormTermsOfService`` requires + agreement to the terms of service. + + """ + form = forms.RegistrationFormTermsOfService(data={'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo'}) + self.failIf(form.is_valid()) + self.assertEqual(form.errors['tos'], + [u"You must agree to the terms to register"]) + + form = forms.RegistrationFormTermsOfService(data={'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo', + 'tos': 'on'}) + self.failUnless(form.is_valid()) + + def test_registration_form_unique_email(self): + """ + Test that ``RegistrationFormUniqueEmail`` validates uniqueness + of email addresses. + + """ + # Create a user so we can verify that duplicate addresses + # aren't permitted. + User.objects.create_user('alice', 'alice@example.com', 'secret') + + form = forms.RegistrationFormUniqueEmail(data={'username': 'foo', + 'email': 'alice@example.com', + 'password1': 'foo', + 'password2': 'foo'}) + self.failIf(form.is_valid()) + self.assertEqual(form.errors['email'], + [u"This email address is already in use. Please supply a different email address."]) + + form = forms.RegistrationFormUniqueEmail(data={'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo'}) + self.failUnless(form.is_valid()) + + def test_registration_form_no_free_email(self): + """ + Test that ``RegistrationFormNoFreeEmail`` disallows + registration with free email addresses. + + """ + base_data = {'username': 'foo', + 'password1': 'foo', + 'password2': 'foo'} + for domain in forms.RegistrationFormNoFreeEmail.bad_domains: + invalid_data = base_data.copy() + invalid_data['email'] = u"foo@%s" % domain + form = forms.RegistrationFormNoFreeEmail(data=invalid_data) + self.failIf(form.is_valid()) + self.assertEqual(form.errors['email'], + [u"Registration using free email addresses is prohibited. Please supply a different email address."]) + + base_data['email'] = 'foo@example.com' + form = forms.RegistrationFormNoFreeEmail(data=base_data) + self.failUnless(form.is_valid()) diff --git a/thirdpart/registration/tests/models.py b/thirdpart/registration/tests/models.py new file mode 100644 index 0000000000..f763cb2c7d --- /dev/null +++ b/thirdpart/registration/tests/models.py @@ -0,0 +1,225 @@ +import datetime +import re + +from django.conf import settings +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.core import mail +from django.core import management +from django.test import TestCase +from django.utils.hashcompat import sha_constructor + +from registration.models import RegistrationProfile + + +class RegistrationModelTests(TestCase): + """ + Test the model and manager used in the default backend. + + """ + user_info = {'username': 'alice', + 'password': 'swordfish', + 'email': 'alice@example.com'} + + def setUp(self): + self.old_activation = getattr(settings, 'ACCOUNT_ACTIVATION_DAYS', None) + settings.ACCOUNT_ACTIVATION_DAYS = 7 + + def tearDown(self): + settings.ACCOUNT_ACTIVATION_DAYS = self.old_activation + + def test_profile_creation(self): + """ + Creating a registration profile for a user populates the + profile with the correct user and a SHA1 hash to use as + activation key. + + """ + new_user = User.objects.create_user(**self.user_info) + profile = RegistrationProfile.objects.create_profile(new_user) + + self.assertEqual(RegistrationProfile.objects.count(), 1) + self.assertEqual(profile.user.id, new_user.id) + self.failUnless(re.match('^[a-f0-9]{40}$', profile.activation_key)) + self.assertEqual(unicode(profile), + "Registration information for alice") + + def test_activation_email(self): + """ + ``RegistrationProfile.send_activation_email`` sends an + email. + + """ + new_user = User.objects.create_user(**self.user_info) + profile = RegistrationProfile.objects.create_profile(new_user) + profile.send_activation_email(Site.objects.get_current()) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].to, [self.user_info['email']]) + + def test_user_creation(self): + """ + Creating a new user populates the correct data, and sets the + user's account inactive. + + """ + new_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(), + **self.user_info) + self.assertEqual(new_user.username, 'alice') + self.assertEqual(new_user.email, 'alice@example.com') + self.failUnless(new_user.check_password('swordfish')) + self.failIf(new_user.is_active) + + def test_user_creation_email(self): + """ + By default, creating a new user sends an activation email. + + """ + new_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(), + **self.user_info) + self.assertEqual(len(mail.outbox), 1) + + def test_user_creation_no_email(self): + """ + Passing ``send_email=False`` when creating a new user will not + send an activation email. + + """ + new_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(), + send_email=False, + **self.user_info) + self.assertEqual(len(mail.outbox), 0) + + def test_unexpired_account(self): + """ + ``RegistrationProfile.activation_key_expired()`` is ``False`` + within the activation window. + + """ + new_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(), + **self.user_info) + profile = RegistrationProfile.objects.get(user=new_user) + self.failIf(profile.activation_key_expired()) + + def test_expired_account(self): + """ + ``RegistrationProfile.activation_key_expired()`` is ``True`` + outside the activation window. + + """ + new_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(), + **self.user_info) + new_user.date_joined -= datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + new_user.save() + profile = RegistrationProfile.objects.get(user=new_user) + self.failUnless(profile.activation_key_expired()) + + def test_valid_activation(self): + """ + Activating a user within the permitted window makes the + account active, and resets the activation key. + + """ + new_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(), + **self.user_info) + profile = RegistrationProfile.objects.get(user=new_user) + activated = RegistrationProfile.objects.activate_user(profile.activation_key) + + self.failUnless(isinstance(activated, User)) + self.assertEqual(activated.id, new_user.id) + self.failUnless(activated.is_active) + + profile = RegistrationProfile.objects.get(user=new_user) + self.assertEqual(profile.activation_key, RegistrationProfile.ACTIVATED) + + def test_expired_activation(self): + """ + Attempting to activate outside the permitted window does not + activate the account. + + """ + new_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(), + **self.user_info) + new_user.date_joined -= datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + new_user.save() + + profile = RegistrationProfile.objects.get(user=new_user) + activated = RegistrationProfile.objects.activate_user(profile.activation_key) + + self.failIf(isinstance(activated, User)) + self.failIf(activated) + + new_user = User.objects.get(username='alice') + self.failIf(new_user.is_active) + + profile = RegistrationProfile.objects.get(user=new_user) + self.assertNotEqual(profile.activation_key, RegistrationProfile.ACTIVATED) + + def test_activation_invalid_key(self): + """ + Attempting to activate with a key which is not a SHA1 hash + fails. + + """ + self.failIf(RegistrationProfile.objects.activate_user('foo')) + + def test_activation_already_activated(self): + """ + Attempting to re-activate an already-activated account fails. + + """ + new_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(), + **self.user_info) + profile = RegistrationProfile.objects.get(user=new_user) + RegistrationProfile.objects.activate_user(profile.activation_key) + + profile = RegistrationProfile.objects.get(user=new_user) + self.failIf(RegistrationProfile.objects.activate_user(profile.activation_key)) + + def test_activation_nonexistent_key(self): + """ + Attempting to activate with a non-existent key (i.e., one not + associated with any account) fails. + + """ + # Due to the way activation keys are constructed during + # registration, this will never be a valid key. + invalid_key = sha_constructor('foo').hexdigest() + self.failIf(RegistrationProfile.objects.activate_user(invalid_key)) + + def test_expired_user_deletion(self): + """ + ``RegistrationProfile.objects.delete_expired_users()`` only + deletes inactive users whose activation window has expired. + + """ + new_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(), + **self.user_info) + expired_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(), + username='bob', + password='secret', + email='bob@example.com') + expired_user.date_joined -= datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + expired_user.save() + + RegistrationProfile.objects.delete_expired_users() + self.assertEqual(RegistrationProfile.objects.count(), 1) + self.assertRaises(User.DoesNotExist, User.objects.get, username='bob') + + def test_management_command(self): + """ + The ``cleanupregistration`` management command properly + deletes expired accounts. + + """ + new_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(), + **self.user_info) + expired_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(), + username='bob', + password='secret', + email='bob@example.com') + expired_user.date_joined -= datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + expired_user.save() + + management.call_command('cleanupregistration') + self.assertEqual(RegistrationProfile.objects.count(), 1) + self.assertRaises(User.DoesNotExist, User.objects.get, username='bob') diff --git a/thirdpart/registration/tests/urls.py b/thirdpart/registration/tests/urls.py new file mode 100644 index 0000000000..02ef609000 --- /dev/null +++ b/thirdpart/registration/tests/urls.py @@ -0,0 +1,82 @@ +""" +URLs used in the unit tests for django-registration. + +You should not attempt to use these URLs in any sort of real or +development environment; instead, use +``registration/backends/default/urls.py``. This URLconf includes those +URLs, and also adds several additional URLs which serve no purpose +other than to test that optional keyword arguments are properly +handled. + +""" + +from django.conf.urls.defaults import * +from django.views.generic.simple import direct_to_template + +from registration.views import activate +from registration.views import register + + +urlpatterns = patterns('', + # Test the 'activate' view with custom template + # name. + url(r'^activate-with-template-name/(?P\w+)/$', + activate, + {'template_name': 'registration/test_template_name.html', + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_activate_template_name'), + # Test the 'activate' view with + # extra_context_argument. + url(r'^activate-extra-context/(?P\w+)/$', + activate, + {'extra_context': {'foo': 'bar', 'callable': lambda: 'called'}, + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_activate_extra_context'), + # Test the 'activate' view with success_url argument. + url(r'^activate-with-success-url/(?P\w+)/$', + activate, + {'success_url': 'registration_test_custom_success_url', + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_activate_success_url'), + # Test the 'register' view with custom template + # name. + url(r'^register-with-template-name/$', + register, + {'template_name': 'registration/test_template_name.html', + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_register_template_name'), + # Test the'register' view with extra_context + # argument. + url(r'^register-extra-context/$', + register, + {'extra_context': {'foo': 'bar', 'callable': lambda: 'called'}, + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_register_extra_context'), + # Test the 'register' view with custom URL for + # closed registration. + url(r'^register-with-disallowed-url/$', + register, + {'disallowed_url': 'registration_test_custom_disallowed', + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_register_disallowed_url'), + # Set up a pattern which will correspond to the + # custom 'disallowed_url' above. + url(r'^custom-disallowed/$', + direct_to_template, + {'template': 'registration/registration_closed.html'}, + name='registration_test_custom_disallowed'), + # Test the 'register' view with custom redirect + # on successful registration. + url(r'^register-with-success_url/$', + register, + {'success_url': 'registration_test_custom_success_url', + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_register_success_url' + ), + # Pattern for custom redirect set above. + url(r'^custom-success/$', + direct_to_template, + {'template': 'registration/test_template_name.html'}, + name='registration_test_custom_success_url'), + (r'', include('registration.backends.default.urls')), + ) diff --git a/thirdpart/registration/tests/views.py b/thirdpart/registration/tests/views.py new file mode 100644 index 0000000000..17d3ad5303 --- /dev/null +++ b/thirdpart/registration/tests/views.py @@ -0,0 +1,246 @@ +import datetime + +from django.conf import settings +from django.contrib.auth.models import User +from django.core import mail +from django.core.urlresolvers import reverse +from django.test import TestCase + +from registration import forms +from registration.models import RegistrationProfile + + +class RegistrationViewTests(TestCase): + """ + Test the registration views. + + """ + urls = 'registration.tests.urls' + + def setUp(self): + """ + These tests use the default backend, since we know it's + available; that needs to have ``ACCOUNT_ACTIVATION_DAYS`` set. + + """ + self.old_activation = getattr(settings, 'ACCOUNT_ACTIVATION_DAYS', None) + if self.old_activation is None: + settings.ACCOUNT_ACTIVATION_DAYS = 7 + + def tearDown(self): + """ + Yank ``ACCOUNT_ACTIVATION_DAYS`` back out if it wasn't + originally set. + + """ + if self.old_activation is None: + settings.ACCOUNT_ACTIVATION_DAYS = self.old_activation + + def test_registration_view_initial(self): + """ + A ``GET`` to the ``register`` view uses the appropriate + template and populates the registration form into the context. + + """ + response = self.client.get(reverse('registration_register')) + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed(response, + 'registration/registration_form.html') + self.failUnless(isinstance(response.context['form'], + forms.RegistrationForm)) + + def test_registration_view_success(self): + """ + A ``POST`` to the ``register`` view with valid data properly + creates a new user and issues a redirect. + + """ + response = self.client.post(reverse('registration_register'), + data={'username': 'alice', + 'email': 'alice@example.com', + 'password1': 'swordfish', + 'password2': 'swordfish'}) + self.assertRedirects(response, + 'http://testserver%s' % reverse('registration_complete')) + self.assertEqual(RegistrationProfile.objects.count(), 1) + self.assertEqual(len(mail.outbox), 1) + + def test_registration_view_failure(self): + """ + A ``POST`` to the ``register`` view with invalid data does not + create a user, and displays appropriate error messages. + + """ + response = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bobe@example.com', + 'password1': 'foo', + 'password2': 'bar'}) + self.assertEqual(response.status_code, 200) + self.failIf(response.context['form'].is_valid()) + self.assertFormError(response, 'form', field=None, + errors=u"The two password fields didn't match.") + self.assertEqual(len(mail.outbox), 0) + + def test_registration_view_closed(self): + """ + Any attempt to access the ``register`` view when registration + is closed fails and redirects. + + """ + old_allowed = getattr(settings, 'REGISTRATION_OPEN', True) + settings.REGISTRATION_OPEN = False + + closed_redirect = 'http://testserver%s' % reverse('registration_disallowed') + + response = self.client.get(reverse('registration_register')) + self.assertRedirects(response, closed_redirect) + + # Even if valid data is posted, it still shouldn't work. + response = self.client.post(reverse('registration_register'), + data={'username': 'alice', + 'email': 'alice@example.com', + 'password1': 'swordfish', + 'password2': 'swordfish'}) + self.assertRedirects(response, closed_redirect) + self.assertEqual(RegistrationProfile.objects.count(), 0) + + settings.REGISTRATION_OPEN = old_allowed + + def test_registration_template_name(self): + """ + Passing ``template_name`` to the ``register`` view will result + in that template being used. + + """ + response = self.client.get(reverse('registration_test_register_template_name')) + self.assertTemplateUsed(response, + 'registration/test_template_name.html') + + def test_registration_extra_context(self): + """ + Passing ``extra_context`` to the ``register`` view will + correctly populate the context. + + """ + response = self.client.get(reverse('registration_test_register_extra_context')) + self.assertEqual(response.context['foo'], 'bar') + # Callables in extra_context are called to obtain the value. + self.assertEqual(response.context['callable'], 'called') + + def test_registration_disallowed_url(self): + """ + Passing ``disallowed_url`` to the ``register`` view will + result in a redirect to that URL when registration is closed. + + """ + old_allowed = getattr(settings, 'REGISTRATION_OPEN', True) + settings.REGISTRATION_OPEN = False + + closed_redirect = 'http://testserver%s' % reverse('registration_test_custom_disallowed') + + response = self.client.get(reverse('registration_test_register_disallowed_url')) + self.assertRedirects(response, closed_redirect) + + settings.REGISTRATION_OPEN = old_allowed + + def test_registration_success_url(self): + """ + Passing ``success_url`` to the ``register`` view will result + in a redirect to that URL when registration is successful. + + """ + success_redirect = 'http://testserver%s' % reverse('registration_test_custom_success_url') + response = self.client.post(reverse('registration_test_register_success_url'), + data={'username': 'alice', + 'email': 'alice@example.com', + 'password1': 'swordfish', + 'password2': 'swordfish'}) + self.assertRedirects(response, success_redirect) + + def test_valid_activation(self): + """ + Test that the ``activate`` view properly handles a valid + activation (in this case, based on the default backend's + activation window). + + """ + success_redirect = 'http://testserver%s' % reverse('registration_activation_complete') + + # First, register an account. + self.client.post(reverse('registration_register'), + data={'username': 'alice', + 'email': 'alice@example.com', + 'password1': 'swordfish', + 'password2': 'swordfish'}) + profile = RegistrationProfile.objects.get(user__username='alice') + response = self.client.get(reverse('registration_activate', + kwargs={'activation_key': profile.activation_key})) + self.assertRedirects(response, success_redirect) + self.failUnless(User.objects.get(username='alice').is_active) + + def test_invalid_activation(self): + """ + Test that the ``activate`` view properly handles an invalid + activation (in this case, based on the default backend's + activation window). + + """ + # Register an account and reset its date_joined to be outside + # the activation window. + self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + expired_user = User.objects.get(username='bob') + expired_user.date_joined = expired_user.date_joined - datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS) + expired_user.save() + + expired_profile = RegistrationProfile.objects.get(user=expired_user) + response = self.client.get(reverse('registration_activate', + kwargs={'activation_key': expired_profile.activation_key})) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.context['activation_key'], + expired_profile.activation_key) + self.failIf(User.objects.get(username='bob').is_active) + + def test_activation_success_url(self): + """ + Passing ``success_url`` to the ``activate`` view and + successfully activating will result in that URL being used for + the redirect. + + """ + success_redirect = 'http://testserver%s' % reverse('registration_test_custom_success_url') + self.client.post(reverse('registration_register'), + data={'username': 'alice', + 'email': 'alice@example.com', + 'password1': 'swordfish', + 'password2': 'swordfish'}) + profile = RegistrationProfile.objects.get(user__username='alice') + response = self.client.get(reverse('registration_test_activate_success_url', + kwargs={'activation_key': profile.activation_key})) + self.assertRedirects(response, success_redirect) + + def test_activation_template_name(self): + """ + Passing ``template_name`` to the ``activate`` view will result + in that template being used. + + """ + response = self.client.get(reverse('registration_test_activate_template_name', + kwargs={'activation_key': 'foo'})) + self.assertTemplateUsed(response, 'registration/test_template_name.html') + + def test_activation_extra_context(self): + """ + Passing ``extra_context`` to the ``activate`` view will + correctly populate the context. + + """ + response = self.client.get(reverse('registration_test_activate_extra_context', + kwargs={'activation_key': 'foo'})) + self.assertEqual(response.context['foo'], 'bar') + # Callables in extra_context are called to obtain the value. + self.assertEqual(response.context['callable'], 'called') diff --git a/thirdpart/registration/urls.py b/thirdpart/registration/urls.py new file mode 100644 index 0000000000..af4102653b --- /dev/null +++ b/thirdpart/registration/urls.py @@ -0,0 +1,15 @@ +""" +Backwards-compatible URLconf for existing django-registration +installs; this allows the standard ``include('registration.urls')`` to +continue working, but that usage is deprecated and will be removed for +django-registration 1.0. For new installs, use +``include('registration.backends.default.urls')``. + +""" + +import warnings + +warnings.warn("include('registration.urls') is deprecated; use include('registration.backends.default.urls') instead.", + PendingDeprecationWarning) + +from registration.backends.default.urls import * diff --git a/thirdpart/registration/views.py b/thirdpart/registration/views.py new file mode 100644 index 0000000000..5a26910ad1 --- /dev/null +++ b/thirdpart/registration/views.py @@ -0,0 +1,204 @@ +""" +Views which allow users to create and activate accounts. + +""" + + +from django.shortcuts import redirect +from django.shortcuts import render_to_response +from django.template import RequestContext + +from registration.backends import get_backend + + +def activate(request, backend, + template_name='registration/activate.html', + success_url=None, extra_context=None, **kwargs): + """ + Activate a user's account. + + The actual activation of the account will be delegated to the + backend specified by the ``backend`` keyword argument (see below); + the backend's ``activate()`` method will be called, passing any + keyword arguments captured from the URL, and will be assumed to + return a ``User`` if activation was successful, or a value which + evaluates to ``False`` in boolean context if not. + + Upon successful activation, the backend's + ``post_activation_redirect()`` method will be called, passing the + ``HttpRequest`` and the activated ``User`` to determine the URL to + redirect the user to. To override this, pass the argument + ``success_url`` (see below). + + On unsuccessful activation, will render the template + ``registration/activate.html`` to display an error message; to + override thise, pass the argument ``template_name`` (see below). + + **Arguments** + + ``backend`` + The dotted Python import path to the backend class to + use. Required. + + ``extra_context`` + A dictionary of variables to add to the template context. Any + callable object in this dictionary will be called to produce + the end result which appears in the context. Optional. + + ``success_url`` + The name of a URL pattern to redirect to on successful + acivation. This is optional; if not specified, this will be + obtained by calling the backend's + ``post_activation_redirect()`` method. + + ``template_name`` + A custom template to use. This is optional; if not specified, + this will default to ``registration/activate.html``. + + ``\*\*kwargs`` + Any keyword arguments captured from the URL, such as an + activation key, which will be passed to the backend's + ``activate()`` method. + + **Context:** + + The context will be populated from the keyword arguments captured + in the URL, and any extra variables supplied in the + ``extra_context`` argument (see above). + + **Template:** + + registration/activate.html or ``template_name`` keyword argument. + + """ + backend = get_backend(backend) + account = backend.activate(request, **kwargs) + + if account: + if success_url is None: + to, args, kwargs = backend.post_activation_redirect(request, account) + return redirect(to, *args, **kwargs) + else: + return redirect(success_url) + + if extra_context is None: + extra_context = {} + context = RequestContext(request) + for key, value in extra_context.items(): + context[key] = callable(value) and value() or value + + return render_to_response(template_name, + kwargs, + context_instance=context) + + +def register(request, backend, success_url=None, form_class=None, + disallowed_url='registration_disallowed', + template_name='registration/registration_form.html', + extra_context=None): + """ + Allow a new user to register an account. + + The actual registration of the account will be delegated to the + backend specified by the ``backend`` keyword argument (see below); + it will be used as follows: + + 1. The backend's ``registration_allowed()`` method will be called, + passing the ``HttpRequest``, to determine whether registration + of an account is to be allowed; if not, a redirect is issued to + the view corresponding to the named URL pattern + ``registration_disallowed``. To override this, see the list of + optional arguments for this view (below). + + 2. The form to use for account registration will be obtained by + calling the backend's ``get_form_class()`` method, passing the + ``HttpRequest``. To override this, see the list of optional + arguments for this view (below). + + 3. If valid, the form's ``cleaned_data`` will be passed (as + keyword arguments, and along with the ``HttpRequest``) to the + backend's ``register()`` method, which should return the new + ``User`` object. + + 4. Upon successful registration, the backend's + ``post_registration_redirect()`` method will be called, passing + the ``HttpRequest`` and the new ``User``, to determine the URL + to redirect the user to. To override this, see the list of + optional arguments for this view (below). + + **Required arguments** + + None. + + **Optional arguments** + + ``backend`` + The dotted Python import path to the backend class to use. + + ``disallowed_url`` + URL to redirect to if registration is not permitted for the + current ``HttpRequest``. Must be a value which can legally be + passed to ``django.shortcuts.redirect``. If not supplied, this + will be whatever URL corresponds to the named URL pattern + ``registration_disallowed``. + + ``form_class`` + The form class to use for registration. If not supplied, this + will be retrieved from the registration backend. + + ``extra_context`` + A dictionary of variables to add to the template context. Any + callable object in this dictionary will be called to produce + the end result which appears in the context. + + ``success_url`` + URL to redirect to after successful registration. Must be a + value which can legally be passed to + ``django.shortcuts.redirect``. If not supplied, this will be + retrieved from the registration backend. + + ``template_name`` + A custom template to use. If not supplied, this will default + to ``registration/registration_form.html``. + + **Context:** + + ``form`` + The registration form. + + Any extra variables supplied in the ``extra_context`` argument + (see above). + + **Template:** + + registration/registration_form.html or ``template_name`` keyword + argument. + + """ + backend = get_backend(backend) + if not backend.registration_allowed(request): + return redirect(disallowed_url) + if form_class is None: + form_class = backend.get_form_class(request) + + if request.method == 'POST': + form = form_class(data=request.POST, files=request.FILES) + if form.is_valid(): + new_user = backend.register(request, **form.cleaned_data) + if success_url is None: + to, args, kwargs = backend.post_registration_redirect(request, new_user) + return redirect(to, *args, **kwargs) + else: + return redirect(success_url) + else: + form = form_class() + + if extra_context is None: + extra_context = {} + context = RequestContext(request) + for key, value in extra_context.items(): + context[key] = callable(value) and value() or value + + return render_to_response(template_name, + { 'form': form }, + context_instance=context) diff --git a/urls.py b/urls.py index 2240c826e1..0273807367 100644 --- a/urls.py +++ b/urls.py @@ -1,10 +1,10 @@ from django.conf.urls.defaults import * from django.conf import settings -from seahub.views import root, home, peers, groups +from seahub.views import root, home, peers, groups, myhome # Uncomment the next two lines to enable the admin: -# from django.contrib import admin -# admin.autodiscover() +from django.contrib import admin +admin.autodiscover() urlpatterns = patterns('', # Example: @@ -15,12 +15,18 @@ urlpatterns = patterns('', # (r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: - # (r'^admin/', include(admin.site.urls)), + (r'^admin/', include(admin.site.urls)), + + (r'^accounts/', include('registration.backends.default.urls')), (r'^$', root), (r'^home/$', home), + (r'^home/my/$', myhome), (r'^peers/$', peers), (r'^groups/$', groups), + + (r'^avatar/', include('avatar.urls')), + (r'^profile/', include('seahub.profile.urls')), ) if settings.DEBUG: diff --git a/views.py b/views.py index 3a9bcdb07b..7673ea9d18 100644 --- a/views.py +++ b/views.py @@ -2,6 +2,7 @@ from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render_to_response from django.core.urlresolvers import reverse from django.template import RequestContext +from django.contrib.auth.decorators import login_required from seaserv import cclient, ccnet_rpc, get_groups, get_users @@ -14,6 +15,13 @@ def home(request): }, context_instance=RequestContext(request)) +@login_required +def myhome(request): + return render_to_response('myhome.html', { + }, context_instance=RequestContext(request)) + + + def peers(request): peer_type = request.REQUEST.get('type', 'all') peer_ids = ccnet_rpc.list_peers()