Anpassungen für das Hosting bei Djangoeurope und damit verbundenen Versionen Django 1.8 und Python 2.7

This commit is contained in:
Christian Berg
2015-08-05 18:55:48 +02:00
committed by Christian Berg
parent cb4b15b3c6
commit b96b485b61
1354 changed files with 7289 additions and 6858 deletions

View File

@@ -0,0 +1 @@
PAID_MEMBERSHIP_GROUP = 2

72
src/membership/admin.py Normal file
View File

@@ -0,0 +1,72 @@
"""
Created on 19.09.2011
@author: christian
"""
# import stuff we need from django
from django.contrib import admin
from django.utils.translation import ugettext as _
from imagekit.admin import AdminThumbnail
from membership.models import Membership, ActivationRequest
def activate_user(modeladmin, request, queryset):
for activation in queryset:
membership = Membership.objects.get(username=activation.user.username)
membership.save()
activation.activate()
activate_user.short_description = _('Activate selected User')
def cleanup_activation(modeladmin, request, queryset):
for activation in queryset:
if activation.expired:
activation.user.delete()
cleanup_activation.short_description = _("Cleanup selected Activation Requests")
class MembershipAdmin(admin.ModelAdmin):
admin_thumbnail = AdminThumbnail(image_field='thumbnail')
list_filter = ('membership', 'confirmed')
list_display = (
'admin_thumbnail',
'nickname',
'first_name',
'last_name',
'membership',
'confirmed',
'paid_until',
)
list_editable = ('confirmed', 'paid_until',)
list_display_links = ('nickname',)
fieldsets = (
(None, {
'fields': (
'gender', ('first_name', 'last_name'), ('email', 'website'))
}),
(_('Membership'), {
'classes': ('collapse',),
'fields': (('membership', 'confirmed'), 'birthday', 'telephone',
'street_name', ('post_code', 'city'))
}),
)
ordering = ('nickname',)
search_fields = ('nickname', 'first_name', 'last_name',)
admin.site.register(Membership, MembershipAdmin)
class RegistrationAdmin(admin.ModelAdmin):
list_display = ('username', 'first_name', 'last_name', 'email',
'registration_date', 'expired')
search_fields = ('user__username', 'user__first_name')
actions = [cleanup_activation, activate_user]
admin.site.register(ActivationRequest, RegistrationAdmin)

150
src/membership/forms.py Normal file
View File

