From 608b5149fc06ea5c402998b0c97528fed1116e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Marti=CC=81n?= Date: Sat, 10 Jan 2015 10:36:35 +0100 Subject: [PATCH 01/54] Fixed setup_environment fabfile task --- config/requirements.txt | 1 + fabfile.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config/requirements.txt b/config/requirements.txt index 9cdc129..a91b730 100644 --- a/config/requirements.txt +++ b/config/requirements.txt @@ -17,6 +17,7 @@ dj-database-url==0.3.0 psycopg2==2.5.4 # Files +django-mptt==0.6.1 django-filer==0.9.8 # Blog diff --git a/fabfile.py b/fabfile.py index ce74215..8edfc2b 100644 --- a/fabfile.py +++ b/fabfile.py @@ -54,7 +54,7 @@ def setup_virtualenv(): Creates or updates a virtualenv """ print(yellow('Create virtualenv')) - local('virtualenv-2.7 .virtualenv') + local('virtualenv .virtualenv') with virtualenv(): print(yellow('Installing requirements')) From 96f90ec0b4e075d4a36b1ec7daa64568374a3240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Marti=CC=81n?= Date: Sat, 10 Jan 2015 10:36:48 +0100 Subject: [PATCH 02/54] Fixed javascript path from old library --- shelfzilla/themes/bootflat/templates/_layout.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shelfzilla/themes/bootflat/templates/_layout.html b/shelfzilla/themes/bootflat/templates/_layout.html index 29485a1..aeb9ab7 100644 --- a/shelfzilla/themes/bootflat/templates/_layout.html +++ b/shelfzilla/themes/bootflat/templates/_layout.html @@ -100,7 +100,7 @@ var USE_PJAX = {{ site_config.use_pjax|lower }}; - + From 68ae5659294b9d122b5c1bbd3a3b6c0e35dac422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Marti=CC=81n?= Date: Sat, 10 Jan 2015 10:58:50 +0100 Subject: [PATCH 03/54] Added AccessCode model --- shelfzilla/apps/account/admin.py | 10 +++++ .../migrations/0003_auto_20150110_1056.py | 37 ++++++++++++++++++ .../migrations/0004_auto_20150110_1058.py | 20 ++++++++++ shelfzilla/apps/account/models.py | 38 +++++++++++++++++++ shelfzilla/settings/base.py | 2 +- 5 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 shelfzilla/apps/account/migrations/0003_auto_20150110_1056.py create mode 100644 shelfzilla/apps/account/migrations/0004_auto_20150110_1058.py diff --git a/shelfzilla/apps/account/admin.py b/shelfzilla/apps/account/admin.py index 6c72074..8fd9d14 100644 --- a/shelfzilla/apps/account/admin.py +++ b/shelfzilla/apps/account/admin.py @@ -103,3 +103,13 @@ class UserAdmin(DjangoUserAdmin): # Now register the new UserAdmin... admin.site.register(models.User, UserAdmin) admin.site.register(Permission) + + +class AccessCodeAdmin(admin.ModelAdmin): + list_display = ('code', 'max_uses', 'used_by', 'expiration', 'active', 'usable') + + def usable(self, obj): + return obj.usabe + usable.boolean = True + +admin.site.register(models.AccessCode, AccessCodeAdmin) diff --git a/shelfzilla/apps/account/migrations/0003_auto_20150110_1056.py b/shelfzilla/apps/account/migrations/0003_auto_20150110_1056.py new file mode 100644 index 0000000..9653ec8 --- /dev/null +++ b/shelfzilla/apps/account/migrations/0003_auto_20150110_1056.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0002_auto_20141111_1208'), + ] + + operations = [ + migrations.CreateModel( + name='AccessCode', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('code', models.CharField(max_length=128, verbose_name='Code')), + ('max_uses', models.IntegerField(default=1, verbose_name='Number of uses')), + ('expiration', models.DateTimeField(default=None, null=True, verbose_name='Expires')), + ('active', models.BooleanField(default=True, verbose_name='Active')), + ('user', models.ForeignKey(related_name='access_codes', blank=True, to=settings.AUTH_USER_MODEL, null=True)), + ], + options={ + 'verbose_name': 'Access code', + 'verbose_name_plural': 'Access codes', + }, + bases=(models.Model,), + ), + migrations.AddField( + model_name='user', + name='access_code', + field=models.ForeignKey(related_name='used_by', blank=True, to='account.AccessCode', null=True), + preserve_default=True, + ), + ] diff --git a/shelfzilla/apps/account/migrations/0004_auto_20150110_1058.py b/shelfzilla/apps/account/migrations/0004_auto_20150110_1058.py new file mode 100644 index 0000000..50db860 --- /dev/null +++ b/shelfzilla/apps/account/migrations/0004_auto_20150110_1058.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0003_auto_20150110_1056'), + ] + + operations = [ + migrations.AlterField( + model_name='accesscode', + name='expiration', + field=models.DateTimeField(default=None, null=True, verbose_name='Expires', blank=True), + preserve_default=True, + ), + ] diff --git a/shelfzilla/apps/account/models.py b/shelfzilla/apps/account/models.py index bc00560..ce27a28 100644 --- a/shelfzilla/apps/account/models.py +++ b/shelfzilla/apps/account/models.py @@ -92,6 +92,11 @@ class User(AbstractBaseUser, USERNAME_FIELD = 'username' REQUIRED_FIELDS = ('email',) + # Access codes / Invitations + access_code = models.ForeignKey('account.AccessCode', + null=True, blank=True, + related_name='used_by') + objects = UserManager() class Meta: @@ -132,3 +137,36 @@ class User(AbstractBaseUser, return today.year - birthdate.year - ( (today.month, today.day) < (birthdate.month, birthdate_day) ) + + +class AccessCode(models.Model): + code = models.CharField(_('Code'), max_length=128) + max_uses = models.IntegerField(_('Number of uses'), default=1) + user = models.ForeignKey(User, null=True, blank=True, + related_name='access_codes') + expiration = models.DateTimeField(_('Expires'), null=True, blank=True, + default=None) + active = models.BooleanField(_('Active'), default=True) + + class Meta: + verbose_name = _('Access code') + verbose_name_plural = _('Access codes') + + def __unicode__(self): + return self.code + + @property + def usabe(self): + # Check if active + if not self.active: + return False + + # Check if expired + if timezone.now() >= self.expiration: + return False + + # Check if it someone already used it + if self.used_by.count() == self.max_uses: + return False + + return True diff --git a/shelfzilla/settings/base.py b/shelfzilla/settings/base.py index b82c226..9aec081 100644 --- a/shelfzilla/settings/base.py +++ b/shelfzilla/settings/base.py @@ -255,7 +255,7 @@ SUIT_CONFIG = { { 'label': 'Authorization', 'icon': 'icon-lock', - 'models': ('account.user', 'auth.group') + 'models': ('account.user', 'auth.group', 'account.accesscode', ) }, { 'app': 'config', From 0eb4eef7072c8c338592bb6d7b200ad8cbdf932c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Marti=CC=81n?= Date: Sat, 10 Jan 2015 12:43:18 +0100 Subject: [PATCH 04/54] Added dummy registration form --- shelfzilla/apps/account/forms.py | 48 ++++++++++++++++++ shelfzilla/apps/account/urls.py | 5 +- shelfzilla/apps/account/views.py | 23 ++++++++- .../bootflat/templates/account/register.html | 50 +++++++++++++++++++ 4 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 shelfzilla/themes/bootflat/templates/account/register.html diff --git a/shelfzilla/apps/account/forms.py b/shelfzilla/apps/account/forms.py index b85e62d..895de7a 100644 --- a/shelfzilla/apps/account/forms.py +++ b/shelfzilla/apps/account/forms.py @@ -5,6 +5,8 @@ from django.contrib.auth.forms import ( ) from django.utils.translation import ugettext_lazy as _ +from . import models + class LoginForm(forms.Form): username = forms.CharField(max_length=75, label=_('Username')) @@ -43,3 +45,49 @@ class LoginForm(forms.Form): class PasswordChangeForm(DjangoPasswordChangeForm): pass + + +class RegistrationForm(forms.ModelForm): + """ + Custom for for registering an user + """ + password1 = forms.CharField(label=_('Password'), widget=forms.PasswordInput) + password2 = forms.CharField(label=_('Repeat password'), + widget=forms.PasswordInput) + access_code = forms.CharField(label=_('Invitation code'), required=True) + + class Meta: + model = models.User + fields = ('email', 'username', ) + + def get_access_code(self): + try: + return models.AccessCode.objects.get( + code=self.cleaned_data['access_code']) + except models.AccessCode.DoesNotExist: + return False + + def clean_access_code(self): + code = self.get_access_code() + if not code or (code and not code.usable): + raise forms.ValidationError(_('Invitation code is not valid')) + + return self.cleaned_data['access_code'] + + def clean_password2(self): + # Check that the two password entries match + password1 = self.cleaned_data.get("password1") + password2 = self.cleaned_data.get("password2") + if password1 and password2 and password1 != password2: + raise forms.ValidationError("Passwords don't match") + return password2 + + def save(self, commit=True): + # Save the provided password in hashed format + user = super(UserCreationForm, self).save(commit=False) + access_code = self.get_access_code() + user.access_code = access_code + user.set_password(self.cleaned_data["password1"]) + if commit: + user.save() + return user diff --git a/shelfzilla/apps/account/urls.py b/shelfzilla/apps/account/urls.py index c4f2a3f..69711fa 100644 --- a/shelfzilla/apps/account/urls.py +++ b/shelfzilla/apps/account/urls.py @@ -1,10 +1,13 @@ from django.conf.urls import patterns, url -from .views import LoginView, LogoutView, UserProfileView, AccountView +from .views import ( + LoginView, LogoutView, UserProfileView, AccountView, RegisterView +) urlpatterns = patterns( '', url(r'^login/$', LoginView.as_view(), name="login"), + url(r'^register/$', RegisterView.as_view(), name="register"), url(r'^logout/$', LogoutView.as_view(), name="logout"), url( r'^user/(?P[\w\d\-\.]+)/$', diff --git a/shelfzilla/apps/account/views.py b/shelfzilla/apps/account/views.py index 3227dc4..28ff6aa 100644 --- a/shelfzilla/apps/account/views.py +++ b/shelfzilla/apps/account/views.py @@ -11,7 +11,7 @@ from django.contrib import messages from django.contrib.auth import login from django.core.urlresolvers import reverse -from .forms import LoginForm, PasswordChangeForm +from .forms import LoginForm, PasswordChangeForm, RegistrationForm from .models import User from shelfzilla.apps.manga.models import ( UserReadVolume, UserHaveVolume, UserWishlistVolume @@ -140,3 +140,24 @@ class AccountView(View): ctx = RequestContext(request, data) return render_to_response(self.template, context_instance=ctx) + + +class RegisterView(View): + template = 'account/register.html' + form = RegistrationForm + + def get(self, request): + data = { + 'form': self.form + } + + ctx = RequestContext(request, data) + return render_to_response(self.template, context_instance=ctx) + + def post(self, request): + form = self.form(request.POST) + if form.is_valid(): + pass + + ctx = RequestContext(request, { 'form': form }) + return render_to_response(self.template, context_instance=ctx) diff --git a/shelfzilla/themes/bootflat/templates/account/register.html b/shelfzilla/themes/bootflat/templates/account/register.html new file mode 100644 index 0000000..0276a99 --- /dev/null +++ b/shelfzilla/themes/bootflat/templates/account/register.html @@ -0,0 +1,50 @@ +{% extends "_layout.html" %} +{% load i18n %} + +{% block main_content %} +
+
+
+
+
+
+

