neue Verzeichnissstruktur
This commit is contained in:
1
membership/__init__.py
Normal file
1
membership/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
PAID_MEMBERSHIP_GROUP = 2
|
||||
62
membership/admin.py
Normal file
62
membership/admin.py
Normal file
@@ -0,0 +1,62 @@
|
||||
'''
|
||||
Created on 19.09.2011
|
||||
|
||||
@author: christian
|
||||
'''
|
||||
# import stuff we need from django
|
||||
from django.contrib import admin
|
||||
from membership.models import Membership, ActivationRequest
|
||||
from django.utils.translation import ugettext as _
|
||||
from imagekit.admin import AdminThumbnail
|
||||
|
||||
|
||||
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)
|
||||
146
membership/forms.py
Normal file
146
membership/forms.py
Normal file
@@ -0,0 +1,146 @@
|
||||
'''
|
||||
Created on 03.10.2011
|
||||
|
||||
@author: Christian
|
||||
'''
|
||||
from . import models
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.sites.models import Site
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
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 = User
|
||||
fields = ('first_name', 'last_name', 'username', 'email',)
|
||||
|
||||
def clean_username(self):
|
||||
'''
|
||||
Validate that the username is not already in use.
|
||||
'''
|
||||
try:
|
||||
User.objects.get(username__iexact=self.cleaned_data['username'])
|
||||
except User.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 User.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)
|
||||
self.send_email(activation)
|
||||
return user
|
||||
1
membership/management/__init__.py
Normal file
1
membership/management/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#!/usr/bin/python
|
||||
1
membership/management/commands/__init__.py
Normal file
1
membership/management/commands/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#!/usr/bin/python
|
||||
15
membership/management/commands/cleanup-registration.py
Normal file
15
membership/management/commands/cleanup-registration.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
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()
|
||||
|
||||
55
membership/management/commands/get-user.py
Normal file
55
membership/management/commands/get-user.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
When you get an error email from your Django app telling you someone got a
|
||||
server error,
|
||||
it’s 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 django.contrib.auth.models import User
|
||||
from django.contrib.sessions.models import Session
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from optparse import make_option
|
||||
|
||||
|
||||
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 = User.objects.get(pk=uid)
|
||||
except Session.DoesNotExist:
|
||||
self.stderr.write('Session "%s" does not exist' % session_key)
|
||||
continue
|
||||
except User.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)
|
||||
277
membership/models.py
Normal file
277
membership/models.py
Normal file
@@ -0,0 +1,277 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from . import PAID_MEMBERSHIP_GROUP
|
||||
from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import FieldError
|
||||
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 os import path
|
||||
from utils import OverwriteStorage
|
||||
import random
|
||||
import hashlib
|
||||
|
||||
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.ForeignKey(User, unique=True, 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, *args, **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' in kwargs:
|
||||
return models.Manager.get(self,
|
||||
user__username=kwargs['username']
|
||||
)
|
||||
else:
|
||||
return models.Manager.get(self, *args, **kwargs)
|
||||
except FieldError:
|
||||
user = User.objects.get(*args, **kwargs)
|
||||
except Membership.DoesNotExist:
|
||||
if 'user' in kwargs:
|
||||
user = User.objects.get(pk=kwargs['user'].id)
|
||||
else:
|
||||
user = User.objects.get(*args, **kwargs)
|
||||
|
||||
membership = Membership(
|
||||
user=user,
|
||||
nickname=user.username,
|
||||
first_name=user.first_name,
|
||||
last_name=user.last_name,
|
||||
email=user.email
|
||||
)
|
||||
return membership
|
||||
|
||||
|
||||
class Membership(models.Model):
|
||||
user = models.OneToOneField(User)
|
||||
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()
|
||||
21
membership/specs.py
Normal file
21
membership/specs.py
Normal file
@@ -0,0 +1,21 @@
|
||||
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,)
|
||||
16
membership/templates/membership/activation_error.html
Normal file
16
membership/templates/membership/activation_error.html
Normal file
@@ -0,0 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Account aktivierung fehlgeschlagen{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
<h2 class="grid_12">Aktivierung fehlgeschlagen</h2>
|
||||
<p class="grid_12">Leider war die Aktivierung des Schlüssels “{{ activation_key }}” 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 %}
|
||||
24
membership/templates/membership/activation_sent.html
Normal file
24
membership/templates/membership/activation_sent.html
Normal file
@@ -0,0 +1,24 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Bestätigungs E-Mail wurde abgeschickt{% 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 %}
|
||||
11
membership/templates/membership/activation_successful.html
Normal file
11
membership/templates/membership/activation_successful.html
Normal file
@@ -0,0 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Aktivierung erfolgreich{% 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 %}
|
||||
24
membership/templates/membership/email/activation_email.html
Normal file
24
membership/templates/membership/email/activation_email.html
Normal 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 %}
|
||||
13
membership/templates/membership/email/activation_email.txt
Normal file
13
membership/templates/membership/email/activation_email.txt
Normal 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.
|
||||
@@ -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 %}
|
||||
@@ -0,0 +1,3 @@
|
||||
{% load i18n %}{% autoescape off %}
|
||||
{% blocktrans %}Password reset on {{ site_name }}{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
32
membership/templates/membership/hanchan_table.html
Normal file
32
membership/templates/membership/hanchan_table.html
Normal 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>
|
||||
78
membership/templates/membership/membership_detail.html
Normal file
78
membership/templates/membership/membership_detail.html
Normal file
@@ -0,0 +1,78 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n comments %}
|
||||
|
||||
{% block title %}{{ membership.first_name }} {{membership.last_name}}{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
<h2 class="grid_12">{% trans 'profile for' %} {{membership.user.username|title}}</h2>
|
||||
{% 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 %}
|
||||
{% if kyu_dan_ranking %}
|
||||
<ul class="tabs grid_12">
|
||||
<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-ladder-score' membership.user.username %}">{% trans "Invalid Hanchans" %}</a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% if membership %}
|
||||
{% render_comment_list for membership %}
|
||||
{% render_comment_form for membership %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block buttonbar%}{%endblock%}
|
||||
41
membership/templates/membership/membership_form.html
Normal file
41
membership/templates/membership/membership_form.html
Normal file
@@ -0,0 +1,41 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Edit Userprofile" %}{% 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 %}
|
||||
31
membership/templates/membership/register_form.html
Normal file
31
membership/templates/membership/register_form.html
Normal file
@@ -0,0 +1,31 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block title %}{% trans "Registration"%}{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
<div class="grid_5">
|
||||
<h2>{% trans "Registration" %}</h2>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<form method="post" action="{% url 'membership-register' %}" class="grid_7">
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
<legend>{% trans "Registration"%}</legend>
|
||||
{% 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/user_add.png" alt="{% trans 'register' %}" /> {% trans 'register' %}</button>
|
||||
</p>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
14
membership/templates/membership/register_successful.html
Normal file
14
membership/templates/membership/register_successful.html
Normal file
@@ -0,0 +1,14 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block title %}{% trans "Activation sent"%}{% 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 %}
|
||||
46
membership/urls.py
Normal file
46
membership/urls.py
Normal file
@@ -0,0 +1,46 @@
|
||||
'''
|
||||
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")
|
||||
)
|
||||
"""
|
||||
urlpatterns += patterns('social_auth.views',
|
||||
url(r'^login/(?P<backend>[^/]+)/$', 'auth',
|
||||
name='socialauth_begin'),
|
||||
url(r'^complete/(?P<backend>[^/]+)/$', 'complete',
|
||||
name='socialauth_complete'),
|
||||
url(r'^associate/(?P<backend>[^/]+)/$', 'auth',
|
||||
name='socialauth_associate_begin'),
|
||||
url(r'^associate/complete/(?P<backend>[^/]+)/$', 'complete',
|
||||
name='socialauth_associate_complete'),
|
||||
url(r'^disconnect/(?P<backend>[^/]+)/$', 'disconnect',
|
||||
name='socialauth_disconnect'),
|
||||
url(r'^disconnect/(?P<backend>[^/]+)/(?P<association_id>[^/]+)/$',
|
||||
'disconnect',
|
||||
name='socialauth_disconnect_individual'),
|
||||
)
|
||||
"""
|
||||
109
membership/views.py
Normal file
109
membership/views.py
Normal file
@@ -0,0 +1,109 @@
|
||||
from django import http
|
||||
from django.conf import settings
|
||||
from django.contrib import auth, messages
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views import generic
|
||||
from mahjong_ranking.models import KyuDanRanking, LadderRanking, LadderSeason
|
||||
import forms
|
||||
import models
|
||||
from utils import mixins
|
||||
|
||||
|
||||
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)
|
||||
print 'User: ', user
|
||||
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):
|
||||
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)
|
||||
|
||||
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:
|
||||
context['kyu_dan_ranking'] = None
|
||||
try:
|
||||
context['ladder_ranking'] = LadderRanking.objects.get(
|
||||
user_id=self.object.user_id,
|
||||
season=LadderSeason.objects.current())
|
||||
except:
|
||||
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)
|
||||
Reference in New Issue
Block a user