@@ -0,0 +1,150 @@
"""
Created on 03.10.2011
@author: Christian
"""
from django.conf import settings
from django.contrib import auth
from django.contrib.sites.models import Site
from django.utils.translation import ugettext_lazy as _
from . import models
from utils.html5 import forms
from utils.massmailer import MassMailer
class MembershipForm(forms.ModelForm):
error_css_class = 'error'
required_css_class = 'required'
birthday = forms.DateField(label=_('birthday'), required=False)
class Meta:
model = models.Membership
fields = (
'nickname', 'gender', 'first_name', 'last_name', 'email', 'avatar',
'website', 'membership', 'birthday', 'telephone', 'street_name',
'post_code', 'city', 'comment'
)
def clean_birthday(self):
if self.cleaned_data['membership'] and not self.cleaned_data[
'birthday']:
raise forms.ValidationError(_('For your membership, we need this. \
Please fill out this field yet.'))
return self.cleaned_data['birthday']
def clean_telephone(self):
if self.cleaned_data['membership'] and not self.cleaned_data[
'telephone']:
raise forms.ValidationError(_('For your membership, we need this. \
Please fill out this field yet.'))
return self.cleaned_data['telephone']
def clean_street_name(self):
if self.cleaned_data['membership'] and not self.cleaned_data[
'street_name']:
raise forms.ValidationError(_('For your membership, we need this. \
Please fill out this field yet.'))
return self.cleaned_data['street_name']
def clean_post_code(self):
if self.cleaned_data['membership'] and not self.cleaned_data[
'post_code']:
raise forms.ValidationError(_('For your membership, we need this. \
Please fill out this field yet.'))
return self.cleaned_data['post_code']
def clean_city(self):
if self.cleaned_data['membership'] and not self.cleaned_data['city']:
raise forms.ValidationError(_('For your membership, we need this. \
Please fill out this field yet.'))
return self.cleaned_data['city']
class RegistrationForm(forms.ModelForm):
"""
Form to register a new user account.
Validates that the requested username and email is not already in use,
requires the password to be entered twice to catch typos.
sends an activation request per mail, to validate the eMail.
"""
error_css_class = 'error'
required_css_class = 'required'
first_name = forms.CharField(max_length=30, required=True,
label=_('Given Name'))
last_name = forms.CharField(max_length=30, required=True,
label=_('Last Name'))
username = forms.SlugField(required=True, max_length=30,
label=_('Username'),
help_text=_("The Username can only contain the letters from A to Z, \
Numbers, and the underscore. It must be at least 2 characters long, \
and cannot be longer the 30. The first character must be a letter."))
email = forms.EmailField(required=True)
password1 = forms.CharField(widget=forms.PasswordInput(),
label=_('password'))
password2 = forms.CharField(widget=forms.PasswordInput(),
label=_('password (again)'))
recaptcha = forms.ReCaptchaField()
class Meta:
model = auth.get_user_model()
fields = ('first_name', 'last_name', 'username', 'email',)
def clean_username(self):
"""
Validate that the username is not already in use.
"""
try:
auth.get_user_model().objects.get(
username__iexact=self.cleaned_data['username']
)
except auth.get_user_model().DoesNotExist:
return self.cleaned_data['username']
raise forms.ValidationError(_(u'This username is already taken. \
Please choose another.'))
def clean_email(self):
"""
Validate that the supplied email address is unique for the site.
"""
if auth.get_user_model().objects.filter(
email__iexact=self.cleaned_data['email']):
raise forms.ValidationError(_(u'This email address is already in \
use. Please supply a different email address.'))
return self.cleaned_data['email']
def clean_password2(self):
password1 = self.cleaned_data.get("password1", "")
password2 = self.cleaned_data["password2"]
if password1 != password2:
raise forms.ValidationError(
_("The two password fields didn't match."))
return password2
def send_email(self, activation):
mailer = MassMailer()
mailer.subject = 'Deine Anmeldung auf %s' % Site.objects.get_current()
mailer.txt_template = 'membership/email/activation_email.txt'
mailer.context = {
'user': activation.user,
'site': Site.objects.get_current(),
'activation_key': activation.activation_key,
'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
}
mailer.add_recipient(activation.user)
mailer.send()
def save(self, commit=True):
"""
Creates the new ``User`` and ``RegistrationProfile`` and
returns the ``User``.
"""
user = super(RegistrationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
user.is_active = False
if commit:
user.save()
activation = models.ActivationRequest.objects.create_pending_registration(user) # @IgnorePep8
self.send_email(activation)
return user

View File

@@ -0,0 +1,396 @@
# 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 <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: kasu.membership\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-05 19:23+0100\n"
"PO-Revision-Date: 2014-12-08 16:18+0100\n"
"Last-Translator: Christian Berg <xeniac.at@gmail.com>\n"
"Language-Team: Kasu <verein@kasu.at>\n"
"Language: de\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"
"X-Translated-Using: django-rosetta 0.7.2\n"
"X-Generator: Poedit 1.6.11\n"
#: admin.py:21
msgid "Activate selected User"
msgstr "Ausgewählte Benutzer freischalten"
#: admin.py:30
msgid "Cleanup selected Activation Requests"
msgstr "Ausgewählte Aktivierungsanfragen bereinigen"
#: admin.py:52 models.py:193 models.py:252
msgid "Membership"
msgstr "Mitgliedschaft"
#: forms.py:19
msgid "birthday"
msgstr "Geburtstag"
#: forms.py:32 forms.py:39 forms.py:46 forms.py:53
msgid ""
"For your membership, we need this. Please fill out this field "
"yet."
msgstr "Diese Angabe wird für eine Mitgliedschaft benötigt, bitte ausfüllen."
#: forms.py:59
msgid ""
"For your membership, we need this. Please fill out this field "
"yet."
msgstr "Diese Angabe wird für eine Mitgliedschaft benötigt, bitte ausfüllen."
#: forms.py:75 models.py:182
msgid "Given Name"
msgstr "Vorname"
#: forms.py:77 models.py:183
msgid "Last Name"
msgstr "Nachname"
#: forms.py:79
msgid "Username"
msgstr "Benutzername"
#: forms.py:80
msgid ""
"The Username can only contain the letters from A to Z, Numbers, and "
"the underscore. It must be at least 2 characters long, and cannot be "
"longer the 30. The first character must be a letter."
msgstr ""
"Der Benutzername kann aus den Buchstaben A-Z, Ziffern und dem Unterstrich "
"bestehen. Es sollte wenigstens 2, aber maximal 30 Zeichen lang sein. Das "
"erste Zeichen muss ein Buchstabe sein."
#: forms.py:85
msgid "password"
msgstr "Passwort"
#: forms.py:87
msgid "password (again)"
msgstr "Passwort (wiederholen)"
#: forms.py:104
msgid "This username is already taken. Please choose another."
msgstr "Diesen Benutzername ist schon vergeben. Bitte einen anderen auswählen."
#: forms.py:113
msgid ""
"This email address is already in use. Please supply a different "
"email address."
msgstr "Die E-Mail Adresse wird schon verwendet. Bitte eine andere angeben."
#: forms.py:122
msgid "The two password fields didn't match."
msgstr "Die beiden Passwörter passen nicht."
#: models.py:22
msgid "Male"
msgstr "Männlich"
#: models.py:23
msgid "Female"
msgstr "Weiblich"
#: models.py:91
msgid "user"
msgstr "Benutzer"
#: models.py:93
msgid "activation key"
msgstr "Aktivierungsschlüssel"
#: models.py:97
msgid "pending activation"
msgstr "Ausstehende Aktivierung"
#: models.py:98
msgid "pending activations"
msgstr "Wartende Aktivierungen"
#: models.py:101
#, python-format
msgid "user registration for %s"
msgstr "Benutzerregistrierung für %s"
#: models.py:176
msgid "Nickname"
msgstr "Spitzname"
#: models.py:178
msgid "Gender"
msgstr "Geschlecht"
#: models.py:184
msgid "Email"
msgstr "E-Mail"
#: models.py:195
msgid ""
"Yes, I confirm that I am in agreement with the statutes and would "
"like to become a member."
msgstr "Ja, ich bin mit den Statuen einverstanden und möchte Mitglied werden."
#: models.py:198
msgid "Birthday Date"
msgstr "Geburtstag"
#: models.py:200
msgid "Telephone"
msgstr "Telefon"
#: models.py:206
msgid "Address"
msgstr "Adresse"
#: models.py:212
msgid "Postcode"
msgstr "Postleitzahl"
#: models.py:217
msgid "Town/City"
msgstr "Ort"
#: models.py:225
msgid "Paid until"
msgstr "Bezahlt bis"
#: models.py:231
msgid "Confirmed"
msgstr "Bestätigt"
#: models.py:233
msgid "This person has paid the membership fee."
msgstr "Diese Person hat ihre Mitgliedschaft bezahlt"
#: models.py:253
msgid "Memberships"
msgstr "Mitgliedschaften"
#: models.py:256
#, python-format
msgid "Userprofile for %s"
msgstr "Benutzerprofil für %s"
#: views.py:54
msgid ""
"Activation successful. You can now login anytime with you username "
"and password."
msgstr ""
"Die Aktivierung war erfolgreich. Du kannst dich ab jetzt jederzeit mit "
"deinem Benutzernamen und Passwort anmelden."
#: views.py:75
msgid "User Profile changed successfully"
msgstr "Benutzerprofil erfolgreich geändert."
#: templates/membership/hanchan_table.html:5
msgid "Start"
msgstr "Beginn"
#: templates/membership/hanchan_table.html:6
msgid "Event"
msgstr "Termin"
#: templates/membership/hanchan_table.html:7
msgid "Players"
msgstr "Spieler"
#: templates/membership/hanchan_table.html:8
msgid "Kyu Points"
msgstr "Kyū Punkte"
#: templates/membership/hanchan_table.html:9
msgid "Dan Points"
msgstr "Dan Punkte"
#: templates/membership/hanchan_table.html:10
msgid "Bonus Points"
msgstr "Bonus Punkte"
#: templates/membership/hanchan_table.html:11
msgid "Comment"
msgstr "Anmerkung"
#: templates/membership/hanchan_table.html:26
msgid "This Hanchan does not validate"
msgstr "Diese Hanchan ist ungültig"
#: templates/membership/membership_detail.html:6
msgid "profile for"
msgstr "Profil für"
#: templates/membership/membership_detail.html:10
msgid "Ladder Hanchans"
msgstr "Ladder Hanchans"
#: templates/membership/membership_detail.html:11
msgid "Kyu Hanchans"
msgstr "Kyū Hanchans"
#: templates/membership/membership_detail.html:12
msgid "Dan Hanchans"
msgstr "Dan Hanchans"
#: templates/membership/membership_detail.html:13
msgid "Invalid Hanchans"
msgstr "Ungültige Hanchans"
#: templates/membership/membership_detail.html:14
msgid "Mai-Star Games"
msgstr "Mai-Star Spiele"
#: templates/membership/membership_detail.html:20
msgid "Profile Image"
msgstr "Profilbild"
#: templates/membership/membership_detail.html:28
msgid "Member Since"
msgstr "Mitglied seit"
#: templates/membership/membership_detail.html:29
msgid "Last Login"
msgstr "Letzte Anmeldung"
#: templates/membership/membership_detail.html:38
#: templates/membership/membership_detail.html:40
msgid "Points"
msgstr "Punkte"
#: templates/membership/membership_detail.html:42
msgid "Games Total"
msgstr "Spiele gesamt"
#: templates/membership/membership_detail.html:43
#: templates/membership/membership_detail.html:45
msgid "Won"
msgstr "Gewonnen"
#: templates/membership/membership_detail.html:43
#: templates/membership/membership_detail.html:45
msgid "Good"
msgstr "Gut"
#: templates/membership/membership_detail.html:45
msgid "Current Season"
msgstr "Aktuelle Saison"
#: templates/membership/membership_detail.html:54
#: templates/membership/membership_form.html:3
#: templates/membership/membership_form.html:12
msgid "Edit Userprofile"
msgstr "Profil bearbeiten"
#: templates/membership/membership_detail.html:55
msgid "Edit Profile"
msgstr "Profil bearbeiten"
#: templates/membership/membership_detail.html:58
#: templates/membership/membership_detail.html:59
msgid "Change Password"
msgstr "Passwort ändern"
#: templates/membership/membership_detail.html:62
#: templates/membership/membership_detail.html:66
#: templates/membership/membership_detail.html:70
msgid "Associate"
msgstr "Verbinden"
#: templates/membership/membership_detail.html:63
#: templates/membership/membership_detail.html:67
#: templates/membership/membership_detail.html:71
#, python-format
msgid "Associate with %(name)s"
msgstr "Verbinde mit %(name)s"
#: templates/membership/membership_form.html:17
#: templates/membership/register_form.html:33
msgid "reset"
msgstr "Zurücksetzen"
#: templates/membership/membership_form.html:18
msgid "Reset"
msgstr "Zurücksetzen"
#: templates/membership/membership_form.html:21
msgid "save"
msgstr "Speichern"
#: templates/membership/membership_form.html:22
msgid "Save"
msgstr "Speichern"
#: templates/membership/register_form.html:5
#: templates/membership/register_form.html:7
msgid "Registration"
msgstr "Registrieren"
#: templates/membership/register_form.html:16
msgid "Name"
msgstr "Name"
#: templates/membership/register_form.html:21
msgid "Security"
msgstr "Sicherheit"
#: templates/membership/register_form.html:27
msgid ""
"After you've provided your account data, you'll\n"
" receive an email asking you to verify your email address.\n"
" You have to click on the link in this verification email to\n"
" confirm your email address, otherwise your can't login.\n"
" "
msgstr ""
"<p>Nach dem du deine Daten eingegeben hast, wirst du eine E-Mail zur "
"Bestätigung bekommen.</p>\n"
"<p>Bitte klicke auf den Link in dieser E-Mail zur Verifizierung, erst dann "
"ist die Anmeldung möglich.</p>"
#: templates/membership/register_form.html:34
msgid "register"
msgstr "Registrieren"
#: templates/membership/register_successful.html:5
#: templates/membership/register_successful.html:8
msgid "Activation sent"
msgstr "Aktivierung wurde zugesendet"
#: templates/membership/email/password_reset_email.html:2
msgid "You're receiving this e-mail because you requested a password reset"
msgstr ""
"Du hast diese E-Mail erhalten, weil jemand die das Passwort zurücksetzen "
"möchte. "
#: templates/membership/email/password_reset_email.html:3
#, python-format
msgid "for your user account at %(site_name)s"
msgstr "Für deinen Benutzerzugang auf %(site_name)s"
#: templates/membership/email/password_reset_email.html:5
msgid "Please go to the following page and choose a new password:"
msgstr "Bitte gehe auf folgende Seite und wähle ein neues Passwort aus:"
#: templates/membership/email/password_reset_email.html:9
msgid "Your username, in case you've forgotten:"
msgstr "Dein Benutzername, für den Fall das du diesen vergessen hast:"
#: templates/membership/email/password_reset_email.html:11
msgid "Thanks for using our site!"
msgstr "Danke das du unsere Seite verwendest!"
#: templates/membership/email/password_reset_email.html:13
#, python-format
msgid "The %(site_name)s team"
msgstr "Das %(site_name)s Team"
#: templates/membership/email/password_reset_subject.txt:2
#, python-format
msgid "Password reset on %(site_name)s"
msgstr "Passwort auf %(site_name)s zurücksetzen"

View File

@@ -0,0 +1 @@
#!/usr/bin/python

View File

@@ -0,0 +1 @@
#!/usr/bin/python

View File

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from django.core.management.base import BaseCommand
from membership.models import ActivationRequest
class Command(BaseCommand):
help = "Delete all expired user registrations from the database"
def handle(self, *args, **options):
for activation in ActivationRequest.objects.expired():
activation.user.delete()

View File

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
"""
When you get an error email from your Django app telling you someone got a
server error,
its not always easy to tell which user had a problem.
It might help your debugging to know or you might want to contact the user to
tell them you have fixed the problem.
"""
from optparse import make_option
from django.conf import settings
from django.contrib import auth
from django.contrib.sessions.models import Session
from django.core.management.base import BaseCommand
class Command(BaseCommand):
args = '<session_key session_key ...>'
help = 'If the session still exists find it and show the related User'
option_list = BaseCommand.option_list + (
make_option(
'--delete',
action='store_true',
dest='delete',
default=False,
help='Delete the Useraccount'
),
make_option(
'--ban',
action='store_true',
dest='ban',
default=False,
help='Ban the Useraccount'
),
)
def handle(self, *args, **options):
for session_key in args:
try:
session = Session.objects.get(session_key=session_key)
uid = session.get_decoded().get('_auth_user_id')
user = auth.get_user_model().objects.get(pk=uid)
except Session.DoesNotExist:
self.stderr.write('Session "%s" does not exist' % session_key)
continue
except settings.AUTH_USER_MODEL.DoesNotExist:
self.stderr.write(
'Session "%s" has no registed User' % session_key)
continue
if options['delete']:
self.stdout.write('deleting %s' % user.username)
user.delete()
elif options['ban']:
self.stdout.write('banning %s' % user.username)
user.is_active = False
user.save()
else:
self.stdout.write("Username: %s" % user.username)

281
src/membership/models.py Normal file
View File

@@ -0,0 +1,281 @@
# -*- encoding: utf-8 -*-
from datetime import timedelta
from os import path
import random
import hashlib
from django.utils import timezone
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.translation import ugettext as _
from imagekit.models import ImageSpecField
from imagekit.processors import SmartResize
from . import PAID_MEMBERSHIP_GROUP
from utils import OverwriteStorage
GENDER_CHOICES = (
('m', _('Male')),
('f', _('Female')),
)
def get_upload_path(instance, filename):
"""
Erstellt den Pfad und Dateinamen für den Upload dynmisch.
@param instance: The Membership Object for the uploaded image
@param filename: the filename of the uploaded image
"""
extension = path.splitext(filename)[1]
return 'membership/%s%s' % (instance.user.username, extension)
class ActivationManager(models.Manager):
"""
Manages pending user registrations
"""
def activate(self, activation_key):
"""
searches the pending registrations for the given activation key.
Set the corresponding user to active, if the key was found
and the key has not expired yet.s
@param activation_key: the key found in the activation email
"""
try:
activation_request = self.get(activation_key=activation_key)
if activation_request.expired():
activation_request.user.delete()
activation_request.delete()
return False
elif not activation_request.user.is_active:
activation_request.user.is_active = True
activation_request.user.save()
activation_request.delete()
return activation_request.user
except self.model.DoesNotExist:
return False
def create_pending_registration(self, user):
"""
creates a PendingActivation instance with an random activation key.
@param user: the user that requests activation.
"""
salt = str(random.random())
activation_key = hashlib.sha1(salt + user.username).hexdigest()
return self.create(user=user, activation_key=activation_key)
def expired(self):
return self.filter(
user__is_active=False,
user__date_joined__lt=self.expiration_date
)
class ActivationRequest(models.Model):
"""
Each ActivationRequest contains an activation key and an user.
The key will be send by email to the user
if the user clicks on the link he can activate his in_active account.
"""
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
verbose_name=_('user')
)
activation_key = models.CharField(_('activation key'), max_length=40)
objects = ActivationManager()
class Meta:
verbose_name = _('pending activation')
verbose_name_plural = _('pending activations')
def __unicode__(self):
return _("user registration for %s") % self.user
def activate(self):
self.user.is_active = True
self.user.save()
self.delete()
@property
def expiration_date(self):
timespan = timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS)
return self.user.date_joined + timespan
@property
def email(self):
return self.user.email
def expired(self):
if self.user.is_active:
return False
elif timezone.now() >= self.expiration_date:
return True
else:
return False
expired.boolean = True
@property
def first_name(self):
return self.user.first_name
@property
def last_name(self):
return self.user.last_name
@property
def registration_date(self):
return self.user.date_joined
@property
def username(self):
return self.user.username
class MembershipManager(models.Manager):
def get(self, user=None, username=None, **kwargs):
"""
First try's to fetch the requested Membership Object from the Database,
if the requestetd Membership does not Exists (yet) try to fetch the
corresponding User, and create the Membership with the filled in
Userdata.
"""
try:
if username and not user:
user = get_user_model().objects.get(username=username)
return super(MembershipManager, self).get(user=user)
except Membership.DoesNotExist:
membership = Membership(
user=user,
nickname=user.username,
first_name=user.first_name,
last_name=user.last_name,
email=user.email
)
membership.save()
return membership
class Membership(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
nickname = models.SlugField(_('Nickname'), unique=True)
gender = models.CharField(
_("Gender"),
max_length=1,
choices=GENDER_CHOICES
)
first_name = models.CharField(_("Given Name"), max_length=30)
last_name = models.CharField(_("Last Name"), max_length=30)
email = models.EmailField(_('Email'), unique=True)
website = models.URLField(blank=True)
avatar = models.ImageField(
upload_to=get_upload_path,
storage=OverwriteStorage(),
blank=True,
null=True
)
membership = models.BooleanField(
_('Membership'),
default=False,
help_text=_('Yes, I confirm that I am in agreement with the statutes \
and would like to become a member.')
)
birthday = models.DateField(_("Birthday Date"), blank=True, null=True)
telephone = models.CharField(
_("Telephone"),
max_length=30,
blank=True,
null=True
)
street_name = models.CharField(
_("Address"),
max_length=75,
blank=True,
null=True
)
post_code = models.PositiveSmallIntegerField(
_("Postcode"),
blank=True,
null=True
)
city = models.CharField(
_("Town/City"),
max_length=75,
blank=True,
null=True
)
deposit = models.PositiveSmallIntegerField(default=0, editable=False)
registration_date = models.DateField(auto_now_add=True, editable=False)
paid_until = models.DateField(
_('Paid until'),
blank=True,
null=True,
editable=True
)
confirmed = models.BooleanField(
_('Confirmed'),
default=False,
help_text=_('This person has paid the membership fee.')
)
comment = models.TextField(blank=True)
objects = MembershipManager()
thumbnail = ImageSpecField(
processors=[SmartResize(width=60, height=60)],
format='PNG',
source='avatar',
)
profile = ImageSpecField(
processors=[SmartResize(width=140, height=140)],
format='PNG',
source='avatar',
)
class Meta(object):
ordering = ('last_name', 'first_name',)
verbose_name = _('Membership')
verbose_name_plural = _('Memberships')
def __unicode__(self):
return _('Userprofile for %s' % self.user.username)
def clean(self):
# Update the Profile Info from the User Object
if not self.nickname:
self.nickname = self.user.username
if not self.first_name:
self.first_name = self.user.first_name
if not self.last_name:
self.last_name = self.user.last_name
if not self.email:
self.email = self.user.email
def get_absolute_url(self):
return reverse(
'membership-details',
kwargs={'username': self.user.username}
)
def save(self, *args, **kwargs):
super(Membership, self).save(*args, **kwargs)
self.user.username = self.nickname
self.user.first_name = self.first_name
self.user.last_name = self.last_name
self.user.email = self.email
if self.confirmed:
self.user.groups.add(PAID_MEMBERSHIP_GROUP)
else:
self.user.groups.remove(PAID_MEMBERSHIP_GROUP)
self.user.save()

24
src/membership/specs.py Normal file
View File

@@ -0,0 +1,24 @@
from imagekit.specs import ImageSpec
from imagekit import processors
class ResizeThumbnail(processors.Resize):
width = 60
height = 60
crop = True
class ResizeProfile(processors.Resize):
width = 140
height = 140
crop = True
class Thumbnail(ImageSpec):
pre_cache = True
processors = (ResizeThumbnail,)
class Profile(ImageSpec):
pre_cache = False
processors = (ResizeProfile,)

View File

@@ -0,0 +1,18 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}Account aktivierung fehlgeschlagen{% endblock %}
{% block teaser %}<h1>Account aktivierung fehlgeschlagen</h1>{% endblock %}
{% block maincontent %}
<h2 class="grid_12">Aktivierung fehlgeschlagen</h2>
<p class="grid_12">Leider war die Aktivierung des Schlüssels &ldquo;{{ activation_key }}&rdquo; war nicht erfolgreich, dies könnte mehrere Gründe haben:</p>
<img class="grid_4" src="" alt="" />
<ul class="grid_8">
<li><strong>Der Aktivierungs Link ist nicht korrekt.</strong> Bitte kopiere die ganze Zeile aus der E-Mail in die Adressleiste.</li>
<li><strong>Der Aktivierungs Schlüssel ist abgelaufen.</strong> Dieser ist nur {{ expiration_days }} Tage gültig.In diesem Fall fülle das <a href="{% url 'membership-register' %}">Registrierungs Formular</a> bitte erneut aus.</p></li>
<li><strong>Der Account wurde schon aktiviert.</strong> Dies passiert sehr schnell, veruche zuerst dich mit deinen Daten <a href="{% url 'login' %}">anzumelden</a>. Sollte das nicht funktionieren, kannst du dir ja mal <a href="{% url 'password_reset' %}">neue Zugangsdaten zuschicken</a> lassen.</li>
<li><strong>Ein obskurer Fehler ist aufgetreten.</strong> In diesem Fall, nimm bitte Kontakt mit dem Webadmin auf.</li>
</ul>
{% endblock %}