{% trans "Register" %}

+
+
+
+ {% csrf_token %} + {% for field in form %} +
+ {% for error in field.errors %} + {{ error }} + {% endfor %} +
+
+ +
+ + {% if field.errors %} + + {% endif %} +
+
+ {% endfor %} +
+
+ +
+
+
+
+
+
+
+
+{% endblock %} From be076e7d6ddb20e26319eb446c19942d5cd1feed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Marti=CC=81n?= Date: Sat, 10 Jan 2015 12:46:59 +0100 Subject: [PATCH 05/54] Added information message in registration form --- shelfzilla/themes/bootflat/templates/account/register.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/shelfzilla/themes/bootflat/templates/account/register.html b/shelfzilla/themes/bootflat/templates/account/register.html index 0276a99..69b6bcb 100644 --- a/shelfzilla/themes/bootflat/templates/account/register.html +++ b/shelfzilla/themes/bootflat/templates/account/register.html @@ -3,6 +3,10 @@ {% block main_content %}
+
+ Para registrarte en Shelfzilla necesitarás un código de invitación. Los registros abiertos no están disponibles por el momento.
+ Permanece atento a nuestras redes sociales, ¡pronto empezaremos a repatir! +
From 1d191fee80057778e104de1a3668dfc5a335ddec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Marti=CC=81n?= Date: Sat, 10 Jan 2015 12:52:19 +0100 Subject: [PATCH 06/54] Added registration links to the site --- .../themes/bootflat/templates/_layout.html | 5 ++ .../bootflat/templates/users/login.html | 54 +++++++++++-------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/shelfzilla/themes/bootflat/templates/_layout.html b/shelfzilla/themes/bootflat/templates/_layout.html index aeb9ab7..e3aae8d 100644 --- a/shelfzilla/themes/bootflat/templates/_layout.html +++ b/shelfzilla/themes/bootflat/templates/_layout.html @@ -64,6 +64,11 @@ {% else %} +
  • + + {% trans "Register" %} + +
  • {% trans "Log in" %} diff --git a/shelfzilla/themes/bootflat/templates/users/login.html b/shelfzilla/themes/bootflat/templates/users/login.html index 44a7cb3..2b334a2 100644 --- a/shelfzilla/themes/bootflat/templates/users/login.html +++ b/shelfzilla/themes/bootflat/templates/users/login.html @@ -1,7 +1,6 @@ {% extends '_layout.html' %} {% load i18n %} -{% block navigation_bar %}{% endblock %} {% block page_title %}{{ block.super }} | Login{% endblock %} {% block extra_js %} @@ -19,27 +18,40 @@ $(function(){ {% endblock %} {% block main_content %} -