View File

@@ -0,0 +1,26 @@
{% extends "base.html" %}
{% block title %}Bestätigungs E-Mail wurde abgeschickt{% endblock %}
{% block teaser %}<h1>Bestätigungs E-Mail wurde abgeschickt</h1>{% endblock %}
{% block content %}
<div class="left_column">
<h2>Bestätigungs E-Mail wurde abgeschickt</h2>
<p>Danke für die Anmeldung! Um sicher zu gehen das die angegebene E-Mail Adresse korrekt ist,
wurde eine <em>Aktivierungs E-Mail</em> an deinen Adresse geschickt.Diese enthält einen Link, der deinen Benutzer Account frei schaltet.</p>
<p>Solltest du <em>keine</em> Bestätigung bekommen haben, oder deine Mail Adresse <em>falsch</em> sein.
Setze dich bitte mit den Webadministrator in Kontakt. Dieser kann bei deinem Anliegen helfen.</p>
<p class="more"><a href="mailto:webmaster@animanga.at">E-Mail an den Webmaster</a></p>
</div>
<div class="right_column">
<h2>E-Mail nicht angekommen?</h2>
<ul>
<li><p>Einige Anti-Spam Techniken verzögern den Empfang von E-Mails.
Bei solchen Postfächern kann der <strong>Empfang</strong> einzelner Mails <strong>bis zu 30 Minuten</strong> dauern.</p></li>
<li><p>Bitte überprüfe den <em>"Junk"</em> bzw. <em>"SPAM"</em> Ordner deines Postfaches.
Automatisch generierte E-Mails werden leider sehr oft falsch von SPAM-Filtern geschluckt</p></li>
<li><p>Bitte Überprüfe die Richigkeit deiner angegebenen <strong>E-Mail Adresse</strong>.</p></li>
</ul>
</div>
{% endblock %}

View File

@@ -0,0 +1,14 @@
{% extends "base.html" %}
{% block title %}Aktivierung erfolgreich{% endblock %}
{% block teaser %}<h1>Aktivierung erfolgreich</h1>{% endblock %}
{% block maincontent %}
<h2 class="grid_12">Aktivierung erfolgreich</h2>
<p class="grid_7">Du kannst dich absofort jeder Zeit
<a href="/accounts/login/">anmelden</a> und deinen Acount zu Nutzen.<br/>
Viel Spaß auf der Seite!</p>
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends "email.html" %}
{% block body %}
<h2>Herzlich Willkommen {{user}}!</h2>
<p>Jemand (hoffentlich du selbst) möchte mit dieser E-Mail Adresse einen neuen
Benutzer Account für {{site}} anlegen.</p>
<p>Solltest du diesen Account aktivieren wollen, klicke bitte auf den unten
stehenden Link, oder kopiere diesen in die Adresszeile deines Browsers:</p>
<ul>
<li><a href="http://{{site.domain}}/accounts/activate/{{ activation_key }}">http://{{site.domain}}/accounts/activate/{{ activation_key }}</a></li>
</ul>
<p>Solltest du nichts beantragt haben, ignoriere diese E-Mail einfach.
Du wirst keine weiteren Mails mehr von uns bekommen.</p>
<p><strong>Wichtig:</strong> Der Account muss innerhalb von <em>{{expiration_days}}</em> Tagen aktiviert werden,
ansonsten verfällt er automatisch.</p>
<p>mit freundlichen Grüßen</p>
<p>Das AniManga Team.</p>
{% endblock %}

View File

@@ -0,0 +1,13 @@
{% load i18n %}
Dear {{user}},
We received an account request on {{site.domain}} for your email address.
To activate your account please visit the following link:
http://{{site.domain}}/membership/activate/{{ activation_key }}/
If you do not want to open an account on {{site.domain}}, please ignore this email.
Your information will then be deleted in a few days time.
Best Regards,
The {{site.domain}} team.

View File

@@ -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 %}

View File

@@ -0,0 +1,3 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}Password reset on {{ site_name }}{% endblocktrans %}
{% endautoescape %}

View File

@@ -0,0 +1,32 @@
{% load i18n %}
<table>
<thead><tr>
<th>{% trans 'Start' %}</th>
<th>{% trans 'Event' %}</th>
<th colspan="4">{% trans 'Players' %}</th>
<th>{% trans 'Kyu Points' %}</th>
<th>{% trans 'Dan Points' %}</th>
<th>{% trans 'Bonus Points' %}</th>
<th>{% trans 'Comment' %}</th>
</tr>
</thead>
{% for result in hanchan_list %}
<tr>
<td>{{ result.hanchan.start }}</td>
<td><a href="{{ result.hanchan.event.get_absolute_url }}">{{ result.hanchan.event.name }}</a></td>
{% if result.hanchan.valid %}
{% for player in result.hanchan.player_set.all %}
<td class="center"><a href="{% url 'membership-details' player.user.username %}">{{player.user}}</a><br/> {{player.score }}</td>
{% endfor %}
<td class="center">{{result.kyu_points}}</td>
<td class="center">{{result.dan_points}}</td>
{% else %}
<td colspan="6" class="center">{% trans 'This Hanchan does not validate' %}</td>
{% endif %}
<td class="center">{{result.bonus_points}}</td>
<td>{{ result.hanchan.comment }}</td>
</tr>
{% endfor %}
</table>

View File

@@ -0,0 +1,87 @@
{% extends "base.html" %}
{% load i18n comments %}
{% block title %}{{ membership.first_name }} {{membership.last_name}}{% endblock %}
{% block teaser %}<h1>{% trans 'profile for' %} {{membership.user.username|title}}</h1>{% endblock %}
{% block navigation %}
<ul id="navigation">
<li><a href="{% url 'player-ladder-score' membership.user.username %}">{% trans "Ladder Hanchans" %}</a></li>
<li><a href="{% url 'player-kyu-score' membership.user.username %}">{% trans "Kyu Hanchans" %}</a></li>
<li><a href="{% url 'player-dan-score' membership.user.username %}">{% trans "Dan Hanchans" %}</a></li>
<li><a href="{% url 'player-invalid-score' membership.user.username %}">{% trans "Invalid Hanchans" %}</a></li>
<li><a href="{% url 'maistar-player-games' membership.user.username %}">{% trans "Mai-Star Games" %}</a></li>
</ul>
{% endblock %}
{% block maincontent %}
{% if membership.profile %}
<img class="grid_3" src="{{membership.profile.url}}" alt="{% trans 'Profile Image' %}"/>
{% else %}
<div class="grid_3"> Noch kein Foto hoch geladen</div>
{% endif %}
<div class="grid_6">
<ul>
<li><strong>Name:</strong> {{membership.first_name}} {{membership.last_name}}</li>
<li><strong>{% trans "Member Since" %}:</strong> {{membership.user.date_joined}}</li>
<li><strong>{% trans "Last Login" %}:</strong> {{membership.user.last_login}}</li>
{% if website %}
<li><strong>Homepage:</strong> <a href="{{website}}">{{website}}</a></li>
{% endif %}
</ul>
{% if kyu_dan_ranking %}
<h3>Mahjong</h3>
<ul>
{% if kyu_dan_ranking.dan %}
<li><strong>{{kyu_dan_ranking.dan}}. Dan: </strong> {{ kyu_dan_ranking.dan_points }} {% trans 'Points' %}</li>
{% elif kyu_dan_ranking.kyu%}
<li><strong>{{kyu_dan_ranking.kyu}}. Kyu: </strong> {{ kyu_dan_ranking.kyu_points }} {% trans 'Points' %}</li>
{% endif %}
<li><strong>{% trans 'Games Total' %}: </strong> {{ kyu_dan_ranking.hanchan_count }} Hanchans -
{{kyu_dan_ranking.won_hanchans }} {% trans 'Won' %} / {{ kyu_dan_ranking.good_hanchans }} {% trans 'Good' %}
</li>
<li><strong>{% trans 'Current Season' %}: </strong> {{ ladder_ranking.hanchan_count }} Hanchans - {{ ladder_ranking.won_hanchans }} {% trans 'Won' %} / {{ ladder_ranking.good_hanchans }} {% trans 'Good' %}
</li>
</ul>
{% endif %}
</div>
{% ifequal membership.user user %}
<div class="grid_3">
<a href="{% url 'membership-edit' membership.user.username %}" class="button">
<img src="{{STATIC_URL}}icons/user_edit.png" alt="{% trans 'Edit Userprofile' %}" width="16" height="16"/>
{% trans "Edit Profile" %}
</a>
<a href="{% url 'password_change' %}" class="button">
<img src="{{STATIC_URL}}icons/lock_edit.png" alt="{% trans 'Change Password' %}" width="16" height="16"/>
{% trans 'Change Password' %}
</a>
<a class="button" href="{% url 'social:begin' 'facebook' %}" rel="nofollow">
<img src="{{STATIC_URL}}img/facebook.png" alt="{% trans 'Associate' %}" width="26" height="26"/>
{% blocktrans with 'Facebook' as name %}Associate with {{ name }}{% endblocktrans %}
</a>
<a class="button" href="{% url 'social:begin' 'twitter' %}" rel="nofollow">
<img src="{{STATIC_URL}}img/twitter.png" alt="{% trans 'Associate' %}" width="26" height="26"/>
{% blocktrans with 'Twitter' as name %}Associate with {{ name }}{% endblocktrans %}
</a>
<a class="button" href="{% url 'social:begin' 'google-oauth2' %}" rel="nofollow">
<img src="{{STATIC_URL}}img/google.png" alt="{% trans 'Associate' %}" width="26" height="26"/>
{% blocktrans with 'Google' as name %}Associate with {{ name }}{% endblocktrans %}
</a>
</div>
{% endifequal %}
{% block score_list %} {% endblock %}
{% endblock %}
{% block comments %}
{% if membership %}
{% render_comment_list for membership %}
{% render_comment_form for membership %}
{% endif %}
{% endblock %}
{% block buttonbar%}{%endblock%}

View File

@@ -0,0 +1,45 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans "Edit Userprofile" %}{% endblock %}
{% block teaser %}<h1>{% trans "Edit Userprofile" %}</h1>{% endblock %}
{% block extra_head %}
<link rel="stylesheet" href="{{STATIC_URL}}css/jquery-ui-1.8.16.custom.css" type="text/css">
<script type="text/javascript" src="{{STATIC_URL}}js/jquery-ui-1.8.16.custom.min.js"></script>
{% endblock %}
{% block maincontent %}
<form method="post" action="" enctype="multipart/form-data" class="grid_12 clearfix">
<fieldset>
<legend>{% trans "Edit Userprofile" %}</legend>
{% csrf_token %}
{% include 'form.html' %}
<p class="buttonbar">
<button type="reset">
<img src="{{STATIC_URL}}icons/arrow_undo.png" alt="{% trans 'reset' %}" />
{% trans "Reset" %}
</button>
<button type="submit">
<img src="{{STATIC_URL}}icons/disk.png" alt="{% trans 'save' %}" />
{% trans "Save" %}
</button>
</p>
</fieldset>
</form>
{% endblock %}
{% block javascript %}
$(function() {
$( "#id_birthday" ).datepicker({
changeMonth: true,
changeYear: true,
dateFormat: 'yy-mm-dd',
yearRange: '-50,0',
firstDay: 1
});
});
{% endblock %}
{% block buttonbar %}{% endblock %}

View File

@@ -0,0 +1,39 @@
{% extends "base.html" %}
{% load i18n fieldset_extras %}
{% load url from future %}
{% block title %}{% trans "Registration"%}{% endblock %}
{% block teaser%}<h1>{% trans "Registration"%}</h1>{% endblock %}
{% block maincontent %}
<form method="post" action="{% url 'membership-register' %}">
{% csrf_token %}
<fieldset class="grid_5">
<legend>{% trans "Name"%}</legend>
{% get_fieldset "first_name, last_name, username" from form as form1 %}
{% with form1 as form %}{% include "form.html" %}{% endwith %}
</fieldset>
<fieldset class="grid_7">
<legend>{% trans "Security"%}</legend>
{% get_fieldset "email, password1, password2, recaptcha" from form as form2 %}
{% with form2 as form %}{% include "form.html" %}{% endwith %}
</fieldset>
<div class="grid_12">
<p>{% blocktrans %}After you've provided your account data, you'll
receive an email asking you to verify your email address.
You have to click on the link in this verification email to
confirm your email address, otherwise your can't login.
{% endblocktrans %}</p>
<p class="buttonbar">
<button type="reset"><img src="{{STATIC_URL}}icons/arrow_undo.png" alt="{% trans 'reset' %}" /> {% trans 'reset' %}</button>
<button type="submit"><img src="{{STATIC_URL}}icons/user_add.png" alt="{% trans 'register' %}" /> {% trans 'register' %}</button>
</p>
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,16 @@
{% extends "base.html" %}
{% load i18n %}
{% load url from future %}
{% block title %}{% trans "Activation sent"%}{% endblock %}
{% block teaser %}<h1>{% trans "Activation sent"%}</h1>{% endblock %}
{% block content %}
<h2>{% trans "Activation sent"%}</h2>
<p>Bitte fülle das Anmeldeformular vollständig aus.
Es ist dabei wichtig eine gültige E-Mail Adresse anzugeben, welche du öfters abrufst.</p>
<p>Während der Registrierung schicken wir dir eine E-Mail, diese enthält einen Link zur Aktivierung.
Ohne diesen Schritt ist es nicht Möglich sich mit dem Account anzumelden.</p>
{% endblock %}

30
src/membership/urls.py Normal file
View File

@@ -0,0 +1,30 @@
"""
Created on 03.10.2011
@author: christian
"""
from django.conf.urls import * # @UnusedWildImport
from . import views
urlpatterns = patterns(
'',
url('', include('social.apps.django_app.urls', namespace='social')),
url('', include('django.contrib.auth.urls')),
url(r'^register/$',
views.RegisterForm.as_view(),
name="membership-register"),
url(r'^activate/(?P<activation_key>[\d\w]+)/$',
views.ActivateRegistration.as_view(),
name='membership-activate-registration'),
url(r'^activation_sent/$',
views.ActivationSent.as_view(),
name="membership-registration-complete"),
url(r'^(?P<username>[\-\.\d\w]+)/$',
views.MembershipDetail.as_view(),
name='membership-details'),
url(r'^(?P<username>[\-\.\d\w]+)/edit/$',
views.EditMembership.as_view(),
name="membership-edit")
)

114
src/membership/views.py Normal file
View File

@@ -0,0 +1,114 @@
from django import http
from django.conf import settings
from django.contrib import auth, messages
from django.core.urlresolvers import reverse
from django.http import Http404
from django.utils.translation import ugettext as _
from django.views import generic
from mahjong_ranking.models import KyuDanRanking, LadderRanking, LadderSeason
from utils import mixins
from . import forms, models
class ActivateRegistration(generic.DetailView):
"""
Activates the Registration of an User and logs him in
"""
template_name = 'membership/activation_error.html'
def get(self, request, **kwargs):
activation_key = self.kwargs.get('activation_key')
user = self.get_user(activation_key)
if user:
return self.login(user)
else:
self.object = user
context = self.get_context_data()
return self.render_to_response(context)
def get_context_data(self, **kwargs):
context = generic.DetailView.get_context_data(self, **kwargs)
context.update({
'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
'activation_key': self.kwargs.get('activation_key'),
})
return context
def get_user(self, activation_key):
user = models.ActivationRequest.objects.activate(activation_key)
if user:
return user
elif self.request.user.is_authenticated():
return self.request.user
else:
return None
def login(self, user):
backend = auth.get_backends()[0]
user.backend = "%s.%s" % (
backend.__module__, backend.__class__.__name__) # @IgnorePep8
auth.login(self.request, user)
messages.success(self.request, _('Activation successful. \
You can now login anytime with you username and password.'))
redirect_url = reverse('membership-edit', args=(user.username,))
return http.HttpResponseRedirect(redirect_url) # @IgnorePep8
class ActivationSent(generic.TemplateView):
template_name = 'membership/activation_sent.html'
class EditMembership(mixins.LoginRequiredMixin, generic.UpdateView):
form_class = forms.MembershipForm
def get_object(self, queryset=None):
if self.request.user.has_perm('membership.change_membership'):
return models.Membership.objects.get(
username=self.kwargs['username'])
else:
return models.Membership.objects.get(user=self.request.user)
def form_valid(self, form):
messages.success(self.request, _('User Profile changed successfully'))
return generic.UpdateView.form_valid(self, form)
class MembershipDetail(mixins.LoginRequiredMixin, generic.DetailView):
model = models.Membership
def get_object(self, queryset=None):
try:
if self.kwargs.get('username'):
return models.Membership.objects.get(username=self.kwargs['username'])
elif self.request.user.is_authenticated():
return models.Membership.objects.get(user=self.request.user)
except models.Membership.DoesNotExist:
raise Http404(_("No %(verbose_name)s found matching the query") %
{'verbose_name': self.model._meta.verbose_name})
def get_context_data(self, **kwargs):
context = generic.DetailView.get_context_data(self, **kwargs)
try:
context['kyu_dan_ranking'] = KyuDanRanking.objects.get(
user_id=self.object.user_id)
except KyuDanRanking.DoesNotExist:
context['kyu_dan_ranking'] = None
try:
context['ladder_ranking'] = LadderRanking.objects.get(
user_id=self.object.user_id,
season=LadderSeason.objects.current())
except LadderRanking.DoesNotExist:
context['ladder_ranking'] = LadderRanking(user=self.object.user)
return context
class RegisterForm(generic.FormView):
form_class = forms.RegistrationForm
success_url = '/membership/activation_sent/'
template_name = 'membership/register_form.html'
def form_valid(self, form):
form.save()
return generic.FormView.form_valid(self, form)