Paginator der besser ins Design passt.
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
*.pyc
|
||||
htdocs/media/
|
||||
htdocs/
|
||||
static/
|
||||
media/
|
||||
.idea
|
||||
.gitignore
|
||||
|
||||
4
.idea/encodings.xml
generated
4
.idea/encodings.xml
generated
@@ -1,4 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
||||
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false">
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,6 +1,8 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from models import Feed, FeedItem
|
||||
|
||||
|
||||
admin.site.register(
|
||||
Feed,
|
||||
list_display=["title", "public_url", "last_update", 'is_functional'],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import django.contrib.syndication.views
|
||||
|
||||
from .models import FeedItem
|
||||
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@ Update feeds for Django community page. Requires Mark Pilgrim's excellent
|
||||
Universal Feed Parser (http://feedparser.org)
|
||||
"""
|
||||
|
||||
from aggregator.models import Feed
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from aggregator.models import Feed
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Updates all RSS Feeds"
|
||||
|
||||
@@ -5,14 +5,16 @@ Created on 05.02.2011
|
||||
"""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
import HTMLParser
|
||||
import urllib2
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
from django.utils import timezone
|
||||
from utils.html5 import models
|
||||
import HTMLParser
|
||||
import django.db.models
|
||||
import feedparser
|
||||
import urllib2
|
||||
|
||||
from utils.html5 import models
|
||||
|
||||
|
||||
class FeedManager(django.db.models.Manager):
|
||||
@@ -35,7 +37,7 @@ class FeedItemManager(django.db.models.Manager):
|
||||
return self.select_related().filter(feed__site=site)[:max_items]
|
||||
|
||||
|
||||
class Feed(models.Model):
|
||||
class Feed(django.db.models.Model):
|
||||
title = models.CharField(max_length=500)
|
||||
site = models.ForeignKey(Site)
|
||||
feed_url = models.URLField(unique=True, max_length=255)
|
||||
@@ -51,7 +53,8 @@ class Feed(models.Model):
|
||||
parsed_feed = feedparser.parse(self.feed_url)
|
||||
html_parser = HTMLParser.HTMLParser()
|
||||
|
||||
if parsed_feed.bozo and type(parsed_feed.bozo_exception) == urllib2.URLError:
|
||||
if parsed_feed.bozo and type(
|
||||
parsed_feed.bozo_exception) == urllib2.URLError:
|
||||
self.is_functional = False
|
||||
return self.save()
|
||||
|
||||
@@ -68,16 +71,22 @@ class Feed(models.Model):
|
||||
feed_entry.get("content", u"")
|
||||
))
|
||||
)
|
||||
date_modified = feed_entry.get("published_parsed", parsed_feed.get("published_parsed", timezone.now))
|
||||
date_modified = timezone.make_aware(datetime(*date_modified[:6]), timezone.get_current_timezone())
|
||||
date_modified = feed_entry.get(
|
||||
"published_parsed",
|
||||
parsed_feed.get("published_parsed",
|
||||
timezone.now))
|
||||
date_modified = timezone.make_aware(
|
||||
datetime(*date_modified[:6]),
|
||||
timezone.get_current_timezone())
|
||||
|
||||
feed_item, updated = self.feed_items.get_or_create(guid=guid,
|
||||
defaults={
|
||||
'title': title,
|
||||
'link': link,
|
||||
'summary': summary,
|
||||
'date_modified': date_modified
|
||||
})
|
||||
feed_item, updated = self.feed_items.get_or_create(
|
||||
guid=guid,
|
||||
defaults={
|
||||
'title': title,
|
||||
'link': link,
|
||||
'summary': summary,
|
||||
'date_modified': date_modified
|
||||
})
|
||||
feed_item.save()
|
||||
self.last_update = timezone.now()
|
||||
return self.save()
|
||||
@@ -86,7 +95,7 @@ class Feed(models.Model):
|
||||
ordering = ("title",)
|
||||
|
||||
|
||||
class FeedItem(models.Model):
|
||||
class FeedItem(django.db.models.Model):
|
||||
feed = models.ForeignKey(Feed, related_name='feed_items')
|
||||
title = models.CharField(max_length=500)
|
||||
link = models.URLField(max_length=500)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from django.contrib.sitemaps import Sitemap
|
||||
|
||||
from models import FeedItem
|
||||
|
||||
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
class FeedItemSitemap(Sitemap):
|
||||
changefreq = "never"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from django import template
|
||||
|
||||
from models import Feed
|
||||
|
||||
|
||||
class FeedListNode(template.Node):
|
||||
|
||||
def __init__(self, varname):
|
||||
self.varname = varname
|
||||
|
||||
@@ -26,5 +26,6 @@ def do_get_feed_list(parser, token):
|
||||
"First argument to '%s' tag must be 'as'" % bits[0]
|
||||
return FeedListNode(bits[2])
|
||||
|
||||
|
||||
register = template.Library()
|
||||
register.tag('get_feed_list', do_get_feed_list)
|
||||
|
||||
19
cleanup.sh
19
cleanup.sh
@@ -1,12 +1,23 @@
|
||||
#!/bin/sh
|
||||
echo "aktualisiere Übersetzungen..."
|
||||
cd src
|
||||
./manage.py makemessages -l de
|
||||
./manange.py compilemessages -a
|
||||
unset DJANGO_SETTINGS_MODULE
|
||||
for dir in *
|
||||
do
|
||||
if [ -d $dir/locale ]
|
||||
then
|
||||
echo -n "$dir: "
|
||||
cd $dir
|
||||
django-admin.py makemessages -l de
|
||||
cd ..
|
||||
fi
|
||||
done
|
||||
sleep 5s
|
||||
export DJANGO_SETTINGS_MODULE="kasu.settings.production"
|
||||
./manage.py compilemessages
|
||||
|
||||
echo "lösche den Python Compiler Cache..."
|
||||
find . -name "*.pyc" -exec rm -rf {} \;
|
||||
touch kasu.wsgi
|
||||
|
||||
./manage.py collectstatic --noinput
|
||||
./manage.py generateimages
|
||||
touch kasu/wsgi.py
|
||||
|
||||
@@ -3,11 +3,13 @@ Created on 03.10.2011
|
||||
|
||||
@author: christian
|
||||
"""
|
||||
from . import models
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.contrib.auth import get_user_model
|
||||
from utils.html5.widgets import DateTimeInput
|
||||
|
||||
from . import models
|
||||
from events.models import Event
|
||||
|
||||
|
||||
user_query = get_user_model().objects.all()
|
||||
|
||||
@@ -15,8 +17,8 @@ user_query = get_user_model().objects.all()
|
||||
class PhotoUploadForm(forms.Form):
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
photographer = forms.ModelChoiceField(user_query, required=True,)
|
||||
event = forms.ModelChoiceField(models.Event.objects.all(), required=True,)
|
||||
photographer = forms.ModelChoiceField(user_query, required=True, )
|
||||
event = forms.ModelChoiceField(Event.objects.all(), required=True, )
|
||||
upload = forms.FileField(
|
||||
label=_('Images'),
|
||||
required=True,
|
||||
@@ -38,20 +40,3 @@ class EditPhotoForm(forms.ModelForm):
|
||||
fields = ('event', 'name', 'description', 'photographer',
|
||||
'anchor_horizontal', 'anchor_vertical',
|
||||
'created_date', 'on_startpage')
|
||||
|
||||
|
||||
class EventForm(forms.ModelForm):
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
|
||||
start = forms.DateTimeField(
|
||||
label=_('start'), required=True,
|
||||
widget=DateTimeInput() # widget=SplitDateTimeWidget()
|
||||
)
|
||||
end = forms.DateTimeField(
|
||||
label=_('end'), required=False,
|
||||
widget=DateTimeInput() # widget=SplitDateTimeWidget()
|
||||
)
|
||||
|
||||
class Meta(object):
|
||||
model = models.Event
|
||||
|
||||
@@ -5,29 +5,11 @@ import os
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.template.defaultfilters import slugify
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext as _
|
||||
from imagekit import ImageSpec
|
||||
import imagekit
|
||||
from imagekit.models import ImageSpecField
|
||||
from pilkit import processors
|
||||
import pyexiv2
|
||||
|
||||
from utils import COUNTRIES, OverwriteStorage
|
||||
|
||||
|
||||
CHOICES_HORIZONTAL = (
|
||||
(0.00000001, _('left')),
|
||||
(0.5, _('center')),
|
||||
(1, _('right'))
|
||||
)
|
||||
|
||||
CHOICES_VERTICAL = (
|
||||
(0.00000001, _('top')),
|
||||
(0.5, _('middle')),
|
||||
(1, _('bottom'))
|
||||
)
|
||||
from utils import OverwriteStorage
|
||||
from kasu import image_models
|
||||
|
||||
|
||||
def get_upload_path(instance, filename):
|
||||
@@ -42,231 +24,10 @@ def get_upload_path(instance, filename):
|
||||
@type filename: String
|
||||
"""
|
||||
extension = filename[filename.rfind('.') + 1:]
|
||||
if isinstance(instance, Event):
|
||||
if instance.id:
|
||||
return "events/%s.%s" % (instance.id, extension)
|
||||
else:
|
||||
return "events/%s.%s" % (slugify(instance.name), extension)
|
||||
elif isinstance(instance, Location):
|
||||
if instance.id:
|
||||
return "events/location/%s.%s" % (instance.id, extension)
|
||||
else:
|
||||
return "events/location/%s.%s" % (instance.id, extension)
|
||||
elif isinstance(instance, Photo):
|
||||
if isinstance(instance, Photo):
|
||||
return "events/%s/%s" % (instance.event.id, filename)
|
||||
|
||||
|
||||
def post_save_image(sender, instance=None, created=False, raw=False, **kwargs):
|
||||
"""
|
||||
Reganerate the images.
|
||||
"""
|
||||
os.remove(instance.display.path)
|
||||
os.remove(instance.callout.path)
|
||||
os.remove(instance.thumbnail.path)
|
||||
|
||||
"""
|
||||
instance.callout.generate(force=True)
|
||||
instance.display.generate(force=True)
|
||||
instance.thumbnail.generate(force=True)
|
||||
"""
|
||||
|
||||
|
||||
class CalloutImage(ImageSpec):
|
||||
format = 'PNG'
|
||||
width = 940
|
||||
height = 300
|
||||
|
||||
@property
|
||||
def processors(self):
|
||||
model, field_name = imagekit.utils.get_field_info(self.source) # @UnusedVariable @IgnorePep8
|
||||
anchor = model.get_anchor()
|
||||
if anchor:
|
||||
return [processors.Transpose(), processors.ResizeToFill(
|
||||
width=self.width,
|
||||
height=self.height, anchor=anchor
|
||||
)]
|
||||
else:
|
||||
return [processors.Transpose(), processors.SmartResize(
|
||||
width=self.width,
|
||||
height=self.height
|
||||
)]
|
||||
|
||||
|
||||
class DisplayImage(ImageSpec):
|
||||
format = 'PNG'
|
||||
processors = [processors.Transpose(),
|
||||
processors.ResizeToFit(width=940, height=940, upscale=False)]
|
||||
|
||||
|
||||
class ThumbnailImage(CalloutImage):
|
||||
format = 'PNG'
|
||||
width = 140
|
||||
height = 140
|
||||
|
||||
|
||||
imagekit.register.generator('kasu:image:callout', CalloutImage)
|
||||
imagekit.register.generator('kasu:image:display', DisplayImage)
|
||||
imagekit.register.generator('kasu:image:thumbnail', ThumbnailImage)
|
||||
|
||||
|
||||
class ImageModel(models.Model):
|
||||
callout = ImageSpecField(source='image', id='kasu:image:callout')
|
||||
display = ImageSpecField(source='image', id='kasu:image:display')
|
||||
thumbnail = ImageSpecField(source='image', id='kasu:image:thumbnail')
|
||||
|
||||
def get_anchor(self):
|
||||
try:
|
||||
anchor_horizontal = getattr(self, 'anchor_horizontal')
|
||||
anchor_vertical = getattr(self, 'anchor_vertical')
|
||||
except AttributeError:
|
||||
return None
|
||||
if anchor_horizontal and anchor_vertical:
|
||||
return self.anchor_horizontal, self.anchor_vertical
|
||||
else:
|
||||
return None
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class EventManager(models.Manager):
|
||||
def current_event(self):
|
||||
try:
|
||||
current = self.filter(start__lte=now())
|
||||
current = current.filter(end__gte=now())
|
||||
return current.order_by('start', 'end')[0]
|
||||
except:
|
||||
return None
|
||||
|
||||
def next_event(self):
|
||||
try:
|
||||
return self.filter(start__gt=now()).order_by('start', 'end')[0]
|
||||
except:
|
||||
return None
|
||||
|
||||
def archive(self):
|
||||
return self.filter(start__lt=now())
|
||||
|
||||
def upcoming(self, limit=3):
|
||||
result = self.filter(start__gt=now()).order_by('start', 'end')
|
||||
if limit:
|
||||
return result[1:(limit + 1)]
|
||||
else:
|
||||
return result
|
||||
|
||||
|
||||
class Event(ImageModel):
|
||||
name = models.CharField(_('Name'), max_length=255)
|
||||
description = models.TextField(_("Description"), blank=True)
|
||||
location = models.ForeignKey('Location')
|
||||
start = models.DateTimeField(_('Start'))
|
||||
end = models.DateTimeField(_('End'), blank=True, null=True)
|
||||
url = models.URLField(_('Homepage'), blank=True)
|
||||
image = models.ImageField(_("Image"), upload_to=get_upload_path,
|
||||
storage=OverwriteStorage(), blank=True, null=True)
|
||||
is_tournament = models.BooleanField(_('Tournament'), default=False,
|
||||
help_text=_(u'This event is a tournament, different rules apply for \
|
||||
the kyu ranking.'))
|
||||
photo_count = models.PositiveIntegerField(default=0, editable=False)
|
||||
event_series = models.ForeignKey('Event', blank=True, null=True,
|
||||
on_delete=models.SET_NULL, editable=False,
|
||||
verbose_name=_('Event Series'),
|
||||
help_text=_(u'Wenn dieser Event zu einer Veranstaltungsreihe gehört \
|
||||
werden Ort, Beschreibung, Bild und Homepage von dem hier angegebenen \
|
||||
Event übernommen.'))
|
||||
objects = EventManager()
|
||||
|
||||
class Meta(object):
|
||||
verbose_name = _('Event')
|
||||
verbose_name_plural = _('Events')
|
||||
ordering = ('-start', '-end',)
|
||||
|
||||
def __unicode__(self):
|
||||
try:
|
||||
return "%(name)s (%(date)s)" % {'name': self.name,
|
||||
'date': self.start.date()}
|
||||
except:
|
||||
return "New Event Model"
|
||||
|
||||
def get_absolute_url(self):
|
||||
kwargs = {
|
||||
'pk': self.id,
|
||||
'year': self.start.strftime('%Y'),
|
||||
'month': self.start.strftime('%m')
|
||||
}
|
||||
return reverse('event-detail', kwargs=kwargs)
|
||||
|
||||
def get_edit_url(self):
|
||||
kwargs = {
|
||||
'pk': self.id,
|
||||
'year': self.start.strftime('%Y'),
|
||||
'month': self.start.strftime('%m')
|
||||
}
|
||||
return reverse('event-form', kwargs=kwargs)
|
||||
|
||||
def get_callout(self):
|
||||
if self.image:
|
||||
return self.callout
|
||||
elif self.photo_set.count():
|
||||
return self.photo_set.all().order_by('?')[0].callout
|
||||
elif self.location.image:
|
||||
return self.location.callout
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_thumbnail(self):
|
||||
if self.image:
|
||||
return self.thumbnail
|
||||
elif self.photo_set.count():
|
||||
return self.photo_set.all().order_by('?')[0].thumbnail
|
||||
elif self.location.image:
|
||||
return self.location.thumbnail
|
||||
else:
|
||||
return None
|
||||
|
||||
def save(self, **kwargs):
|
||||
if self.event_series:
|
||||
master_event = self.event_series
|
||||
self.description = master_event.description
|
||||
self.location = master_event.location
|
||||
self.url = master_event.url
|
||||
self.image = master_event.image
|
||||
self.photo_count = self.photo_set.count()
|
||||
models.Model.save(self, **kwargs)
|
||||
|
||||
# Update the rest of the event series:
|
||||
for sub_event in Event.objects.filter(event_series=self):
|
||||
sub_event.save()
|
||||
|
||||
# Update the Hanchans if necesery:
|
||||
for hanchan in self.hanchan_set.all():
|
||||
hanchan.save()
|
||||
|
||||
|
||||
class Location(ImageModel):
|
||||
name = models.CharField(_("Name"), max_length=200)
|
||||
description = models.TextField(_("Description"), blank=True)
|
||||
image = models.ImageField(_("Image"), upload_to=get_upload_path,
|
||||
storage=OverwriteStorage(), blank=True, null=True)
|
||||
url = models.URLField(_('Homepage'), blank=True)
|
||||
postal_code = models.CharField(_('Postal Code'), max_length=6)
|
||||
street_address = models.CharField(_('Street Address'), max_length=127)
|
||||
locality = models.CharField(_('Locality'), max_length=127)
|
||||
country = models.CharField(_('Country'), max_length=2, choices=COUNTRIES)
|
||||
|
||||
class Meta(object):
|
||||
verbose_name = _('Venue')
|
||||
verbose_name_plural = _('Venues')
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
address = (self.street_address, self.locality, self.country,)
|
||||
return ','.join(address)
|
||||
|
||||
|
||||
class PhotoManager(models.Manager):
|
||||
def get_random(self, startpage=True):
|
||||
if startpage:
|
||||
@@ -279,26 +40,26 @@ class PhotoManager(models.Manager):
|
||||
return Photo()
|
||||
|
||||
|
||||
class Photo(ImageModel):
|
||||
class Photo(image_models.ImageModel):
|
||||
name = models.CharField(_("Name"), max_length=100, blank=True)
|
||||
image = models.ImageField(_("Image"), upload_to=get_upload_path,
|
||||
storage=OverwriteStorage())
|
||||
anchor_horizontal = models.FloatField(
|
||||
_('horizontal Anchorpoint'),
|
||||
choices=CHOICES_HORIZONTAL,
|
||||
choices=image_models.CHOICES_HORIZONTAL,
|
||||
blank=True, null=True,
|
||||
help_text='Der Ankerpunkt ist der interessante Teil des Bildes,\
|
||||
welcher nie abgeschnitten werden darf'
|
||||
)
|
||||
anchor_vertical = models.FloatField(
|
||||
_('vertical Anchorpoint'),
|
||||
choices=CHOICES_VERTICAL,
|
||||
choices=image_models.CHOICES_VERTICAL,
|
||||
blank=True, null=True,
|
||||
help_text='Wenn kein Ankerpunkt von Hand (horizontal und vertikal)\
|
||||
festgelegt wird, versucht die Software diesen selbst zu erraten.'
|
||||
)
|
||||
|
||||
event = models.ForeignKey(Event)
|
||||
event = models.ForeignKey('events.Event')
|
||||
description = models.TextField(
|
||||
_("Description"),
|
||||
max_length=300,
|
||||
@@ -321,10 +82,12 @@ class Photo(ImageModel):
|
||||
orientation = 1
|
||||
|
||||
class Meta:
|
||||
get_latest_by = "created_date"
|
||||
ordering = ["created_date"]
|
||||
db_table = 'events_photo'
|
||||
verbose_name = _('Event Image')
|
||||
verbose_name_plural = _('Event Images')
|
||||
ordering = ["created_date"]
|
||||
get_latest_by = "created_date"
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
return os.path.basename(self.image.name)
|
||||
@@ -395,9 +158,14 @@ class Photo(ImageModel):
|
||||
Triggers to save related Event to save. This should force an update for
|
||||
the denormalized Photo count.
|
||||
"""
|
||||
ImageModel.save(self, **kwargs)
|
||||
super(Photo, self).save()
|
||||
self.save_metadata()
|
||||
self.event.save()
|
||||
|
||||
|
||||
models.signals.post_save.connect(post_save_image, sender=Photo)
|
||||
def update_event(sender, instance=None, created=False, raw=False, **kwargs):
|
||||
image_models.regenerate_image_cache(sender, instance=instance)
|
||||
instance.event.save()
|
||||
|
||||
|
||||
models.signals.post_save.connect(update_event, sender=Photo)
|
||||
models.signals.post_delete.connect(update_event, sender=Photo)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "events/event_detail.html" %}
|
||||
{% load i18n comments %}
|
||||
|
||||
{% block maincontent %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "events/photo_list.html" %}
|
||||
{% extends "gallery/photo_list.html" %}
|
||||
{% load i18n comments %}
|
||||
|
||||
{% block title %} {{ photo.name }} - {{ photo.event.name }} {% endblock %}
|
||||
@@ -22,35 +22,36 @@
|
||||
<a href="{{ photo.next_photo.get_absolute_url }}" class="next">Next</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="grid_10 push_1">{{ photo.description }}</p>
|
||||
<div class="grid_12 more_link">
|
||||
<a href="https://m.google.com/app/plus/x/?v=compose&content={{photo.headline|urlencode}}+-+http%3A%2F%2Fwww.kasu.at/{{photo.get_absolute_url|urlencode}}" target="_blank"><img src="{{STATIC_URL}}img/google_plus.png" alt="Google+" title="{% trans 'Share on Google+'%}" /></a>
|
||||
<a href="https://twitter.com/share?url=http%3A%2F%2Fwww.kasu.at/{{photo.get_absolute_url|urlencode}}" target='_blank'><img src="{{STATIC_URL}}img/twitter.png" alt="Twitter" title="{% trans 'Share on Twitter' %}" /></a>
|
||||
<a href="http://facebook.com/sharer.php?u=http%3A%2F%2Fwww.kasu.at{{photo.get_absolute_url|urlencode}}" target="_blank"><img src="{{STATIC_URL}}img/facebook.png" alt="Facebook" title="{% trans 'Share on Facebook'%}" /></a>
|
||||
</div>
|
||||
|
||||
{% if perms.events.change_photo %}
|
||||
<ul class="info grid_12">
|
||||
<li class="user"><strong>{% trans 'Photographer' %}: </strong>{{ photo.photographer }}</li>
|
||||
<li class="date"><strong>{% trans 'on' %}</strong> {{ photo.created_date }}</li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
{% block comment %}
|
||||
{% render_comment_list for photo %}
|
||||
{% render_comment_form for photo %}
|
||||
{% endblock %}
|
||||
|
||||
{% block buttonbar %}
|
||||
{% if perms.events.change_photo %}
|
||||
<form method="post" enctype="multipart/form-data" class="grid_12">
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
{% include "form.html" %}
|
||||
<p class="buttonbar">
|
||||
<a href="{{ photo.image.url }}" class="button" type="application/octet-stream"><img src="{{ STATIC_URL}}icons/drive_go.png" alt="{% trans 'download' %}" title="{% trans 'download' %}" /></a>
|
||||
<button type="submit" name="rotate" value="counter-clockwise"><img src="{{STATIC_URL}}icons/shape_rotate_anticlockwise.png" title="Gegen den Uhrzeiger drehen"></button>
|
||||
<button type="submit" name="rotate" value="clockwise"><img src="{{STATIC_URL}}icons/shape_rotate_clockwise.png" title="Im Uhrzeiger drehen"></button>
|
||||
<button type="submit"><img src="{{STATIC_URL}}icons/disk.png" alt=""> {% trans "save" %}</button>
|
||||
</p>
|
||||
</fieldset>
|
||||
</form>
|
||||
{% else %}
|
||||
<p class="grid_10 push_1">{{ photo.description }}</p>
|
||||
|
||||
<ul class="info grid_11 push_1">
|
||||
<li class="user"><strong>{% trans 'Photographer' %}: </strong>{{ photo.photographer }}</li>
|
||||
<li class="date"><strong>{% trans 'on' %}</strong> {{ photo.created_date }}</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
<div class="grid_12 more_link">
|
||||
<a href="https://m.google.com/app/plus/x/?v=compose&content={{photo.headline|urlencode}}+-+http%3A%2F%2Fwww.kasu.at/{{photo.get_absolute_url|urlencode}}" target="_blank"><img src="{{STATIC_URL}}img/google_plus.png" alt="Google+" title="{% trans 'Share on Google+'%}" /></a>
|
||||
<a href="https://twitter.com/share?url=http%3A%2F%2Fwww.kasu.at/{{photo.get_absolute_url|urlencode}}" target='_blank'><img src="{{STATIC_URL}}img/twitter.png" alt="Twitter" title="{% trans 'Share on Twitter' %}" /></a>
|
||||
<a href="http://facebook.com/sharer.php?u=http%3A%2F%2Fwww.kasu.at{{photo.get_absolute_url|urlencode}}" target="_blank"><img src="{{STATIC_URL}}img/facebook.png" alt="Facebook" title="{% trans 'Share on Facebook'%}" /></a>
|
||||
</div>
|
||||
{% render_comment_list for photo %}
|
||||
{% render_comment_form for photo %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "events/photo_gallery.html" %}
|
||||
{% extends "events/event_detail.html" %}
|
||||
{% load i18n comments %}
|
||||
|
||||
{% block maincontent %}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
from .views import *
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^$', EventGallery.as_view(), name='event-gallery'),
|
||||
url(r'^(?P<event>[\d]+)/$', EventPhotoList.as_view(),
|
||||
name='event-photo-list'),
|
||||
url(r'^(?P<event>[\d]+)/upload/$', EventPhotoUpload.as_view(),
|
||||
name='event-photo-upload'),
|
||||
url(r'^(?P<event>[\d]+)/(?P<pk>[\d]+)/$', EventPhoto.as_view(),
|
||||
name='event-photo'),
|
||||
url(r'^delete/(?P<pk>[\d]+)/$', DeleteEventPhoto.as_view(),
|
||||
name='delete-event-photo'),
|
||||
url(r'^upload/$', EventPhotoUpload.as_view(), name='event-photo-upload'),
|
||||
)
|
||||
165
gallery/views.py
165
gallery/views.py
@@ -5,159 +5,55 @@ from datetime import timedelta
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.http import Http404
|
||||
from django.shortcuts import redirect
|
||||
from django.utils import timezone
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views import generic
|
||||
from icalendar import Calendar, Event
|
||||
import pyexiv2
|
||||
|
||||
from utils.mixins import PermissionRequiredMixin
|
||||
|
||||
from . import models, forms
|
||||
from events.models import Event
|
||||
from .models import Photo
|
||||
from . import forms
|
||||
|
||||
|
||||
class DeleteEventPhoto(generic.DeleteView):
|
||||
model = models.Photo
|
||||
"""
|
||||
def get_object(self, queryset=None):
|
||||
return models.Photo.objects.get(pk=self.kwargs['pk'])
|
||||
"""
|
||||
model = Photo
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('event-photo-list', args=[self.object.event.id])
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DeleteEventPhoto, self).get_context_data()
|
||||
context['event'] = self.object.event
|
||||
return context
|
||||
|
||||
@method_decorator(permission_required('events.delete_photo'))
|
||||
def dispatch(self, *args, **kwargs):
|
||||
return super(DeleteEventPhoto, self).dispatch(*args, **kwargs)
|
||||
|
||||
|
||||
class EventArchiveIndex(generic.ArchiveIndexView):
|
||||
allow_empty = True
|
||||
context_object_name = 'event_list'
|
||||
date_field = 'start'
|
||||
model = models.Event
|
||||
queryset = model.objects.all()
|
||||
paginate_by = 15
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.ArchiveIndexView.get_context_data(self, **kwargs)
|
||||
context['is_archive'] = True
|
||||
return context
|
||||
|
||||
|
||||
class EventArchiveMonth(generic.MonthArchiveView):
|
||||
date_field = 'start'
|
||||
make_object_list = True
|
||||
model = models.Event
|
||||
month_format = '%m'
|
||||
paginate_by = 15
|
||||
template_name = 'events/event_archive.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.MonthArchiveView.get_context_data(self, **kwargs)
|
||||
context['is_archive'] = True
|
||||
return context
|
||||
|
||||
|
||||
class EventArchiveYear(generic.YearArchiveView):
|
||||
date_field = 'start'
|
||||
make_object_list = True
|
||||
model = models.Event
|
||||
paginate_by = 15
|
||||
template_name = 'events/event_archive.html'
|
||||
year_format = '%Y'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.YearArchiveView.get_context_data(self, **kwargs)
|
||||
context['is_archive'] = True
|
||||
return context
|
||||
|
||||
|
||||
class EventDetail(generic.DetailView):
|
||||
model = models.Event
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.DetailView.get_context_data(self, **kwargs)
|
||||
context['form'] = forms.PhotoUploadForm(initial={'event': self.object, 'photographer': self.request.user})
|
||||
return context
|
||||
|
||||
|
||||
class EventForm(PermissionRequiredMixin, generic.UpdateView):
|
||||
form_class = forms.EventForm
|
||||
permission_required = 'events.add_event'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
"""
|
||||
If an id has been submitted, try return the existing Event for an update,
|
||||
else creates a new one.
|
||||
@param queryset:
|
||||
"""
|
||||
if self.kwargs.get('pk'):
|
||||
event = models.Event.objects.get(pk=self.kwargs['pk'])
|
||||
if event.event_series:
|
||||
return event.event_series
|
||||
else:
|
||||
return event
|
||||
else:
|
||||
return models.Event()
|
||||
|
||||
|
||||
class EventGallery(generic.ListView):
|
||||
template_name = 'events/photo_gallery.html'
|
||||
queryset = models.Event.objects.filter(start__lt=timezone.now(), photo_count__gt=0)
|
||||
template_name = 'gallery/photo_gallery.html'
|
||||
queryset = Event.objects.filter(start__lt=timezone.now(), photo_count__gt=0)
|
||||
paginate_by = 12
|
||||
|
||||
|
||||
class EventListIcal(generic.View):
|
||||
"""
|
||||
Generates an returns an iCal File with all upcoming events.
|
||||
"""
|
||||
|
||||
def add_event(self, event):
|
||||
ics_event = Event()
|
||||
dtstart = timezone.localtime(event.start)
|
||||
dtend = timezone.localtime(event.end)
|
||||
|
||||
ics_event.add('DTSTART', dtstart)
|
||||
ics_event.add('SUMMARY', event.name)
|
||||
ics_event.add('DESCRIPTION', event.description)
|
||||
ics_event.add('LOCATION', event.location.address)
|
||||
ics_event.add('URL', 'http://www.kasu.at' + event.get_absolute_url())
|
||||
ics_event['UID'] = 'event-%d@www.kasu.at' % event.pk
|
||||
ics_event.add('PRIORITY', 5)
|
||||
if event.end:
|
||||
ics_event.add('DTEND', dtend)
|
||||
self.calendar.add_component(ics_event)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
response = HttpResponse(mimetype="text/calendar; charset=UTF-8")
|
||||
self.calendar = Calendar()
|
||||
self.calendar.add('prodid', 'http://www.kasu.at/')
|
||||
self.calendar.add('version', '2.0')
|
||||
for event in models.Event.objects.upcoming(limit=None):
|
||||
self.add_event(event)
|
||||
response.write(self.calendar.to_ical())
|
||||
return response
|
||||
|
||||
|
||||
class EventPhoto(generic.UpdateView):
|
||||
form_class = forms.EditPhotoForm
|
||||
model = models.Photo
|
||||
template_name = 'events/photo_detail.html'
|
||||
model = Photo
|
||||
template_name = 'gallery/photo_detail.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(EventPhoto, self).get_context_data()
|
||||
event = models.Event.objects.get(id=self.kwargs['event'])
|
||||
context['event'] = event
|
||||
context['event'] = self.object.event
|
||||
return context
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if request.POST.get('rotate') and request.user.has_perm('events.change_photo'):
|
||||
photo = models.Photo.objects.get(pk=kwargs['pk'])
|
||||
if request.POST.get('rotate') and request.user.has_perm(
|
||||
'events.change_photo'):
|
||||
photo = Photo.objects.get(pk=kwargs['pk'])
|
||||
photo.rotate(request.POST['rotate'])
|
||||
# return redirect(photo.get_absolute_url())
|
||||
return self.get(request)
|
||||
@@ -172,20 +68,21 @@ class EventPhotoList(generic.ListView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.ListView.get_context_data(self, **kwargs)
|
||||
context['event'] = self.event
|
||||
context['form'] = forms.PhotoUploadForm(initial={'event': self.event, 'photographer': self.request.user})
|
||||
context['form'] = forms.PhotoUploadForm(
|
||||
initial={'event': self.event, 'photographer': self.request.user})
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
try:
|
||||
self.event = models.Event.objects.get(id=self.kwargs['event'])
|
||||
return models.Photo.objects.filter(event=self.event)
|
||||
except models.Event.DoesNotExist:
|
||||
self.event = Event.objects.get(id=self.kwargs['event'])
|
||||
return Photo.objects.filter(event=self.event)
|
||||
except Event.DoesNotExist:
|
||||
raise Http404(_('Event does not exist'))
|
||||
|
||||
|
||||
class EventPhotoUpload(generic.FormView):
|
||||
form_class = forms.PhotoUploadForm
|
||||
template_name = 'events/photo_upload.html'
|
||||
template_name = 'gallery/photo_upload.html'
|
||||
|
||||
@method_decorator(permission_required('events.add_photo'))
|
||||
def dispatch(self, *args, **kwargs):
|
||||
@@ -193,7 +90,7 @@ class EventPhotoUpload(generic.FormView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.FormView.get_context_data(self, **kwargs)
|
||||
context['event_list'] = models.Event.objects.archive()[:12]
|
||||
context['event_list'] = Event.objects.archive()[:12]
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
@@ -208,14 +105,16 @@ class EventPhotoUpload(generic.FormView):
|
||||
"""
|
||||
|
||||
"""
|
||||
self.event = models.Event.objects.get(id=self.request.REQUEST.get('event'))
|
||||
photographer = self.request.POST.get('photographer', self.request.user.id)
|
||||
self.event = Event.objects.get(
|
||||
id=self.request.REQUEST.get('event'))
|
||||
photographer = self.request.POST.get('photographer',
|
||||
self.request.user.id)
|
||||
photographer = get_user_model().objects.get(id=photographer)
|
||||
self.counter = 1
|
||||
for upload in self.request.FILES.getlist('upload'):
|
||||
name = upload.name
|
||||
created_date, description = self.read_exif(upload)
|
||||
photo = models.Photo(
|
||||
photo = Photo(
|
||||
event=self.event,
|
||||
photographer=photographer,
|
||||
image=upload,
|
||||
@@ -241,7 +140,3 @@ class EventPhotoUpload(generic.FormView):
|
||||
description = ''
|
||||
return created_date, description
|
||||
|
||||
|
||||
class UpcomingEvents(generic.ListView):
|
||||
queryset = models.Event.objects.upcoming(limit=None)
|
||||
paginate_by = 16
|
||||
|
||||
@@ -299,46 +299,28 @@ ul.info li {
|
||||
}
|
||||
|
||||
/** PAGINATOR **/
|
||||
.paginator {
|
||||
clear: both;
|
||||
border: 1px solid #d3d7cf;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
background-color: #f9f9f9;
|
||||
box-shadow: inset 0px 1px 0px 0px #ffffff;
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0.05, #f9f9f9
|
||||
), color-stop(1, #e9e9e9) );
|
||||
background: -moz-linear-gradient(center top, #f9f9f9 5%, #e9e9e9 100%);
|
||||
padding: 4px 10px 2px 10px;
|
||||
.pagination {
|
||||
text-align:center
|
||||
}
|
||||
|
||||
.paginator a, .paginator .current {
|
||||
.pagination a, .pagination .current, .pagination .next, .pagination .previous {
|
||||
display: inline-block;
|
||||
margin: 0px 2px;
|
||||
padding: 2px;
|
||||
min-width: 17px;
|
||||
color: #2e3436;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
text-decoration: none;
|
||||
padding: 0 0.5em 0 0.5em;
|
||||
}
|
||||
|
||||
.paginator .current {
|
||||
border: none;
|
||||
color: #a40000;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.paginator .next {
|
||||
display: inline-block;
|
||||
.pagination .next {
|
||||
float: right;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.paginator .prev {
|
||||
display: inline-block;
|
||||
|
||||
.pagination .previous {
|
||||
float: left;
|
||||
background: none;
|
||||
}
|
||||
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -403,4 +385,4 @@ ul.tabs li.active a {border-bottom: 3px solid #bc0a19; color: #bc0a19}
|
||||
}
|
||||
fieldset.comment {padding: 0}
|
||||
fieldset.comment legend {margin-left: 15px}
|
||||
fieldset.comment .buttonbar {margin: 0; width: 100%}
|
||||
fieldset.comment .buttonbar {margin: 0; width: 100%}
|
||||
|
||||
@@ -136,14 +136,14 @@ ul.main_menu {padding:0px;}
|
||||
width: 920px;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
#navigation, .pagination {
|
||||
clear: both;
|
||||
background: url(../img/navigation-bg.png) no-repeat left top;
|
||||
height: 56px;
|
||||
list-style: none;
|
||||
margin: 0 auto;
|
||||
padding: 8px 0 0 30px;
|
||||
width: 920px;
|
||||
padding: 8px 35px 0px 25px;
|
||||
width: 900px;
|
||||
}
|
||||
|
||||
#navigation a {
|
||||
@@ -434,6 +434,23 @@ ul.tabs li.active a {
|
||||
text-shadow: 1px 1px 0px #98231a;
|
||||
}
|
||||
|
||||
.pagination a, .pagination .current, .pagination .next, .pagination .previous {
|
||||
display: inline-block;
|
||||
padding: 2px;
|
||||
color: #FFF;
|
||||
font-weight: bold;
|
||||
height: 33px;
|
||||
text-decoration: none;
|
||||
padding: 17px 0.5em 0 0.5em;
|
||||
}
|
||||
|
||||
.pagination .current {
|
||||
color: #a40000;
|
||||
}
|
||||
|
||||
.pagination .disabled {color: #ccc}
|
||||
|
||||
|
||||
img.avatar {
|
||||
border: none;
|
||||
box-shadow: 2px 2px 2px #888;
|
||||
|
||||
@@ -337,6 +337,12 @@ fieldset ul li {
|
||||
}
|
||||
|
||||
ul.tabs {margin-top:1em;}
|
||||
.paginator {clear:both;}
|
||||
.pagination {clear:both;}
|
||||
.pagination a, .pagination .current, .pagination .next, .pagination .previous {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
padding: 0 0.5em 0 0.5em;
|
||||
}
|
||||
|
||||
.gallery {float: left; width:150px; height:150px; margin: 10px;}
|
||||
.gallery h3 {font-size: 12pt;}
|
||||
.gallery h3 {font-size: 12pt;}
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
{% load i18n %}
|
||||
<div class="paginator grid_12">
|
||||
{% if page_obj.has_previous %}
|
||||
<a class="prev" href="?page={{ page_obj.previous_page_number }}"><img src="{{STATIC_URL}}icons/resultset_previous.png" alt="«" /> {% trans "Prev" %}</a>
|
||||
{% else %}
|
||||
<span class="prev disabled"><img src="{{STATIC_URL}}icons/resultset_previous.png" alt="«" /> {% trans "Prev" %}</span>
|
||||
{% endif %}
|
||||
{% for page in paginator.page_range %}
|
||||
{% ifequal page_obj.number page %}
|
||||
<span class="current">{{page}}</span>
|
||||
{% else %}
|
||||
<a class="page" href="?page={{page}}">{{page}}</a>
|
||||
{% endifequal %}
|
||||
{% endfor %}
|
||||
{% if page_obj.has_next %}
|
||||
<a class="next" href="?page={{ page_obj.next_page_number }}">{% trans "Next" %} <img src="{{STATIC_URL}}icons/resultset_next.png" alt="»" /></a>
|
||||
{% else %}
|
||||
<span class="next disabled">{% trans "Next" %} <img src="{{STATIC_URL}}icons/resultset_next.png" alt="»" /></span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<nav class="pagination">
|
||||
{% if page_obj.has_previous %}
|
||||
<a class="previous" href="?page={{ page_obj.previous_page_number }}"><span aria-hidden="true">←</span> {% trans "Previous" %}</a></li>
|
||||
{% else %}
|
||||
<div class="previous disabled"><span aria-hidden="true">←</span> {% trans "Prev" %}</div>
|
||||
{% endif %}
|
||||
{% for page in paginator.page_range %}
|
||||
{% ifequal page_obj.number page %}
|
||||
<div class="current">{{page}}</div>
|
||||
{% else %}
|
||||
<a href="?page={{page}}">{{page}}</a>
|
||||
{% endifequal %}
|
||||
{% endfor %}
|
||||
{% if page_obj.has_next %}
|
||||
<a class="next" href="?page={{ page_obj.next_page_number }}">{% trans "Next" %} <span aria-hidden="true">→</span></a></li>
|
||||
{% else %}
|
||||
<div class="next disabled">{% trans "Next" %} <span aria-hidden="true">→</span></div>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ DAN_RANKS = (
|
||||
DAN_RANKS_DICT = dict([(dan, points) for points, dan in DAN_RANKS])
|
||||
MIN_HANCHANS_FOR_LADDER = 10
|
||||
|
||||
|
||||
logger = getLogger('kasu.mahjong_ranking')
|
||||
|
||||
|
||||
|
||||
@@ -7,9 +7,10 @@ Created on 19.09.2011
|
||||
"""
|
||||
# import stuff we need from django
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from . import models, set_dirty
|
||||
from forms import PlayerFormSet
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
|
||||
def recalculate(modeladmin, request, queryset):
|
||||
@@ -27,7 +28,10 @@ def recalculate(modeladmin, request, queryset):
|
||||
set_dirty(season=ranking_season.id)
|
||||
elif isinstance(modeladmin, LadderRankingAdmin):
|
||||
for ladder_ranking in queryset:
|
||||
set_dirty(user=ladder_ranking.user_id, season=ladder_ranking.season_id) # @IgnorePep8
|
||||
set_dirty(user=ladder_ranking.user_id,
|
||||
season=ladder_ranking.season_id) # @IgnorePep8
|
||||
|
||||
|
||||
recalculate.short_description = _("Recalculate")
|
||||
|
||||
|
||||
@@ -35,7 +39,7 @@ class PlayerInline(admin.TabularInline):
|
||||
extra = 4
|
||||
formset = PlayerFormSet
|
||||
readonly_fields = ('placement', 'kyu_points', 'dan_points', 'bonus_points',
|
||||
'comment',)
|
||||
'comment',)
|
||||
max_num = 4
|
||||
model = models.Player
|
||||
|
||||
@@ -43,7 +47,7 @@ class PlayerInline(admin.TabularInline):
|
||||
class EventRankingAdmin(admin.ModelAdmin):
|
||||
list_filter = ['event']
|
||||
list_display = ('placement', 'user', 'event', 'avg_placement', 'avg_score',
|
||||
'hanchan_count', 'good_hanchans', 'won_hanchans', 'dirty')
|
||||
'hanchan_count', 'good_hanchans', 'won_hanchans', 'dirty')
|
||||
list_display_links = ('user',)
|
||||
actions = [recalculate]
|
||||
|
||||
@@ -53,7 +57,7 @@ class HanchanAdmin(admin.ModelAdmin):
|
||||
date_hierarchy = 'start'
|
||||
list_filter = ['season', 'event']
|
||||
list_display = ('event', 'start', 'player_names', 'comment',
|
||||
'confirmed', 'valid', 'check_validity')
|
||||
'confirmed', 'valid', 'check_validity')
|
||||
inlines = (PlayerInline,)
|
||||
readonly_fields = ('valid', 'check_validity')
|
||||
|
||||
@@ -66,13 +70,14 @@ class HanchanAdmin(admin.ModelAdmin):
|
||||
class KyuDanAdmin(admin.ModelAdmin):
|
||||
actions = [recalculate]
|
||||
list_display = ('user', 'kyu', 'kyu_points', 'dan', 'dan_points',
|
||||
'hanchan_count', 'dirty')
|
||||
'hanchan_count', 'dirty')
|
||||
|
||||
|
||||
class LadderRankingAdmin(admin.ModelAdmin):
|
||||
actions = [recalculate]
|
||||
list_display = ('placement', 'season', 'user', 'avg_placement',
|
||||
'avg_score', 'hanchan_count', 'good_hanchans', 'won_hanchans', 'dirty')
|
||||
'avg_score', 'hanchan_count', 'good_hanchans',
|
||||
'won_hanchans', 'dirty')
|
||||
list_display_links = ('user',)
|
||||
list_filter = ('season',)
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ from django.utils import timezone
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from utils.html5 import forms
|
||||
|
||||
from . import models
|
||||
|
||||
|
||||
@@ -81,7 +80,6 @@ class PlayerForm(forms.ModelForm):
|
||||
|
||||
|
||||
class PlayerInlineFormSet(BaseInlineFormSet):
|
||||
|
||||
def clean(self):
|
||||
"""Checks that no two articles have the same title."""
|
||||
for form in self.forms:
|
||||
@@ -102,6 +100,7 @@ class SeasonSelectForm(django.forms.Form):
|
||||
season_list = season_list.values_list('season__id', 'season__name')
|
||||
self.fields['season'] = django.forms.ChoiceField(choices=season_list)
|
||||
|
||||
|
||||
PlayerFormSet = inlineformset_factory(
|
||||
models.Hanchan,
|
||||
models.Player,
|
||||
|
||||
@@ -4,13 +4,16 @@
|
||||
Generate Randum Mahjong Hanchans to the the Raning System
|
||||
"""
|
||||
|
||||
from django.contrib import auth
|
||||
from django.core.management.base import BaseCommand
|
||||
from events.models import Event
|
||||
from mahjong_ranking import models
|
||||
import random
|
||||
from datetime import timedelta
|
||||
|
||||
from django.contrib import auth
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from events.models import Event
|
||||
from mahjong_ranking import models
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Deletes all expired user registrations from the database"
|
||||
|
||||
@@ -23,7 +26,8 @@ class Command(BaseCommand):
|
||||
player_list = list()
|
||||
ostwind_list = list()
|
||||
for user in user_list:
|
||||
player_list.append(models.Player(user=user, hanchan=hanchan, score=25000))
|
||||
player_list.append(
|
||||
models.Player(user=user, hanchan=hanchan, score=25000))
|
||||
for player in player_list:
|
||||
player.save()
|
||||
|
||||
@@ -33,8 +37,8 @@ class Command(BaseCommand):
|
||||
ostwind = ostwind_list.pop()
|
||||
while not end_of_game:
|
||||
score = random.randrange(1300, 8000, 100)
|
||||
loser = player_list[random.randrange(0,4,1)]
|
||||
winner = player_list[random.randrange(0,4,1)]
|
||||
loser = player_list[random.randrange(0, 4, 1)]
|
||||
winner = player_list[random.randrange(0, 4, 1)]
|
||||
winner.score += score
|
||||
|
||||
print 'Ostwind: %s, Gewinner: %s, Verlierer: %s, %d Punkte' % (
|
||||
@@ -73,9 +77,9 @@ class Command(BaseCommand):
|
||||
print ""
|
||||
|
||||
def create_hanchan(self, event):
|
||||
start = event.start + timedelta(minutes = random.randrange(00, 300, 15))
|
||||
start = event.start + timedelta(minutes=random.randrange(00, 300, 15))
|
||||
print event.name, start
|
||||
print '='*80
|
||||
print '=' * 80
|
||||
hanchan = models.Hanchan(event=event, start=start)
|
||||
hanchan.save()
|
||||
self.add_players(hanchan)
|
||||
@@ -87,7 +91,7 @@ class Command(BaseCommand):
|
||||
self.user_list = list(auth.get_user_model().objects.all())
|
||||
|
||||
for event in Event.objects.all():
|
||||
for i in range(random.randrange(2,8)):
|
||||
for i in range(random.randrange(2, 8)):
|
||||
self.create_hanchan(event)
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ Created on 23.05.2011
|
||||
"""
|
||||
from django.core.cache import cache
|
||||
from django.db import transaction
|
||||
|
||||
from mahjong_ranking import models
|
||||
from . import logger
|
||||
|
||||
@@ -30,7 +31,8 @@ class DenormalizationUpdateMiddleware(object):
|
||||
if len(event_ranking_queue) > 0:
|
||||
while len(event_ranking_queue) > 0:
|
||||
event_id, user_id = event_ranking_queue.pop()
|
||||
logger.info("recalculate %d tournament Ranking in %s", user_id, event_id)
|
||||
logger.info("recalculate %d tournament Ranking in %s", user_id,
|
||||
event_id)
|
||||
ranking = models.EventRanking.objects.get_or_create(
|
||||
event_id=event_id, user_id=user_id)[0]
|
||||
ranking.recalculate()
|
||||
@@ -57,7 +59,8 @@ class DenormalizationUpdateMiddleware(object):
|
||||
user_id=user_id, season_id=season_id)[0]
|
||||
ranking.recalculate()
|
||||
else:
|
||||
logger.error('Season: %i; Benutzer Nr. %i - existiert nicht!', season_id, user_id)
|
||||
logger.error('Season: %i; Benutzer Nr. %i - existiert nicht!',
|
||||
season_id, user_id)
|
||||
cache.set('ladder_ranking_queue', ladder_ranking_queue, 360)
|
||||
transaction.commit()
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from datetime import date, timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import ValidationError
|
||||
@@ -9,11 +10,12 @@ from django.db import models
|
||||
from django.db.models.aggregates import Sum
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext as _
|
||||
from events.models import Event
|
||||
|
||||
from events.models import Event
|
||||
from . import KYU_RANKS, DAN_RANKS, DAN_RANKS_DICT, MIN_HANCHANS_FOR_LADDER
|
||||
from . import logger, set_dirty
|
||||
|
||||
|
||||
kyu_dan_rankings = set()
|
||||
ladder_rankings = set()
|
||||
ladder_seasons = set()
|
||||
@@ -53,8 +55,10 @@ class EventRanking(models.Model):
|
||||
zur neuberrechnung zu markieren. Mittlerweile wird ein lokaler
|
||||
Cache dafür verwendet, das ist schneller.
|
||||
"""
|
||||
logger.info(u'Recalculate EventRanking for Player %s in %s', self.user, self.event.name) # @IgnorePep8
|
||||
event_hanchans = Player.objects.valid_hanchans(user=self.user_id, event=self.event_id) # @IgnorePep8
|
||||
logger.info(u'Recalculate EventRanking for Player %s in %s', self.user,
|
||||
self.event.name) # @IgnorePep8
|
||||
event_hanchans = Player.objects.valid_hanchans(user=self.user_id,
|
||||
event=self.event_id) # @IgnorePep8
|
||||
aggregator = event_hanchans.aggregate(
|
||||
models.Avg('placement'),
|
||||
models.Avg('score'),
|
||||
@@ -78,7 +82,9 @@ class Hanchan(models.Model):
|
||||
Außerdem gehört jede Hanchan zu einer Veranstaltung.
|
||||
"""
|
||||
comment = models.TextField(_('Comment'), blank=True)
|
||||
confirmed = models.BooleanField(_('Has been Confirmed'), default=True, help_text=_('Only valid and confirmed Hanchans will be counted in the rating.')) # @IgnorePep8
|
||||
confirmed = models.BooleanField(_('Has been Confirmed'), default=True,
|
||||
help_text=_(
|
||||
'Only valid and confirmed Hanchans will be counted in the rating.')) # @IgnorePep8
|
||||
event = models.ForeignKey(Event)
|
||||
player_names = models.CharField(max_length=127, editable=False)
|
||||
players = models.ManyToManyField(
|
||||
@@ -86,8 +92,10 @@ class Hanchan(models.Model):
|
||||
through='Player',
|
||||
verbose_name=_('Players')
|
||||
)
|
||||
season = models.ForeignKey('LadderSeason', blank=True, null=True, editable=False) # @IgnorePep8
|
||||
start = models.DateTimeField(_('Start'), help_text=_('This is crucial to get the right Hanchans that scores')) # @IgnorePep8
|
||||
season = models.ForeignKey('LadderSeason', blank=True, null=True,
|
||||
editable=False) # @IgnorePep8
|
||||
start = models.DateTimeField(_('Start'), help_text=_(
|
||||
'This is crucial to get the right Hanchans that scores')) # @IgnorePep8
|
||||
valid = models.BooleanField(_('Is Valid'), default=False)
|
||||
|
||||
class Meta(object):
|
||||
@@ -96,7 +104,8 @@ class Hanchan(models.Model):
|
||||
verbose_name_plural = _(u'Hanchans')
|
||||
|
||||
def __str__(self):
|
||||
return "Hanchan am {0:%d.%m.%Y} um {0:%H:%M} ({1})".format(self.start, self.player_names)
|
||||
return "Hanchan am {0:%d.%m.%Y} um {0:%H:%M} ({1})".format(self.start,
|
||||
self.player_names)
|
||||
|
||||
def check_validity(self):
|
||||
"""
|
||||
@@ -148,12 +157,14 @@ class Hanchan(models.Model):
|
||||
Die Gültigkeit wird geprüft und die Sasion in der die Hanchan liegt
|
||||
wird aktualisert.
|
||||
"""
|
||||
logger.debug("Hanchan clean() wurde getriggert!")
|
||||
super(Hanchan, self).clean()
|
||||
|
||||
# if self.pk and self.player_set.distinct().count() != 4:
|
||||
# raise ValidationError(
|
||||
# raise ValidationError(
|
||||
# _('For a Hanchan exactly 4 players are needed.'))
|
||||
if self.start and self.start > timezone.now():
|
||||
if not self.event_id:
|
||||
raise ValidationError(_("Hanchan has no event"))
|
||||
elif self.start and self.start > timezone.now():
|
||||
raise ValidationError(_("It's not allowed to enter future games."))
|
||||
elif not (self.event.start <= self.start <= self.event.end):
|
||||
raise ValidationError(_("Only games during the event are allowed"))
|
||||
@@ -195,7 +206,8 @@ class Hanchan(models.Model):
|
||||
def save(self, **kwargs):
|
||||
logger.debug("Hanchan save() wurde getriggert!")
|
||||
|
||||
self.season = self.season or LadderSeason.objects.get_by_date(self.start)
|
||||
self.season = self.season or LadderSeason.objects.get_by_date(
|
||||
self.start)
|
||||
if self.pk:
|
||||
self.check_validity()
|
||||
self.compute_player_placements()
|
||||
@@ -245,7 +257,8 @@ class KyuDanRanking(models.Model):
|
||||
self.wins_in_a_row = 0
|
||||
|
||||
if self.dan and self.wins_in_a_row > 2:
|
||||
logger.info('adding bonuspoints for 3 wins in a row for %s', self.user) # @IgnorePep8
|
||||
logger.info('adding bonuspoints for 3 wins in a row for %s',
|
||||
self.user) # @IgnorePep8
|
||||
new_dan_rank = self.dan + 1
|
||||
new_dan_points = DAN_RANKS_DICT[new_dan_rank] + 1
|
||||
bonus_points = new_dan_points - self.dan_points
|
||||
@@ -253,7 +266,8 @@ class KyuDanRanking(models.Model):
|
||||
logger.debug("Stats for %s:", self.user)
|
||||
logger.debug("current dan_points: %d", self.dan_points)
|
||||
logger.debug("current dan: %d", self.dan)
|
||||
logger.debug("min required points for the next dan: %d", new_dan_points) # @IgnorePep8
|
||||
logger.debug("min required points for the next dan: %d",
|
||||
new_dan_points) # @IgnorePep8
|
||||
logger.debug("bonus points to add: %d", bonus_points)
|
||||
|
||||
hanchan.dan_points += bonus_points
|
||||
@@ -424,7 +438,8 @@ class LadderRanking(models.Model):
|
||||
return reverse('player-ladder-score', args=[self.user.username])
|
||||
|
||||
def recalculate(self):
|
||||
logger.info(u'Recalculate LadderRanking for Player %s in Season %s', self.user, self.season) # @IgnorePep8
|
||||
logger.info(u'Recalculate LadderRanking for Player %s in Season %s',
|
||||
self.user, self.season) # @IgnorePep8
|
||||
ladder_hanchans = Player.objects.ladder_hanchans(
|
||||
self.user_id, self.season_id)
|
||||
aggregate = ladder_hanchans.aggregate(
|
||||
@@ -443,7 +458,6 @@ class LadderRanking(models.Model):
|
||||
|
||||
|
||||
class LadderSeasonManager(models.Manager):
|
||||
|
||||
def current(self):
|
||||
"""
|
||||
Returns the current season and caches the result for 12 hours
|
||||
@@ -489,6 +503,14 @@ class LadderSeason(models.Model):
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
"""
|
||||
URL zur Hanchanliste des Events wo diese Hanchan gelistet wurde.
|
||||
"""
|
||||
return reverse('mahjong-ladder', kwargs={'season': self.pk})
|
||||
|
||||
|
||||
|
||||
def recalculate(self):
|
||||
logger.info(u'Recalculate LadderSeason %s', self.name)
|
||||
self.ladderranking_set.update(placement=None)
|
||||
@@ -519,7 +541,8 @@ class PlayerManager(models.Manager):
|
||||
queryset = queryset.filter(kyu_points__isnull=False).select_related()
|
||||
return queryset
|
||||
|
||||
def ladder_hanchans(self, user=None, season=None, num_hanchans=None, max_age=None): # @IgnorePep8
|
||||
def ladder_hanchans(self, user=None, season=None, num_hanchans=None,
|
||||
max_age=None): # @IgnorePep8
|
||||
queryset = self.valid_hanchans(user).order_by('-hanchan__start')
|
||||
queryset = queryset.select_related()
|
||||
season = season or LadderSeason.objects.current()
|
||||
@@ -593,7 +616,8 @@ class Player(models.Model):
|
||||
ordering = ['-score']
|
||||
|
||||
def __str__(self):
|
||||
return "{0}'s Punkte vom {1: %d.%m.%Y um %H:%M}".format(self.user.username, self.hanchan.start)
|
||||
return "{0}'s Punkte vom {1: %d.%m.%Y um %H:%M}".format(
|
||||
self.user.username, self.hanchan.start)
|
||||
|
||||
def save(self, mark_dirty=True, season_id=None, *args, **kwargs):
|
||||
season_id = season_id or self.hanchan.season_id
|
||||
@@ -606,12 +630,15 @@ class Player(models.Model):
|
||||
else:
|
||||
return self
|
||||
if season_id:
|
||||
logger.debug("Marking %s's season no. %i ranking for recalculation.", self.user, season_id) # @IgnorePep8
|
||||
logger.debug(
|
||||
"Marking %s's season no. %i ranking for recalculation.",
|
||||
self.user, season_id) # @IgnorePep8
|
||||
set_dirty(season=season_id, user=self.user_id)
|
||||
logger.debug("Marking season no %i for recalculation.", season_id)
|
||||
set_dirty(season=season_id)
|
||||
if self.hanchan.event.is_tournament:
|
||||
logger.debug("Marking tournament %s for recalculation.", self.hanchan.event) # @IgnorePep8
|
||||
logger.debug("Marking tournament %s for recalculation.",
|
||||
self.hanchan.event) # @IgnorePep8
|
||||
set_dirty(event=self.hanchan.event_id, user=self.user_id)
|
||||
return self
|
||||
|
||||
@@ -622,4 +649,5 @@ def update_ranking_delete(sender, instance, **kwargs): # @UnusedVariable
|
||||
if instance.season_id:
|
||||
set_dirty(season=instance.season_id, user=player.user_id)
|
||||
|
||||
|
||||
models.signals.pre_delete.connect(update_ranking_delete, sender=Hanchan)
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
{% extends "events/event_site.html" %}
|
||||
{% extends "events/event_detail.html" %}
|
||||
|
||||
{% load i18n comments%}
|
||||
|
||||
{% block title %}{% trans "Tournament Ranking" %}: {{ event.name }}{% endblock %}
|
||||
{% block teaser %}<h1>{% trans "Tournament Ranking" %}: {{ event.name }}</h1>{% endblock %}
|
||||
|
||||
{% block event_content %}
|
||||
|
||||
{% if event.is_tournament %}
|
||||
<ul class="tabs grid_12">
|
||||
<li><a href="{% url 'event-hanchan-list' event.id %}">{% trans "Tournament Hanchans" %}</a></li>
|
||||
<li class="active"><a href="{% url 'event-ranking' event.id %}">{% trans "Tournament Ranking" %}</a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% block maincontent %}
|
||||
<div class="grid_12">
|
||||
<table>
|
||||
<thead>
|
||||
@@ -21,13 +15,14 @@
|
||||
<th rowspan="2">{% trans "Nickname" %}</th>
|
||||
<th rowspan="2">{% trans "Name" %}</th>
|
||||
<th colspan="2">{% trans 'Average' %}</th>
|
||||
<th colspan="2">Hanchans</th>
|
||||
<th colspan="3">Hanchans</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{% trans 'Placement' %}</th>
|
||||
<th>{% trans "Score" %}</th>
|
||||
<th>{% trans "won" %}</th>
|
||||
<th>{% trans "good" %}</th>
|
||||
<th>{% trans 'Placement' %}</th>
|
||||
<th>{% trans "Score" %}</th>
|
||||
<th>{% trans "count" %}</th>
|
||||
<th>{% trans "good" %}</th>
|
||||
<th>{% trans "won" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for player in eventranking_list %}
|
||||
@@ -42,11 +37,12 @@
|
||||
{% endif %}
|
||||
</a></td>
|
||||
<td><a href="{{ player.user.get_absolute_url }}">{{player.user}}</a></td>
|
||||
<td>{{profile.last_name}} {{profile.first_name}}</td>
|
||||
<td>{% if user.is_authenticated %}{{profile.last_name}} {{profile.first_name}}{% else %} ---{% endif %}</td>
|
||||
<td class="center">{{player.avg_placement|floatformat:2 }}</td>
|
||||
<td class="right">{{player.avg_score|floatformat:0 }}</td>
|
||||
<td class="center">{{player.won_hanchans}}</td>
|
||||
<td class="center">{{player.good_hanchans}}</td>
|
||||
<td class="right">{{player.hanchan_count}}</td>
|
||||
<td class="right">{{player.good_hanchans}}</td>
|
||||
<td class="right">{{player.won_hanchans}}</td>
|
||||
</tr>
|
||||
{% endwith %}
|
||||
{% empty %}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "events/event_detail.html" %}
|
||||
{% load i18n comments %}
|
||||
|
||||
{% block meta_title %}{% trans 'Delete Hanchan' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post">
|
||||
{% block maincontent %}
|
||||
<form method="post" class="grid_12">
|
||||
{% csrf_token %}
|
||||
<fieldset>
|
||||
<legend>{% trans "Delete Hanchan" %}</legend>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{% extends "events/event_site.html" %}
|
||||
{% extends "events/event_detail.html" %}
|
||||
{% load i18n comments fieldset_extras %}
|
||||
|
||||
{% block title %}
|
||||
{% if hanchan.id %}{% trans "Edit Hanchan" %}{% else %}{% trans "Add Hanchan" %}{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block event_content %}
|
||||
{% block maincontent %}
|
||||
{% get_fieldset "event, start" from form as event_formset %}
|
||||
{% if perms.mahjong_ranking.delete_hanchan %}
|
||||
{% get_fieldset "comment, confirmed" from form as hanchan_formset %}
|
||||
|
||||
@@ -1,53 +1,56 @@
|
||||
{% extends "events/event_site.html" %}
|
||||
|
||||
{% load i18n comments %}
|
||||
{% extends "events/event_detail.html" %}
|
||||
{% load i18n humanize %}
|
||||
|
||||
{% block title %}Hanchans: {{ event.name }}{% endblock %}
|
||||
|
||||
{% block event_content %}
|
||||
|
||||
{% if event.is_tournament %}
|
||||
<ul class="tabs grid_12">
|
||||
<li class="active"><a href="{% url 'event-hanchan-list' event.id %}">{% trans "Tournament Hanchans" %}</a></li>
|
||||
<li><a href="{% url 'event-ranking' event.id %}">{% trans "Tournament Ranking" %}</a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% block maincontent %}
|
||||
<h2 class="grid_12">{% trans 'Played Hanchans' %}</h2>
|
||||
<p> </p>
|
||||
{% for hanchan in hanchan_list %}
|
||||
<h3 class="grid_12 clearfix" id="{{ hanchan.pk }}">{{hanchan.start|time:'H:i'}}: {{ hanchan.player_names }}</h3>
|
||||
{% for player in hanchan.player_set.all %}
|
||||
<div class="player" >
|
||||
<a href="{% url 'player-ladder-score' player.user %}"><img
|
||||
src="{% if player.user.get_profile.thumbnail %}{{player.user.get_profile.thumbnail.url}}{% else %}{{STATIC_URL}}img/unknown_thumbnail.png{% endif %}"
|
||||
class="avatar" alt=""
|
||||
title="{% if player.dan_points != None %}Dan P.: {{player.dan_points}}{% else %}Kyu P.: {{player.kyu_points}}{% endif %} - {{player.comment}}"/></a>
|
||||
<h4>{{player.placement}}. - <a href="{% url 'player-ladder-score' player.user %}">{{ player.user }}</a></h4>
|
||||
<strong>{% trans 'Score' %}:</strong> {{player.score}}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if not hanchan.valid %}
|
||||
<p class="grid_12 error"><strong>Ungültig:</strong> {{hanchan.check_validity}}</p>
|
||||
{% elif not hanchan.confirmed %}
|
||||
<p class="grid_12 error">Diese Hanchan wurde nicht anerkannt und wird daher nicht gezählt.</p>
|
||||
{% elif hanchan.comment %}
|
||||
<p class="grid_12">{{ hanchan.comment }}</p>
|
||||
{% endif %}
|
||||
<p class="grid_12 more_link">
|
||||
{% if perms.mahjong_ranking.delete_hanchan %}
|
||||
<a href="{% url 'delete-hanchan' hanchan.pk %}" class="button"><img src="{{STATIC_URL}}icons/table_delete.png" alt="{% trans 'Delete' %}"/> {% trans 'Delete Hanchan' %}</a>
|
||||
{% endif %}
|
||||
{% if perms.mahjong_ranking.change_hanchan %}
|
||||
<a href="{% url 'edit-hanchan' hanchan.pk %}" class="button"><img src="{{STATIC_URL}}icons/table_edit.png" alt="{% trans 'Edit' %}"/> {% trans 'Edit Hanchan' %}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<div class="grid_12" id="{{ hanchan.pk }}"><h3>{{hanchan.start|time:'H:i'}}: {{ hanchan.player_names }}</h3></div>
|
||||
{% for player in hanchan.player_set.all %}
|
||||
<a class="grid_1" href="{% url 'player-ladder-score' player.user %}"><img src="{% if player.user.get_profile.thumbnail %}{{player.user.get_profile.thumbnail.url}}{% else %}{{STATIC_URL}}img/unknown_thumbnail.png{% endif %}" class="avatar" alt="{{ player.user }}" title="{{ player.user }}"/></a>
|
||||
<div class="grid_2">
|
||||
<a class="player" href="{% url 'player-ladder-score' player.user %}" title="{{ player.comment }}">{{ player.user }}</a>
|
||||
<p><strong>{{ player.placement|ordinal }} {% trans 'Place' %}</strong><br />
|
||||
<strong>{% trans 'Score' %}:</strong> {{ player.score|intcomma }}<br />
|
||||
{% if player.dan_points != None %}
|
||||
<strong>{% trans 'Dan Points' %}:</strong> {{ player.dan_points }}
|
||||
{% else %}
|
||||
<strong>{% trans 'Kyu Points' %}:</strong> {{ player.kyu_points|default:0 }}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if not hanchan.valid %}
|
||||
<p class="grid_12 error"><strong>Ungültig:</strong> {{hanchan.check_validity}}</p>
|
||||
{% elif not hanchan.confirmed %}
|
||||
<p class="grid_12 error">Diese Hanchan wurde nicht anerkannt und wird daher nicht gezählt.</p>
|
||||
{% elif hanchan.comment %}
|
||||
<p class="grid_12">{{ hanchan.comment }}</p>
|
||||
{% endif %}
|
||||
<p class="grid_12 more_link">
|
||||
{% if perms.mahjong_ranking.delete_hanchan %}
|
||||
<a href="{% url 'delete-hanchan' hanchan.pk %}" class="button"><img src="{{STATIC_URL}}icons/table_delete.png"
|
||||
alt="{% trans 'Delete' %}"/>
|
||||
{% trans 'Delete Hanchan' %}</a>
|
||||
{% endif %}
|
||||
{% if perms.mahjong_ranking.change_hanchan %}
|
||||
<a href="{% url 'edit-hanchan' hanchan.pk %}" class="button"><img src="{{STATIC_URL}}icons/table_edit.png"
|
||||
alt="{% trans 'Edit' %}"/>
|
||||
{% trans 'Edit Hanchan' %}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% empty %}
|
||||
<h3 class="grid_12">{% trans 'No Hanchan has been added to this event yet.'%}</h3>
|
||||
<h3 class="grid_12">{% trans 'No Hanchan has been added to this event yet.'%}</h3>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block buttonbar %}
|
||||
{% if perms.mahjong_ranking.add_hanchan %}
|
||||
<a class="button" href="{{event.get_edit_url}}"><img src="{{STATIC_URL}}icons/calendar_edit.png" alt="{% trans 'Add' %}"/> {% trans 'Edit Event' %}</a>
|
||||
<a class="button" href="{% url 'add-hanchan-form' event.id %}"><img src="{{STATIC_URL}}icons/table_add.png" alt="{% trans 'Add' %}"/> {% trans 'Add Hanchan' %}</a>
|
||||
{% endif %}
|
||||
{% if perms.mahjong_ranking.add_hanchan %}
|
||||
<a class="button" href="{{event.get_edit_url}}"><img src="{{STATIC_URL}}icons/calendar_edit.png"
|
||||
alt="{% trans 'Add' %}"/> {% trans 'Edit Event' %}</a>
|
||||
<a class="button" href="{% url 'add-hanchan-form' event.id %}"><img src="{{STATIC_URL}}icons/table_add.png"
|
||||
alt="{% trans 'Add' %}"/> {% trans 'Add Hanchan' %}</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,7 +1,9 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n comments%}
|
||||
|
||||
{% block teaser %} <h2>Mahjong Ranking - {{season.name}}</h2> {% endblock %}
|
||||
{% block teaser %}
|
||||
<h2>Mahjong Ranking - {{season.name}}</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
<main class="grid_12">
|
||||
@@ -49,14 +51,6 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block redbox %}
|
||||
{% if is_archive %}
|
||||
<h2>{% trans 'Ladder Archive' %}</h2>
|
||||
<ul class="list">
|
||||
{% for season in season_archive %}
|
||||
<li class="season"><a href="{% url 'mahjong-ladder-archive' season.id %}">{{season.name}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<h2>{% trans 'Latest Hanchans' %}</h2>
|
||||
<ul class="list">
|
||||
{% for hanchan in latest_hanchan_list %}
|
||||
@@ -69,9 +63,17 @@
|
||||
</ul>
|
||||
<h3>{% trans 'Latest Events' %}</h3>
|
||||
<ul class="list">
|
||||
{% for event in latest_event_list|slice:":3" %}
|
||||
{% for event in latest_event_list %}
|
||||
<li class="event"><a href="{% url 'event-hanchan-list' event.pk %}">{{event.name}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<h3>{% trans 'Ladder Archive' %}</h3>
|
||||
<form name="season_select">
|
||||
<label for="season">{% trans 'Season' %}</label>
|
||||
<select id="season" name="season" size="1" onChange="window.location.href = document.season_select.season.options[document.season_select.season.selectedIndex].value;">
|
||||
{% for season_link in season_list%}
|
||||
<option value="{{ season_link.get_absolute_url }}" {% ifequal season.id season_link.id %}selected="selected"{% endifequal %}>{{ season_link.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -2,7 +2,9 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %} {% trans 'Kyu Score for' %} {{player.username}} {% endblock %}
|
||||
|
||||
{% block teaser %}<h2>{% trans 'Invalid hanchans with' %} {{membership.user.username}}</h2>{% endblock %}
|
||||
|
||||
{% block score_list %}
|
||||
<div class="grid_12">
|
||||
<h2>{% trans 'Invalid hanchans with' %} {{membership.user.username}}</h2>
|
||||
|
||||
@@ -6,49 +6,37 @@ Created on 03.10.2011
|
||||
@author: christian
|
||||
"""
|
||||
from django.conf.urls import * # @UnusedWildImport
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
import views
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^$', views.LadderRankingList.as_view(), name="mahjong-ladder"),
|
||||
url(r'archive/$',
|
||||
views.LadderRankingList.as_view(),
|
||||
kwargs={'is_archive': True},
|
||||
name="mahjong-ladder-archive"),
|
||||
url(r'archive/(?P<season>[\d]+)/$',
|
||||
views.LadderRankingList.as_view(),
|
||||
name="mahjong-ladder-archive"),
|
||||
url(r'players/$',
|
||||
views.KyuDanRankingList.as_view(),
|
||||
url('^$', RedirectView.as_view(pattern_name='mahjong-ladder')),
|
||||
url(r'players/$', views.KyuDanRankingList.as_view(),
|
||||
name="kyudanranking-list"),
|
||||
url(r'players/(?P<order_by>[\+\-\w]+)/$',
|
||||
views.KyuDanRankingList.as_view(),
|
||||
url(r'players/(?P<order_by>[\+\-\w]+)/$', views.KyuDanRankingList.as_view(),
|
||||
name="kyudanranking-list"),
|
||||
url(r'event/(?P<event>[\d]+)/$',
|
||||
views.EventHanchanList.as_view(),
|
||||
url(r'ranking/$', views.LadderRankingList.as_view(), name="mahjong-ladder"),
|
||||
url(r'ranking/(?P<season>[\d]+)/$', views.LadderRankingList.as_view(),
|
||||
name="mahjong-ladder"),
|
||||
url(r'ranking/event/(?P<event>[\d]+)/$', views.EventHanchanList.as_view(),
|
||||
name="event-hanchan-list"),
|
||||
url(r'event/(?P<event>[\d]+)/ranking/$',
|
||||
views.EventRankingList.as_view(),
|
||||
name="event-ranking"),
|
||||
url(r'event/(?P<event>[\d]+)/add-hanchan/$',
|
||||
views.HanchanForm.as_view(),
|
||||
name="add-hanchan-form"),
|
||||
url(r'hanchan/(?P<hanchan>[\d]+)/edit/$',
|
||||
views.HanchanForm.as_view(),
|
||||
url(r'ranking/event/(?P<event>[\d]+)/ranking/$',
|
||||
views.EventRankingList.as_view(), name="event-ranking"),
|
||||
url(r'ranking/event/(?P<event>[\d]+)/add-hanchan/$',
|
||||
views.HanchanForm.as_view(), name="add-hanchan-form"),
|
||||
url(r'hanchan/(?P<hanchan>[\d]+)/edit/$', views.HanchanForm.as_view(),
|
||||
name="edit-hanchan"),
|
||||
url(r'hanchan/(?P<hanchan>[\d]+)/delete/$',
|
||||
views.DeleteHanchan.as_view(),
|
||||
url(r'hanchan/(?P<hanchan>[\d]+)/delete/$', views.DeleteHanchan.as_view(),
|
||||
name="delete-hanchan"),
|
||||
url(r'dan_score/(?P<username>[\-\.\d\w]+)/$',
|
||||
views.PlayerDanScore.as_view(),
|
||||
name="player-dan-score"),
|
||||
url(r'invalid_score/(?P<username>[\-\.\d\w]+)/$',
|
||||
views.PlayerInvalidScore.as_view(),
|
||||
name="player-invalid-score"),
|
||||
url(r'kyu_score/(?P<username>[\-\.\d\w]+)/$',
|
||||
views.PlayerKyuScore.as_view(),
|
||||
name="player-kyu-score"),
|
||||
url(r'ladder_score/(?P<username>[\-\.\d\w]+)/$',
|
||||
views.PlayerLadderScore.as_view(),
|
||||
name="player-ladder-score"),
|
||||
url(r'player/(?P<username>[\-\.\d\w]+)/dan/$',
|
||||
views.PlayerDanScore.as_view(), name="player-dan-score"),
|
||||
url(r'player/(?P<username>[\-\.\d\w]+)/invalid/$',
|
||||
views.PlayerInvalidScore.as_view(), name="player-invalid-score"),
|
||||
url(r'player/(?P<username>[\-\.\d\w]+)/kyu/$',
|
||||
views.PlayerKyuScore.as_view(), name="player-kyu-score"),
|
||||
url(r'player/(?P<username>[\-\.\d\w]+)/ladder/$',
|
||||
views.PlayerLadderScore.as_view(), name="player-ladder-score"),
|
||||
)
|
||||
|
||||
@@ -11,6 +11,7 @@ from django.views import generic
|
||||
import xlwt
|
||||
|
||||
from events.models import Event
|
||||
from events.views import EventDetailMixin
|
||||
from . import forms, models
|
||||
from membership.models import Membership
|
||||
from utils.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
||||
@@ -30,7 +31,7 @@ kyu_dan_order = {
|
||||
}
|
||||
|
||||
|
||||
class DeleteHanchan(PermissionRequiredMixin, generic.DeleteView):
|
||||
class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin, generic.DeleteView):
|
||||
"""
|
||||
Fragt zuerst nach, ob die Hanchan wirklich gelöscht werden soll.
|
||||
Wir die Frage mit "Ja" beantwortet, wird die die Hanchan gelöscht.
|
||||
@@ -42,10 +43,10 @@ class DeleteHanchan(PermissionRequiredMixin, generic.DeleteView):
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('event-hanchan-list',
|
||||
kwargs={'event': self.object.event.pk})
|
||||
kwargs={'event': self.object.event.pk})
|
||||
|
||||
|
||||
class HanchanForm(PermissionRequiredMixin, generic.UpdateView):
|
||||
class HanchanForm(EventDetailMixin, PermissionRequiredMixin, generic.UpdateView):
|
||||
"""
|
||||
Ein Formular um eine neue Hanchan anzulegen, bzw. eine bestehende zu
|
||||
bearbeitsen
|
||||
@@ -130,7 +131,7 @@ class HanchanForm(PermissionRequiredMixin, generic.UpdateView):
|
||||
return self.form_invalid(form, formset)
|
||||
|
||||
|
||||
class EventHanchanList(generic.ListView):
|
||||
class EventHanchanList(EventDetailMixin, generic.ListView):
|
||||
"""
|
||||
Auflistung aller Hanchan die während der Veranstaltung gespielt wurden.
|
||||
"""
|
||||
@@ -146,13 +147,8 @@ class EventHanchanList(generic.ListView):
|
||||
except models.Event.DoesNotExist:
|
||||
raise django.http.Http404(_('Event does not exist'))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.ListView.get_context_data(self, **kwargs)
|
||||
context['event'] = self.event
|
||||
return context
|
||||
|
||||
|
||||
class EventRankingList(generic.ListView):
|
||||
class EventRankingList(EventDetailMixin, generic.ListView):
|
||||
"""
|
||||
Anzeige des Eventrankings, daß erstellt wurde falls der Termin als internes
|
||||
Turnier markiert wurde.
|
||||
@@ -168,11 +164,6 @@ class EventRankingList(generic.ListView):
|
||||
except models.Event.DoesNotExist:
|
||||
raise django.http.Http404(_('Event does not exist'))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.ListView.get_context_data(self, **kwargs)
|
||||
context['event'] = self.event
|
||||
return context
|
||||
|
||||
|
||||
class KyuDanRankingList(generic.ListView):
|
||||
"""
|
||||
@@ -183,7 +174,8 @@ class KyuDanRankingList(generic.ListView):
|
||||
paginate_by = 25
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.order_by = kyu_dan_order[kwargs.get('order_by', self.default_order)] # @IgnorePep8
|
||||
self.order_by = kyu_dan_order[
|
||||
kwargs.get('order_by', self.default_order)] # @IgnorePep8
|
||||
return generic.ListView.dispatch(self, request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -202,7 +194,8 @@ class LadderRankingList(generic.ListView):
|
||||
def get_queryset(self):
|
||||
try:
|
||||
if self.kwargs.get('season'):
|
||||
self.season = models.LadderSeason.objects.get(pk=self.kwargs['season'])
|
||||
self.season = models.LadderSeason.objects.get(
|
||||
pk=self.kwargs['season'])
|
||||
self.is_archive = True
|
||||
elif self.kwargs.get('is_archive'):
|
||||
self.season = models.LadderSeason.objects.order_by('-pk')[1]
|
||||
@@ -211,16 +204,18 @@ class LadderRankingList(generic.ListView):
|
||||
self.season = models.LadderSeason.objects.current()
|
||||
except models.LadderSeason.DoesNotExist:
|
||||
raise django.http.Http404(_('Season does not exist'))
|
||||
queryset = models.LadderRanking.objects.filter(season=self.season, placement__isnull=False).select_related()
|
||||
queryset = models.LadderRanking.objects.filter(season=self.season,
|
||||
placement__isnull=False).select_related()
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.ListView.get_context_data(self, **kwargs)
|
||||
context['is_archive'] = self.is_archive
|
||||
context['season'] = self.season
|
||||
context['season_archive'] = models.LadderSeason.objects.all()
|
||||
context['latest_hanchan_list'] = models.Hanchan.objects.filter(valid=True)[:5]
|
||||
context['latest_event_list'] = Event.objects.archive()[:5]
|
||||
context['season_list'] = models.LadderSeason.objects.all()
|
||||
context['latest_hanchan_list'] = models.Hanchan.objects.filter(
|
||||
valid=True)[:3]
|
||||
context['latest_event_list'] = Event.objects.archive()[:3]
|
||||
return context
|
||||
|
||||
|
||||
@@ -238,11 +233,13 @@ class LadderRankingExcel(generic.View):
|
||||
response = django.http.HttpResponse(mimetype=u'application/msexcel')
|
||||
|
||||
filename = urllib.quote(self.filename.encode('utf-8'))
|
||||
response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % filename
|
||||
response[
|
||||
'Content-Disposition'] = "attachment; filename*=UTF-8''%s" % filename
|
||||
|
||||
field_names = [u"Login", u"Name", u"E-Mail", u"Telefon", u"Handy", u"Geburtstag",
|
||||
u"T-Shirt", u"Teams", u"Gr.", u"Kg", u"Notfall Adresse",
|
||||
u"Notfall Nummer", u"Notizen", ]
|
||||
field_names = [u"Login", u"Name", u"E-Mail", u"Telefon", u"Handy",
|
||||
u"Geburtstag",
|
||||
u"T-Shirt", u"Teams", u"Gr.", u"Kg", u"Notfall Adresse",
|
||||
u"Notfall Nummer", u"Notizen", ]
|
||||
|
||||
|
||||
# Erstelle ein Workbook (Das ist eine Excel Datei)
|
||||
@@ -253,7 +250,8 @@ class LadderRankingExcel(generic.View):
|
||||
sheet = workbook.add_sheet('Mitglieder', cell_overwrite_ok=False)
|
||||
sheet.set_panes_frozen(True)
|
||||
sheet.set_horz_split_pos(1) # in general, freeze after last heading row
|
||||
sheet.set_remove_splits(True) # if user does unfreeze, don't leave a split there
|
||||
sheet.set_remove_splits(
|
||||
True) # if user does unfreeze, don't leave a split there
|
||||
for column in range(0, len(field_names)):
|
||||
sheet.write(0, column, field_names[column], style=header)
|
||||
|
||||
@@ -267,13 +265,17 @@ class LadderRankingExcel(generic.View):
|
||||
profile = user.get_profile()
|
||||
self.set_col(sheet, current_row, 3, profile.telephone or None)
|
||||
self.set_col(sheet, current_row, 4, profile.mobilephone or None)
|
||||
self.set_col(sheet, current_row, 5, profile.birthday or None, style=self.date)
|
||||
self.set_col(sheet, current_row, 6, profile.shirt_size or None, style=self.center)
|
||||
self.set_col(sheet, current_row, 5, profile.birthday or None,
|
||||
style=self.date)
|
||||
self.set_col(sheet, current_row, 6, profile.shirt_size or None,
|
||||
style=self.center)
|
||||
self.set_col(sheet, current_row, 7, self.get_other_teams(user))
|
||||
self.set_col(sheet, current_row, 8, profile.height or None)
|
||||
self.set_col(sheet, current_row, 9, profile.weight or None)
|
||||
self.set_col(sheet, current_row, 10, profile.emergency_contact or None)
|
||||
self.set_col(sheet, current_row, 11, profile.emergency_phone or None)
|
||||
self.set_col(sheet, current_row, 10,
|
||||
profile.emergency_contact or None)
|
||||
self.set_col(sheet, current_row, 11,
|
||||
profile.emergency_phone or None)
|
||||
except Membership.DoesNotExist:
|
||||
pass
|
||||
current_row += 1
|
||||
@@ -290,17 +292,22 @@ class PlayerScore(LoginRequiredMixin, generic.ListView):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
try:
|
||||
self.user = auth.get_user_model().objects.get(username=self.kwargs.get('username'))
|
||||
self.membership = Membership.objects.get_or_create(user=self.user)[0]
|
||||
self.user = auth.get_user_model().objects.get(
|
||||
username=self.kwargs.get('username'))
|
||||
self.membership = Membership.objects.get_or_create(user=self.user)[
|
||||
0]
|
||||
except auth.get_user_model().DoesNotExist:
|
||||
raise django.http.Http404(_("No user found matching the name %s") % self.kwargs.get('username'))
|
||||
raise django.http.Http404(
|
||||
_("No user found matching the name %s") % self.kwargs.get(
|
||||
'username'))
|
||||
return generic.ListView.get(self, request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = generic.ListView.get_context_data(self, **kwargs)
|
||||
context['membership'] = self.membership
|
||||
try:
|
||||
context['kyu_dan_ranking'] = models.KyuDanRanking.objects.get(user=self.user)
|
||||
context['kyu_dan_ranking'] = models.KyuDanRanking.objects.get(
|
||||
user=self.user)
|
||||
except models.KyuDanRanking.DoesNotExist:
|
||||
context['ranking'] = None
|
||||
try:
|
||||
@@ -312,7 +319,6 @@ class PlayerScore(LoginRequiredMixin, generic.ListView):
|
||||
return context
|
||||
|
||||
|
||||
|
||||
class PlayerDanScore(PlayerScore):
|
||||
template_name = 'mahjong_ranking/player_dan_score.html'
|
||||
|
||||
@@ -339,16 +345,20 @@ class PlayerLadderScore(PlayerScore):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = PlayerScore.get_context_data(self, **kwargs)
|
||||
season_list = models.LadderRanking.objects.filter(user=self.user).select_related('user')
|
||||
season_list = models.LadderRanking.objects.filter(
|
||||
user=self.user).select_related('user')
|
||||
season_list = season_list.values_list('id', 'season__name')
|
||||
context['season'] = self.season
|
||||
context['seasons_select_form'] = forms.SeasonSelectForm(user=self.user)
|
||||
context['seasons_select_field'] = django.forms.ChoiceField(choices=season_list)
|
||||
context['seasons_select_field'] = django.forms.ChoiceField(
|
||||
choices=season_list)
|
||||
return context
|
||||
|
||||
def get_queryset(self, **kwargs):
|
||||
if self.request.GET.get('season'):
|
||||
self.season = models.LadderSeason.objects.get(pk=self.request.GET['season'])
|
||||
self.season = models.LadderSeason.objects.get(
|
||||
pk=self.request.GET['season'])
|
||||
else:
|
||||
self.season = models.LadderSeason.objects.current()
|
||||
return models.Player.objects.ladder_hanchans(user=self.user, season=self.season)
|
||||
return models.Player.objects.ladder_hanchans(user=self.user,
|
||||
season=self.season)
|
||||
|
||||
@@ -3,6 +3,6 @@ import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "kasu.settings")
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
@@ -5,10 +5,11 @@ Created on 19.09.2011
|
||||
"""
|
||||
# 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
|
||||
|
||||
from membership.models import Membership, ActivationRequest
|
||||
|
||||
|
||||
def activate_user(modeladmin, request, queryset):
|
||||
for activation in queryset:
|
||||
@@ -45,7 +46,8 @@ class MembershipAdmin(admin.ModelAdmin):
|
||||
list_display_links = ('nickname',)
|
||||
fieldsets = (
|
||||
(None, {
|
||||
'fields': ('gender', ('first_name', 'last_name'), ('email', 'website'))
|
||||
'fields': (
|
||||
'gender', ('first_name', 'last_name'), ('email', 'website'))
|
||||
}),
|
||||
(_('Membership'), {
|
||||
'classes': ('collapse',),
|
||||
|
||||
@@ -3,11 +3,12 @@ Created on 03.10.2011
|
||||
|
||||
@author: Christian
|
||||
"""
|
||||
from . import models
|
||||
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
|
||||
|
||||
@@ -26,25 +27,29 @@ class MembershipForm(forms.ModelForm):
|
||||
)
|
||||
|
||||
def clean_birthday(self):
|
||||
if self.cleaned_data['membership'] and not self.cleaned_data['birthday']:
|
||||
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']:
|
||||
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']:
|
||||
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']:
|
||||
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']
|
||||
@@ -103,7 +108,8 @@ class RegistrationForm(forms.ModelForm):
|
||||
"""
|
||||
Validate that the supplied email address is unique for the site.
|
||||
"""
|
||||
if auth.get_user_model().objects.filter(email__iexact=self.cleaned_data['email']):
|
||||
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']
|
||||
@@ -139,6 +145,7 @@ class RegistrationForm(forms.ModelForm):
|
||||
user.is_active = False
|
||||
if commit:
|
||||
user.save()
|
||||
activation = models.ActivationRequest.objects.create_pending_registration(user)
|
||||
activation = models.ActivationRequest.objects.create_pending_registration(
|
||||
user)
|
||||
self.send_email(activation)
|
||||
return user
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from membership.models import ActivationRequest
|
||||
|
||||
|
||||
|
||||
@@ -8,11 +8,12 @@ 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
|
||||
from optparse import make_option
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@@ -45,7 +46,8 @@ class Command(BaseCommand):
|
||||
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)
|
||||
self.stderr.write(
|
||||
'Session "%s" has no registed User' % session_key)
|
||||
continue
|
||||
if options['delete']:
|
||||
self.stdout.write('deleting %s' % user.username)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from . import PAID_MEMBERSHIP_GROUP
|
||||
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
|
||||
@@ -10,10 +13,10 @@ 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 . import PAID_MEMBERSHIP_GROUP
|
||||
from utils import OverwriteStorage
|
||||
import random
|
||||
import hashlib
|
||||
|
||||
|
||||
GENDER_CHOICES = (
|
||||
('m', _('Male')),
|
||||
@@ -150,7 +153,8 @@ class MembershipManager(models.Manager):
|
||||
if user:
|
||||
return super(MembershipManager, self).get(user=user)
|
||||
elif username:
|
||||
return super(MembershipManager, self).get(user__username=username)
|
||||
return super(MembershipManager, self).get(
|
||||
user__username=username)
|
||||
except Membership.DoesNotExist:
|
||||
if username:
|
||||
user = get_user_model().objects.get(username=username)
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
{% block teaser %}<h2>{% trans 'profile for' %} {{membership.user.username|title}}</h2>{% endblock %}
|
||||
|
||||
{% block navigation %}
|
||||
{% if kyu_dan_ranking %}
|
||||
<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>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
|
||||
@@ -1,28 +1,39 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load i18n fieldset_extras %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block title %}{% trans "Registration"%}{% endblock %}
|
||||
{% block teaser%}
|
||||
<h1>{% trans "Registration"%}</h1>
|
||||
<div id="teaser_text">
|
||||
</div>
|
||||
{% 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' %}">
|
||||
{% 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>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
|
||||
|
||||
|
||||
@@ -27,21 +27,4 @@ urlpatterns = patterns(
|
||||
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'),
|
||||
)
|
||||
"""
|
||||
)
|
||||
@@ -9,7 +9,6 @@ from django.contrib.auth import get_user_model
|
||||
|
||||
from mahjong_ranking.models import KyuDanRanking, LadderRanking, LadderSeason
|
||||
from utils import mixins
|
||||
|
||||
from . import forms, models
|
||||
|
||||
|
||||
@@ -49,7 +48,8 @@ class ActivateRegistration(generic.DetailView):
|
||||
|
||||
def login(self, user):
|
||||
backend = auth.get_backends()[0]
|
||||
user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__) # @IgnorePep8
|
||||
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.'))
|
||||
@@ -81,7 +81,8 @@ class MembershipDetail(mixins.LoginRequiredMixin, generic.DetailView):
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
if self.kwargs.get('username'):
|
||||
user = get_object_or_404(get_user_model(), username=self.kwargs['username'])
|
||||
user = get_object_or_404(get_user_model(),
|
||||
username=self.kwargs['username'])
|
||||
return models.Membership.objects.get_or_create(user=user)[0]
|
||||
elif self.request.user.is_authenticated():
|
||||
return models.Membership.objects.get(user=self.request.user)
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}404 - Seite nicht gefunden{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
<h1 class="grid_12">404 - Nōten!</h1>
|
||||
<p class="grid_12">Unter der Adresse <em>{{request.path}}</em> wohnt zur Zeit kein Inhalt.<br />
|
||||
Unsere Empfehlung: Gehe zurück zum <a href="/">Start</a>.</p>
|
||||
|
||||
|
||||
<div class="grid_6">
|
||||
<h2>Ursachen</h2>
|
||||
<p>Tote Links sind ein bislang ungelöstes Problem des Hypertextes und im Kern darauf zurückzuführen, dass das Internet ein dezentrales Netzwerk ist. Daher kann die Integrität desselbigen niemals sichergestellt werden.</p>
|
||||
<p>Die Gründe für das Existieren von toten Links können sehr unterschiedlich sein und vor allem an sehr unterschiedlichen Stellen auftreten. In der Praxis häufig anzutreffende Gründe sind unter anderem:</p>
|
||||
<ul>
|
||||
<li>Die Datei, auf die die URL verweist, ist verschoben, umbenannt oder gelöscht worden.</li>
|
||||
<li>Der Link auf der Ursprungseite ist fehlerhaft angegeben, beispielsweise bei der Groß- oder Kleinschreibung im URL-Bestandteil (URLs sind case sensitive) nach der Angabe des Domainnamens.</li>
|
||||
<li>Die gesuchte Domain ist neu bei einem Registrar vergeben worden und die Website selber noch ohne jeden Inhalt.</li>
|
||||
<li>Der Webserver ist nicht (mehr) erreichbar, beispielsweise wegen Netzproblemen oder weil der Rechner abgeschaltet ist.</li>
|
||||
<li>Der Host- bzw. Domainname der URL existiert nicht (mehr) oder kann nicht im DNS aufgelöst werden.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="grid_6">
|
||||
<h2>Lösungsmöglichkeiten</h2>
|
||||
<p>Eine Möglichkeit, die allerdings eher die Symptome der Dead Links bekämpft, ist die <a href="http://de.wikipedia.org/wiki/Internet_Archive">Wayback Machine</a> des Internetarchivs. Durch chronologische Kopien von Webseiten kann Zugriff auf teilweise längst nicht mehr im Original vorhandene Webseiten gewährt werden.</p>
|
||||
<p>Ursachenbekämpfung betreiben hingegen Gremien wie das W3C mit der Kampagne Cool URIs don't change[1], die in der Öffentlichkeit das Bewusstsein für die Notwendigkeit persistenter URLs wecken. Zudem gibt es bereits Systeme, die das Ziel von unabänderlichen URLs verfolgen, beispielsweise das des Persistent Uniform Resource Locators.</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,205 +0,0 @@
|
||||
{% load i18n %}
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kasu - {% block title %}{{ current_top_page.menu_name|default:"traditionelle asiatische Spielkultur"}}{% endblock %}</title>
|
||||
<meta name="keywords" content="{% block keywords %}{% endblock %}"/>
|
||||
<meta name="description" content="{% block description %}{% endblock %}"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="shortcut icon" href="{{STATIC_URL}}img/favicon.ico">
|
||||
<link rel="stylesheet" media="all" href="{{STATIC_URL}}css/common.css" type="text/css">
|
||||
<link rel="stylesheet" media="print" href="{{STATIC_URL}}css/print.css" type="text/css">
|
||||
<link rel="stylesheet" media="screen and (min-width: 701px)" href="{{STATIC_URL}}css/desktop.css" type="text/css">
|
||||
<link rel="stylesheet" media="screen and (max-width: 700px)" href="{{STATIC_URL}}css/mobile.css" type="text/css">
|
||||
<!--[if lt IE 9]>
|
||||
<link rel="stylesheet" media="screen" href="{{STATIC_URL}}css/desktop.css" type="text/css">
|
||||
<script type="text/javascript">
|
||||
document.createElement('header');
|
||||
document.createElement('nav');
|
||||
document.createElement('section');
|
||||
document.createElement('article');
|
||||
document.createElement('aside');
|
||||
document.createElement('footer');
|
||||
document.createElement('hgroup');
|
||||
</script>
|
||||
<![endif]-->
|
||||
<link rel="alternate" type="application/rss+xml" title="{% trans 'Current News' %}"
|
||||
href="{% url 'feed-latest-news' %}"/>
|
||||
<link rel="alternate" type="application/rss+xml" title="{% trans 'Recent Comments' %}"
|
||||
href="{% url 'feed-latest-comments' %}"/>
|
||||
{% block opengraph %}
|
||||
<meta property="og:type" content="website"/>
|
||||
<meta property="og:title" content="Kasu - Verein Japanische Spielekultur"/>
|
||||
<meta property="og:url" content="http://www.kasu.at{{ request.path_info }}"/>
|
||||
<meta property="og:image" content="http://www.kasu.at/static/img/logo.png"/>
|
||||
{% endblock %}
|
||||
{% block extra_head %}{% endblock %}
|
||||
</head>
|
||||
<body {% block itemscope %}{% endblock %}>
|
||||
<div id="header_bg"></div>
|
||||
|
||||
<header id="siteheader">
|
||||
<h1 id="sitelogo"><a href="/index.html">Kasu - traditionelle asiatische Spielkultur</a></h1>
|
||||
<nav id="mainnav">
|
||||
<input type="checkbox" id="toggle"/>
|
||||
<label for="toggle" class="toggle" onclick>{% trans "Menu" %}</label>
|
||||
<ul class="main_menu">
|
||||
{% for item in top_menu_items %}
|
||||
<li><a href="{{item.get_absolute_url}}" title="{{ item.title }}"
|
||||
class="{%if item.active %}active{% endif %}">{{item.menu_name}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<section id="jumbotron"
|
||||
style="background-image: url('{% block jumbotron_background %}{{STATIC_URL}}img/teaser/{{current_top_page.slug}}.jpg{% endblock %}')">
|
||||
{% block teaser %}
|
||||
<h2>{{page.title}} {{title}}</h2>
|
||||
{% endblock %}
|
||||
<aside id="redbox">
|
||||
{% block redbox %}
|
||||
{% if current_event %}
|
||||
<h2>{% trans "Current Event" %}</h2>
|
||||
|
||||
<h3>{{ current_event.name}}</h3>
|
||||
|
||||
<p><img src="{{ STATIC_URL }}icons/date.png"/> {% trans "since" %}
|
||||
<time datetime="{{current_event.start|date:'c'}}">{{current_event.start|timesince}}</time>
|
||||
</p>
|
||||
<ul class="list">
|
||||
<li class="event"><strong>{% trans "Start" %}:</strong> {{current_event.start|date:'DATETIME_FORMAT'}}</li>
|
||||
<li class="location"><strong>{% trans "Location" %}:</strong> {{ current_event.location }}</li>
|
||||
</ul>
|
||||
<div style="text-align:right"><a class="button" href="{{current_event.get_absolute_url}}">{% trans "More Details" %}
|
||||
<img src="{{ STATIC_URL }}icons/date_go.png" alt="»" width="16" height="16"/></a></div>
|
||||
{% else %}
|
||||
<h2>{% trans "Next Event" %}</h2>
|
||||
|
||||
<h3>{{ next_event.name}}</h3>
|
||||
|
||||
<p><img src="{{ STATIC_URL }}icons/date.png" alt="" width="16" height="16"/> {% trans "in" %}
|
||||
<time datetime="{{next_event.start|date:'c'}}">{{next_event.start|timeuntil}}</time>
|
||||
</p>
|
||||
<ul class="list">
|
||||
<li class="event"><strong>{% trans "Start" %}:</strong> {{next_event.start|date:'DATETIME_FORMAT' }}</li>
|
||||
<li class="location"><strong>{% trans "Location" %}:</strong> {{ next_event.location }}</li>
|
||||
</ul>
|
||||
<div style="text-align:right"><a class="button" href="{{next_event.get_absolute_url}}">{% trans "More Details" %}
|
||||
<img src="{{ STATIC_URL }}icons/date_go.png" alt="»" width="16" height="16"/></a></div>
|
||||
{% endif %}
|
||||
<h3 class="clearfix">{% trans "Upcoming events" %} <a href="{% url 'events-ical' %}"><img
|
||||
src="{{STATIC_URL}}img/ical_feed.gif" alt="iCal Feed" title="iCal Feed" width="16" height="16"/></a>
|
||||
</h3>
|
||||
<ul class="list">
|
||||
{% for event in upcoming_events %}
|
||||
<li class="event">{{event.start|date:'d. M:'}} <a href="{{ event.get_absolute_url}}">{{event.name}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
{% block navigation %}
|
||||
{% if current_top_page.subpages.count %}
|
||||
<ul id="navigation">
|
||||
<li><a href="{{current_top_page.get_absolute_url}}"
|
||||
class="{% ifequal current_page current_top_page %}{% endifequal %}">{{current_top_page.menu_name}}</a></li>
|
||||
{% for subpage in current_top_page.subpages.all %}
|
||||
<li><a href="{{subpage.get_absolute_url}}" class="{% ifequal subpage current_page %}active{% endifequal %}">{{subpage.menu_name}}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% block additional_nav_elements %}{% endblock %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block messages %}
|
||||
{% if messages %}
|
||||
<ul id="messages">
|
||||
{% for message in messages %}
|
||||
<li class="{{ message.tags }}">{{ message }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
<div id="maincontent">
|
||||
{% block maincontent %}
|
||||
<div id="content">{% block content %}{% endblock %}</div>
|
||||
<aside id="sidebar">{% block sidebar %}{% endblock %}</aside>
|
||||
{% endblock %}
|
||||
{% block paginator %}
|
||||
{% if page_obj.has_other_pages %}{% include 'paginator.html' %}{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block comments %}{% endblock %}
|
||||
<br class="clear" />
|
||||
<p id="bottom_buttonbar" class="buttonbar">
|
||||
{% block buttonbar %}
|
||||
{% if current_page and perms.content.change_page %}<a href="{% url 'edit-page' current_page.path %}" class="button"><img
|
||||
src="{{ STATIC_URL }}icons/page_edit.png" alt=""/> {% trans "Edit Page" %}</a>{% endif %}
|
||||
{% if current_page and perms.content.add_page %}<a href="{% url 'add-page' current_page.path %}" class="button"><img
|
||||
src="{{ STATIC_URL }}icons/page_add.png" alt=""/> {% trans "Add Subpage" %}</a>{% endif %}
|
||||
{% block additional_buttonbar %}{% endblock %}
|
||||
{% endblock %}
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div id="footer_bg"> </div>
|
||||
<footer id="footer">
|
||||
<p><strong>Herausgeber:</strong> Verein Kasu - traditionelle asiatische Spielkultur (<a
|
||||
href="/verein/impressum.html">{% trans "imprint" %}</a> – <a
|
||||
href='mailto:ve%72e%69n@%6B%61su.a%74'>{% trans "contact" %}</a>)
|
||||
</p>
|
||||
|
||||
<form action="/i18n/setlang/" method="post" style="text-align:right;">
|
||||
{% csrf_token %}
|
||||
<label for="language">{% trans "Language" %}:</label>
|
||||
<select name="language" id="language">
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% for language in languages %}
|
||||
<option value="{{language.code}}" {% ifequal language.code LANGUAGE_CODE %}
|
||||
selected="selected" {% endifequal %}>{{ language.name_local }} ({{ language.code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="submit">{% trans "Go" %}</button>
|
||||
</form>
|
||||
</footer>
|
||||
<nav id="usernav">
|
||||
{% if user.is_authenticated %}
|
||||
{% trans "Logged in as" %}:
|
||||
<a rel="nofollow" href="{% url 'membership-details' user.username %}">{{user.username}}</a> -
|
||||
{% if user.is_staff %}<a href="/admin">{% trans "Admin" %}</a>{% endif %}
|
||||
<a rel="nofollow" href="{% url 'logout' %}?next={{ request.path_info }}">{% trans "Logout" %}</a>
|
||||
{% else %}
|
||||
{% trans "no user logged in" %} -
|
||||
<a rel="nofollow" href="{% url 'membership-register' %}">{% trans "register" %}</a>
|
||||
<a rel="nofollow" href="{% url 'login' %}?next={{ request.path_info }}">{% trans "login" %}</a>
|
||||
<a rel="nofollow" href="{% url 'social:begin' 'facebook' %}"><img src="{{STATIC_URL}}img/facebook.png"
|
||||
alt="Facebook Login" title="Login with Facebook"
|
||||
width="26" height="26"/></a>
|
||||
<a rel="nofollow" href="{% url 'social:begin' 'twitter' %}"><img src="{{STATIC_URL}}img/twitter.png"
|
||||
alt="Twitter Login" title="Login with Twitter"
|
||||
width="26" height="26"/></a>
|
||||
<a rel="nofollow" href="{% url 'social:begin' 'google-oauth2' %}"><img src="{{STATIC_URL}}img/google.png"
|
||||
alt="Google Login" title="Login with Google"
|
||||
width="26" height="26"/></a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
<script type="text/javascript">
|
||||
{% block javascript %}{% endblock %}
|
||||
var pkBaseURL = (("https:" == document.location.protocol) ? "https://admin.animanga.at/tools/piwik/" : "http://admin.animanga.at/tools/piwik/");
|
||||
document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
|
||||
try {
|
||||
var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", 5);
|
||||
piwikTracker.trackPageView();
|
||||
piwikTracker.enableLinkTracking();
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
</script>
|
||||
<noscript><p><img src="http://admin.animanga.at/tools/piwik/piwik.php?idsite=5" style="border:0" alt=""/></p></noscript>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,51 +0,0 @@
|
||||
{% load comments i18n %}
|
||||
<form action="{% comment_form_target %}" method="post" id='comment_form' class="grid_12">
|
||||
{% csrf_token %}
|
||||
<fieldset class="comment">
|
||||
<legend>{% trans "New Comment" %}</legend>
|
||||
{% if user.is_authenticated %}
|
||||
<p style="display:none">
|
||||
{{form.honeypot}} {{form.honeypot.label}}
|
||||
{% for field in form.hidden_fields %}{{ field }}{% endfor %}
|
||||
<input type="hidden" name="next" value="{{ next|default:request.path_info }}"/>
|
||||
</p>
|
||||
<div class="comment">
|
||||
<div class="comment_picture"><img class="avatar"
|
||||
src="{% if user.get_profile.thumbnail %}{{user.get_profile.thumbnail.url}}{% else %}{{STATIC_URL}}img/unknown_thumbnail.png{% endif %}"
|
||||
alt=""/></div>
|
||||
<header class="comment_header">
|
||||
<h3><a href="{{ user.get_profile.get_absolute_url }}" class="user">{{user}}</a></h3>
|
||||
|
||||
<p class="submit_date">{% trans 'now' %}</p>
|
||||
</header>
|
||||
<div class="comment_text">{{form.comment}}</div>
|
||||
</div>
|
||||
<p class="buttonbar">
|
||||
<button type="submit" name="preview"><img src="{{ STATIC_URL }}icons/comment_edit.png"
|
||||
alt="{% trans 'Preview' %}"/> {% trans "Preview" %}
|
||||
</button>
|
||||
<button type="submit" name="submit"><img src="{{ STATIC_URL }}icons/comment_add.png"
|
||||
alt="{% trans 'Post' %}"/> {% trans "Post" %}
|
||||
</button>
|
||||
</p>
|
||||
{% else %}
|
||||
<div class="comment">
|
||||
<div class="comment_picture"><img class="avatar"
|
||||
src="{{STATIC_URL}}img/unknown_thumbnail.png"
|
||||
alt=""/></div>
|
||||
<header class="comment_header">
|
||||
<h3>{% trans "not logged in" %}</h3>
|
||||
</header>
|
||||
<div class="comment_text">
|
||||
<textarea disabled="disabled" cols="40" rows="10">
|
||||
{% blocktrans %}Register now, or Login to leave a comment here.{% endblocktrans %}
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<p class="buttonbar">
|
||||
<a href="{% url 'membership-register' %}" class="button"><img src="{{STATIC_URL}}icons/user_add.png" alt=""/> {% trans 'register' %}</a>
|
||||
<a href="{% url 'login' %}?next={{ request.path_info|urlencode }}" class="button"><img src="{{STATIC_URL}}icons/lock_break.png" alt=""/> {% trans 'login' %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -1,18 +0,0 @@
|
||||
{% load i18n django_markdown %}
|
||||
<h2 class="grid_12" id="comments">{% trans 'Comments' %}</h2>
|
||||
{% for comment in comment_list %}
|
||||
<article id="c{{ comment.id }}" class="comment">
|
||||
<div class="comment_picture">
|
||||
<img class="avatar" src="{% if comment.user.get_profile.thumbnail %}
|
||||
{{comment.user.get_profile.thumbnail.url}}
|
||||
{% else %}
|
||||
{{STATIC_URL}}img/unknown_thumbnail.png
|
||||
{% endif %}" alt=""/>
|
||||
</div>
|
||||
<header class="comment_header">
|
||||
<h3><a href="{{ comment.user.get_profile.get_absolute_url }}" class="user">{{comment.user}}</a></h3>
|
||||
<time datetime="{{comment.submit_date|date:'Y-m-d'}}" class="submit_date">{{comment.submit_date|timesince}}</time>
|
||||
</header>
|
||||
<div class="comment_text">{{comment.comment|markdown_safe}}</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
@@ -1,8 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Thank you for your comment" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Thank you for your comment" %}!</h1>
|
||||
{% endblock %}
|
||||
@@ -1,29 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n django_markdown %}
|
||||
|
||||
{% block title %}{% trans "Preview your comment" %}{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
{% if form.errors %}
|
||||
<h2 class="grid_12">{% blocktrans count form.errors|length as counter %}Please correct the error below{% plural %}Please correct the errors below{% endblocktrans %}</h2>
|
||||
{% else %}
|
||||
<h2 class="grid_12">{% trans "Preview your comment" %}</h2>
|
||||
<article id="c{{ comment.id }}" class="comment">
|
||||
<div class="comment_picture">
|
||||
<img class="avatar" src="{% if user.get_profile.thumbnail %}
|
||||
{{user.get_profile.thumbnail.url}}
|
||||
{% else %}
|
||||
{{STATIC_URL}}img/unknown_thumbnail.png
|
||||
{% endif %}" alt=""/>
|
||||
</div>
|
||||
<header class="comment_header">
|
||||
<a href="{{ user.get_profile.get_absolute_url }}" class="user">{{comment.user}}</a>
|
||||
<div class="submit_date"><time time="{% now 'c' %}">{% now 'DATETIME_FORMAT' %}</time></div>
|
||||
</header>
|
||||
<div class="comment_text">{{comment|markdown_safe}}</div>
|
||||
</article>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% include "comments/form.html" %}
|
||||
{% endblock %}
|
||||
@@ -1,9 +0,0 @@
|
||||
{% for field in form.visible_fields %}
|
||||
<div>
|
||||
<label for="id_{{ field.html_name}}" class="field_name {{ field.css_classes }}">{{ field.label }}</label>
|
||||
{{ field }}
|
||||
{%if field.help_text %}<p class="help_text">{{field.help_text}}</p>{% endif %}
|
||||
{%if field.errors %}{{ field.errors }}{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}
|
||||
@@ -1 +0,0 @@
|
||||
google-site-verification: google25dabc1a49a9ef03.html
|
||||
@@ -1,115 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n comments%}
|
||||
|
||||
{% block jumbotron_background %}{{ random_photo.url }}{% endblock %}
|
||||
|
||||
{% block teaser %}
|
||||
<h2>{{title}}</h2>
|
||||
<div id="teaser_text">{{content}}</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block redbox %}
|
||||
{% if current_event %}
|
||||
<h2>{% trans "Current Event" %}</h2>
|
||||
<h3>{{ current_event.name}}</h3>
|
||||
<p><img src="{{ STATIC_URL }}icons/date.png"/> {% trans "since" %}
|
||||
<time datetime="{{current_event.start|date:'Y-m-d H:i'}}">{{current_event.start|timesince}}</time>
|
||||
</p>
|
||||
<ul class="list">
|
||||
<li class="event"><strong>{% trans "Start" %}:</strong> {{current_event.start|date:'DATETIME_FORMAT'}}</li>
|
||||
<li class="location"><strong>{% trans "Location" %}:</strong> {{ current_event.location }}</li>
|
||||
</ul>
|
||||
<div style="text-align:right"><a class="button" href="{{current_event.get_absolute_url}}">{% trans "More Details" %}
|
||||
<img src="{{ STATIC_URL }}icons/date_go.png" alt="»" width="16" height="16"/></a></div>
|
||||
{% else %}
|
||||
<h2>{% trans "Next Event" %}</h2>
|
||||
<h3>{{ next_event.name}}</h3>
|
||||
<p><img src="{{ STATIC_URL }}icons/date.png" alt="" width="16" height="16"/> {% trans "in" %}
|
||||
<time datetime="{{next_event.start|date:'Y-m-d H:i'}}">{{next_event.start|timeuntil}}</time>
|
||||
</p>
|
||||
<ul class="list">
|
||||
<li class="event"><strong>{% trans "Start" %}:</strong> {{next_event.start|date:'DATETIME_FORMAT' }}</li>
|
||||
<li class="location"><strong>{% trans "Location" %}:</strong> {{ next_event.location }}</li>
|
||||
</ul>
|
||||
<div style="text-align:right"><a class="button" href="{{next_event.get_absolute_url}}">{% trans "More Details" %}
|
||||
<img src="{{ STATIC_URL }}icons/date_go.png" alt="»" width="16" height="16"/></a></div>
|
||||
{% endif %}
|
||||
<h3 class="clearfix">{% trans "Upcoming events" %} <a href="{% url 'events-ical' %}"><img
|
||||
src="{{STATIC_URL}}img/ical_feed.gif" alt="iCal Feed" title="iCal Feed" width="16" height="16"/></a></h3>
|
||||
<ul class="list">
|
||||
{% for event in upcoming_events %}
|
||||
<li class="event">{{event.start|date:'d. M:'}} <a href="{{ event.get_absolute_url}}">{{event.name}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
<div class="grid_8">
|
||||
{% for article in recent_article_list %}{% get_comment_count for article as comment_count %}
|
||||
<article class="article">
|
||||
<header>
|
||||
<h2><a href="{{article.get_absolute_url}}">{{article.headline}}</a></h2>
|
||||
<ul class="info">
|
||||
<li><img src="{{STATIC_URL}}icons/date.png" width="16" height="16" alt="{% trans 'Created on' %}"
|
||||
title="{% trans 'Created on' %}"/>
|
||||
<time datetime="{{article.date_created|date:'Y-m-d H:i'}}">{{ article.date_created|date:'DATE_FORMAT' }}</time>
|
||||
</li>
|
||||
<li><img src="{{STATIC_URL}}icons/user_red.png" width="16" height="16" alt="{% trans 'Author' %}"
|
||||
title="{% trans 'Author' %}"/> {{ article.author }}
|
||||
</li>
|
||||
<li><img src="{{STATIC_URL}}icons/comments.png" alt="{% trans 'comments' %}:"
|
||||
title="{% trans 'comments' %}"/> <a href="{{article.get_absolute_url}}#comments">{{comment_count}}
|
||||
{% trans "comments" %}</a></li>
|
||||
</ul>
|
||||
</header>
|
||||
<a href="{{article.get_absolute_url}}"><img src="{{article.posting_image.url}}" alt="{{article.category}}:"
|
||||
class="posting_image"/></a>
|
||||
{{article.content|truncatewords_html:50}}
|
||||
<footer class="more_link"><a href="{{article.get_absolute_url}}" class="button">{% trans "Read More"%} <img
|
||||
src="{{STATIC_URL}}icons/page_go.png" alt="»" width="16" height="16"/></a></footer>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<aside class="grid_4">
|
||||
<section>
|
||||
<h2>{% trans "Recent Comments" %} <a href="{% url 'feed-latest-comments' %}"><img
|
||||
src="{{ STATIC_URL }}img/rss_feed.gif" alt="RSS Feed" title="RSS Feed" width="16" height="16"/></a></h2>
|
||||
<ul class="comment_list">
|
||||
{% for comment in recent_comment_list %}
|
||||
{% url 'membership-details' comment.user.username as user_link%}
|
||||
<li>
|
||||
{% blocktrans with comment.user as author and comment.submit_date|timesince as since and comment.submit_date|date:'c' as submit_date and comment.content_object as object and comment.get_absolute_url as comment_link%}
|
||||
From <a href="{{user_link}}">{{author}}</a> in
|
||||
<a href="{{comment_link}}">“{{object}}”</a>
|
||||
since
|
||||
<time datetime="{{submit_date}}">{{since}}</time>
|
||||
{% endblocktrans %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>{% trans 'Kasu in the social network' %}</h2>
|
||||
|
||||
<p style="text-align: center">
|
||||
<a href="https://www.facebook.com/Kasu.at"><img src="{{STATIC_URL}}img/facebook.png" alt="Facebook"
|
||||
title="{% trans 'Visit us on' %} Facebook"/></a>
|
||||
<a href="https://twitter.com/KasuAustria"><img src="{{STATIC_URL}}img/twitter.png" alt="Twitter"
|
||||
title="{% trans 'Visit us on' %} Twitter"/></a>
|
||||
<a href="https://plus.google.com/u/0/114092233962732014973/"><img src="{{STATIC_URL}}img/google_plus.png"
|
||||
alt="Google+"
|
||||
title="{% trans 'Visit us on' %} Google+"/></a>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
||||
</aside>
|
||||
{% endblock %}
|
||||
|
||||
{% if perms.content.add_article %}{% block addidional_buttonbar %}
|
||||
<a href="{% url 'add-article' %}" class="button"><img src="{{ STATIC_URL }}icons/note_add.png" alt="" width="16"
|
||||
height="16"/>{% trans "Add Article" %}</a>
|
||||
{% endblock %}{% endif %}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{% load i18n %}
|
||||
<div class="paginator grid_12">
|
||||
{% if page_obj.has_previous %}
|
||||
<a class="prev" href="?page={{ page_obj.previous_page_number }}"><img src="{{STATIC_URL}}icons/resultset_previous.png" alt="«" /> {% trans "Prev" %}</a>
|
||||
{% else %}
|
||||
<span class="prev disabled"><img src="{{STATIC_URL}}icons/resultset_previous.png" alt="«" /> {% trans "Prev" %}</span>
|
||||
{% endif %}
|
||||
{% for page in paginator.page_range %}
|
||||
{% ifequal page_obj.number page %}
|
||||
<span class="current">{{page}}</span>
|
||||
{% else %}
|
||||
<a class="page" href="?page={{page}}">{{page}}</a>
|
||||
{% endifequal %}
|
||||
{% endfor %}
|
||||
{% if page_obj.has_next %}
|
||||
<a class="next" href="?page={{ page_obj.next_page_number }}">{% trans "Next" %} <img src="{{STATIC_URL}}icons/resultset_next.png" alt="»" /></a>
|
||||
{% else %}
|
||||
<span class="next disabled">{% trans "Next" %} <img src="{{STATIC_URL}}icons/resultset_next.png" alt="»" /></span>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -1,64 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load url from future %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'login' %}{% endblock %}
|
||||
|
||||
{% block teaser %}<h2>{% trans 'login' %}</h2>{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
|
||||
<h2 class="grid_12">Auf der Seite Anmelden</h2>
|
||||
|
||||
<div class="grid_7">
|
||||
<h2>{% trans "Have you already registered?" %}</h2>
|
||||
{% blocktrans %}
|
||||
<p>As a registered member you can:</p>
|
||||
<ul>
|
||||
<li>leave comments on this page.</li>
|
||||
<li>subscribe to our Newsletter</li>
|
||||
<li>apply to a membership to our club</li>
|
||||
<li>club-members have access to our ranking-system</li>
|
||||
</ul>
|
||||
{% endblocktrans %}
|
||||
{% blocktrans %}
|
||||
<p>You can register here with your Google, or Facebook account.
|
||||
If you don't own such an account, or do not want to use it for authentication,
|
||||
you can fill out our registration form.</p>
|
||||
{% endblocktrans %}
|
||||
<p class="more_link">
|
||||
<a href="{% url 'membership-register' %}" class="button">
|
||||
<img src="{{STATIC_URL}}icons/user_add.png" alt="{%trans 'register' %}"/>
|
||||
{%trans "register"%}</a></p>
|
||||
</div>
|
||||
|
||||
|
||||
{% csrf_token %}
|
||||
<fieldset class="grid_5">
|
||||
<legend>{% trans 'login' %}</legend>
|
||||
{% csrf_token %}
|
||||
{% include 'form.html' %}
|
||||
{% if form.errors %}
|
||||
<p>{% blocktrans %}Your username and password didn't match. Please try again.{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
<input type="hidden" name="next" value="{{next}}" />
|
||||
<p><a href="{% url 'password_reset' %}">{% trans 'Forgot your Password?'%}</a></p>
|
||||
<div class="buttonbar">
|
||||
<button type="submit">
|
||||
<img src="{{STATIC_URL}}icons/lock_break.png" alt="{% trans 'Login' %}" /> {% trans 'Login' %}
|
||||
</button></div>
|
||||
</fieldset>
|
||||
|
||||
|
||||
|
||||
<div class="grid_5" style="text-align: center;">
|
||||
<h2>{% trans "or login with an existing Account" %}</h2>
|
||||
<a rel="nofollow" href="{% url 'social:begin' 'facebook' %}"><img src="{{STATIC_URL}}/img/facebook.png" alt="{% trans 'Login with Facebook' %}" title="{% trans 'Login with Facebook' %}"/></a>
|
||||
<a rel="nofollow" href="{% url 'social:begin' 'twitter' %}"><img src="{{STATIC_URL}}/img/twitter.png" alt="{% trans 'Login with twitter' %}" title="{% trans 'Login with Twitter' %}"/></a>
|
||||
<a rel="nofollow" href="{% url 'social:begin' 'google-oauth2' %}"><img src="{{STATIC_URL}}/img/google.png" width="78" height="78" alt="{% trans 'Login with Google' %}" title="{% trans 'Login with Google' %}"/></a>
|
||||
|
||||
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -1,9 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Password change successful' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{% trans 'Password change successful' %}</h2>
|
||||
<p class="info">{% trans 'Your password was changed.' %}</p>
|
||||
{% endblock %}
|
||||
@@ -1,28 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Password change' %}{% endblock %}
|
||||
|
||||
{% block callout %}
|
||||
<div id="callout">
|
||||
<h1>{% trans 'Password change' %}</h1>
|
||||
<p>{% trans "Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly." %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<fieldset>
|
||||
<legend>Passwort ändern:</legend>
|
||||
<form method="post" action="" class="cmxform">
|
||||
{%csrf_token%}
|
||||
{% if form.errors %}<p class="error"> Es sind Fehler beim ändern des Passwortes aufgetreten</p>{% endif %}
|
||||
{% include 'form.html' %}
|
||||
<div class="buttonbar">
|
||||
<button type="submit">
|
||||
<img src="{{STATIC_URL}}icons/lock_break.png" alt="{% trans 'Login' %}" /> {% trans "Change Password" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,10 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Password reset complete' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{% trans 'Password reset complete' %}</h2>
|
||||
<p>{% trans "Your password has been set. You may go ahead and log in now." %}</p>
|
||||
<p class="more"><a href="{{ login_url }}">{% trans 'Log in' %}</a></p>
|
||||
{% endblock %}
|
||||
@@ -1,32 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Password reset' %}{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
{% if validlink %}
|
||||
<h2 class="grid_12">{% trans 'Enter new password' %}</h2>
|
||||
<p class="grid_6">{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
|
||||
<form action="" method="post" class="grid_5">
|
||||
{% csrf_token %}
|
||||
<fieldset style="margin: -12px 0 0 0">
|
||||
<legend>{% trans 'Change my password' %}</legend>
|
||||
<ul>
|
||||
{%for element in form %}
|
||||
<p class="required">{{element.label_tag}} {{element}}
|
||||
{%if element.help_text %} <img src="{{STATIC_URL}}icons/information.png" title="{{element.help_text}}"> {% endif %}
|
||||
{%if element.errors %}{{element.errors }} {% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p class="buttonbar"><button type="submit"><img src="{{STATIC_URL}}icons/key.png" /> {% trans 'Change my password' %}</button></p>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
|
||||
{% else %}
|
||||
<h2>{% trans 'Password reset unsuccessful' %}</h2>
|
||||
<p class="error">{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,30 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans 'Password reset successful' %}{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
|
||||
<div class="grid_12">
|
||||
<h2>{% trans 'Password reset successful' %}</h2>
|
||||
<p>Wir haben dir eine E-Mail mit Anweisungen zum Zurücksetzen deines Passwort an die angegebene E-Mail-Adresse geschickt.<br />
|
||||
Du solltest diese in Kürze erhalten.</p>
|
||||
</div>
|
||||
|
||||
<h3 class="grid_12">E-Mail nicht angekommen?</h3>
|
||||
<div class="grid_4">
|
||||
<h4>Etwas Warten...</h4>
|
||||
<p>Einige Anti-Spam Techniken <strong>verzögern</strong> den <strong>Empfang</strong> von E-Mails.
|
||||
Bei solchen Postfächern kann der Empfang einzelner Mails <strong>bis zu 30 Minuten</strong> dauern.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid_4">
|
||||
<h4>Junk Mails prüfen</h4>
|
||||
<p>Bitte überprüfe den <em>"Junk"</em> bzw. <em>"SPAM"</em> Ordner deines Postfaches.
|
||||
Automatisch generierte E-Mails werden leider sehr oft <strong>falsch</strong> von <strong>SPAM-Filtern</strong> geschluckt</p>
|
||||
</div>
|
||||
<div class="grid_4">
|
||||
<h4>Vorbei schauen</h4>
|
||||
<p>Bitte Überprüfe die Richigkeit deiner angegebenen <strong>E-Mail Adresse</strong>. Wenn deine angegebne <strong>E-Mail</strong> Adresse <strong>nicht</strong> mehr <strong>aktuell</strong> ist, trete mit uns bitte in <strong>persönlichen Kontakt</strong></p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,34 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Password reset" %}{% endblock %}
|
||||
|
||||
{% block maincontent %}
|
||||
|
||||
<h2 class="grid_12">{% trans "Password reset" %}</h2>
|
||||
<div class="grid_5">
|
||||
|
||||
<p>Du hast dein Passwort vergessen?</p>
|
||||
<p>Macht nichts!<br/>
|
||||
Solange die E-Mail Adresse von dir noch gültig ist, kannst du dein Passwort jederzeit wieder ändern.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<form action="" method="post" class="grid_5">
|
||||
{% csrf_token %}
|
||||
<fieldset style="margin:-12px 0 0 0;">
|
||||
<legend>E-Mail zusenden</legend>
|
||||
{% for element in form %}
|
||||
<p>
|
||||
<label class="required" for="id_{{element.html_name}}">{%if element.help_text %}<img src="{{STATIC_URL}}icons/information.png" title="{{element.help_text}}" /> {% endif %}{{element.label}}</label>
|
||||
{{element}}
|
||||
{%if element.errors %} {{element.errors }} {% endif %}
|
||||
</p>
|
||||
{% endfor %}
|
||||
<br />
|
||||
<p class="buttonbar"><button type="submit"><img src="{{STATIC_URL}}icons/email_go.png" alt="submit"/> Zusenden</button></p>
|
||||
</fieldset>
|
||||
</form>
|
||||
<p class="grid_12">Einfach deine E-Mail-Adresse eingeben und wir senden dir die Anweisungen zum Zurücksetzen deines Passworts.</p>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,4 +0,0 @@
|
||||
User-agent: *
|
||||
Disallow: /cgi-bin/
|
||||
Disallow: /admin/
|
||||
Disallow: /users/
|
||||
@@ -3,11 +3,13 @@ Created on 28.09.2011
|
||||
|
||||
@author: christian
|
||||
"""
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from .countries import COUNTRIES
|
||||
from .html_cleaner import HtmlCleaner
|
||||
from .massmailer import MassMailer
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
|
||||
STATUS_REJECTED, STATUS_WAITING, STATUS_PUBLISHED = -1, 0, 1
|
||||
STATUS_CHOICES = (
|
||||
@@ -22,6 +24,7 @@ class OverwriteStorage(FileSystemStorage):
|
||||
"""
|
||||
Returns same name for existing file and deletes existing file on save.
|
||||
"""
|
||||
|
||||
def _save(self, name, content):
|
||||
if self.exists(name):
|
||||
self.delete(name)
|
||||
|
||||
@@ -3,9 +3,10 @@ Created on 24.11.2011
|
||||
|
||||
@author: christian
|
||||
"""
|
||||
from django import forms
|
||||
import datetime
|
||||
|
||||
from django import forms
|
||||
|
||||
|
||||
class DateInput(forms.widgets.DateInput):
|
||||
input_type = 'date'
|
||||
@@ -13,9 +14,9 @@ class DateInput(forms.widgets.DateInput):
|
||||
|
||||
def __init__(self, attrs=None, **kwargs):
|
||||
forms.widgets.DateInput.__init__(self,
|
||||
attrs=attrs,
|
||||
format='%Y-%m-%d',
|
||||
**kwargs)
|
||||
attrs=attrs,
|
||||
format='%Y-%m-%d',
|
||||
**kwargs)
|
||||
|
||||
|
||||
class NumberInput(forms.widgets.Input):
|
||||
@@ -23,8 +24,7 @@ class NumberInput(forms.widgets.Input):
|
||||
|
||||
|
||||
class TimeInput(forms.widgets.Select):
|
||||
|
||||
def __init__(self, attrs=None,):
|
||||
def __init__(self, attrs=None, ):
|
||||
timeset = datetime.datetime(2000, 1, 1, 0, 0)
|
||||
choices = [('', '-------')]
|
||||
while timeset < datetime.datetime(2000, 1, 2, 0, 0):
|
||||
@@ -37,7 +37,7 @@ class TimeInput(forms.widgets.Select):
|
||||
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
return forms.widgets.Select.render(self, name, value, attrs=attrs,
|
||||
choices=choices)
|
||||
choices=choices)
|
||||
|
||||
|
||||
class SplitDateTimeWidget(forms.widgets.MultiWidget):
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import re
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.http import HttpResponse
|
||||
from django.utils.encoding import smart_unicode, force_unicode
|
||||
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
from django.utils import simplejson as json
|
||||
|
||||
class LookupBase(object):
|
||||
|
||||
class LookupBase(object):
|
||||
@classmethod
|
||||
def name(cls):
|
||||
app_name = cls.__module__.split('.')[-2].lower()
|
||||
@@ -69,7 +72,6 @@ class LookupInvalid(Exception):
|
||||
|
||||
|
||||
class LookupRegistry(object):
|
||||
|
||||
def __init__(self):
|
||||
self._registry = {}
|
||||
|
||||
@@ -82,8 +84,8 @@ class LookupRegistry(object):
|
||||
self.validate(lookup)
|
||||
name = force_unicode(lookup.name())
|
||||
if name in self._registry:
|
||||
raise LookupAlreadyRegistered(u'The name %s is already registered'\
|
||||
% name)
|
||||
raise LookupAlreadyRegistered(u'The name %s is already registered' \
|
||||
% name)
|
||||
self._registry[name] = lookup
|
||||
|
||||
def unregister(self, lookup):
|
||||
|
||||
@@ -48,7 +48,7 @@ class AutoCompleteSelectField(Html5Mixin, django.forms.Field):
|
||||
self.allow_new = kwargs.pop('allow_new', False)
|
||||
if not kwargs['widget']:
|
||||
kwargs['widget'] = self.widget(lookup_class,
|
||||
allow_new=self.allow_new)
|
||||
allow_new=self.allow_new)
|
||||
super(AutoCompleteSelectField, self).__init__(*args, **kwargs)
|
||||
|
||||
def to_python(self, value):
|
||||
@@ -172,9 +172,9 @@ class PhoneField(Html5Mixin, django.forms.CharField):
|
||||
widget = widgets.PhoneInput
|
||||
|
||||
def __init__(self, regex=None, max_length=None, min_length=None,
|
||||
error_message=None, *args, **kwargs):
|
||||
error_message=None, *args, **kwargs):
|
||||
super(PhoneField, self).__init__(max_length, min_length,
|
||||
*args, **kwargs)
|
||||
*args, **kwargs)
|
||||
self._set_regex(u'^[0-9+-/ ]+$')
|
||||
|
||||
def _get_regex(self):
|
||||
@@ -184,7 +184,7 @@ class PhoneField(Html5Mixin, django.forms.CharField):
|
||||
regex = re.compile(regex, re.UNICODE)
|
||||
self._regex = regex
|
||||
if hasattr(self, '_regex_validator') \
|
||||
and self._regex_validator in self.validators:
|
||||
and self._regex_validator in self.validators:
|
||||
self.validators.remove(self._regex_validator)
|
||||
self._regex_validator = validators.RegexValidator(regex=regex)
|
||||
self.validators.append(self._regex_validator)
|
||||
@@ -193,68 +193,68 @@ class PhoneField(Html5Mixin, django.forms.CharField):
|
||||
|
||||
|
||||
class ReCaptchaField(django.forms.fields.CharField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.widget = widgets.ReCaptchaInput
|
||||
self.required = True
|
||||
super(ReCaptchaField, self).__init__(*args, **kwargs)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.widget = widgets.ReCaptchaInput
|
||||
self.required = True
|
||||
super(ReCaptchaField, self).__init__(*args, **kwargs)
|
||||
def _check_recaptcha(self, challenge_value, response_value, remote_ip):
|
||||
"""
|
||||
Submits a reCAPTCHA request for verification.
|
||||
Returns RecaptchaResponse for the request
|
||||
|
||||
def _check_recaptcha(self, challenge_value, response_value, remote_ip):
|
||||
"""
|
||||
Submits a reCAPTCHA request for verification.
|
||||
Returns RecaptchaResponse for the request
|
||||
@param challenge_value: value of recaptcha_challenge_field
|
||||
@param response_value: value of recaptcha_response_field
|
||||
@param remoteip -- the user's ip address
|
||||
|
||||
@param challenge_value: value of recaptcha_challenge_field
|
||||
@param response_value: value of recaptcha_response_field
|
||||
@param remoteip -- the user's ip address
|
||||
"""
|
||||
import urllib
|
||||
import urllib2
|
||||
|
||||
"""
|
||||
import urllib
|
||||
import urllib2
|
||||
private_key = settings.RECAPTCHA_PRIVATE_KEY
|
||||
challenge_value = challenge_value.encode('utf-8')
|
||||
response_value = response_value.encode('utf-8')
|
||||
params = urllib.urlencode({
|
||||
'privatekey': private_key,
|
||||
'remoteip': remote_ip,
|
||||
'challenge': challenge_value,
|
||||
'response': response_value,
|
||||
})
|
||||
private_key = settings.RECAPTCHA_PRIVATE_KEY
|
||||
challenge_value = challenge_value.encode('utf-8')
|
||||
response_value = response_value.encode('utf-8')
|
||||
params = urllib.urlencode({
|
||||
'privatekey': private_key,
|
||||
'remoteip': remote_ip,
|
||||
'challenge': challenge_value,
|
||||
'response': response_value,
|
||||
})
|
||||
|
||||
request = urllib2.Request(
|
||||
url="http://www.google.com/recaptcha/api/verify",
|
||||
data=params,
|
||||
headers={
|
||||
"Content-type": "application/x-www-form-urlencoded",
|
||||
"User-agent": "reCAPTCHA Python"
|
||||
}
|
||||
)
|
||||
httpresp = urllib2.urlopen(request)
|
||||
request = urllib2.Request(
|
||||
url="http://www.google.com/recaptcha/api/verify",
|
||||
data=params,
|
||||
headers={
|
||||
"Content-type": "application/x-www-form-urlencoded",
|
||||
"User-agent": "reCAPTCHA Python"
|
||||
}
|
||||
)
|
||||
httpresp = urllib2.urlopen(request)
|
||||
|
||||
return_values = httpresp.read().splitlines()
|
||||
httpresp.close()
|
||||
return_values = httpresp.read().splitlines()
|
||||
httpresp.close()
|
||||
|
||||
return_code = return_values[0]
|
||||
if (return_code == "true"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return_code = return_values[0]
|
||||
if (return_code == "true"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def clean(self, values):
|
||||
challenge_value = values[0]
|
||||
response_value = values[1]
|
||||
super(ReCaptchaField, self).clean(response_value)
|
||||
if not challenge_value:
|
||||
raise util.ValidationError(
|
||||
_(u'The CAPTCHA challenge is missing.'))
|
||||
elif not response_value:
|
||||
raise util.ValidationError(
|
||||
_(u'The CAPTCHA solution is missing.'))
|
||||
elif self._check_recaptcha(challenge_value, response_value):
|
||||
return challenge_value
|
||||
else:
|
||||
raise util.ValidationError(
|
||||
_(u'The CAPTCHA solution was incorrect.'))
|
||||
def clean(self, values):
|
||||
challenge_value = values[0]
|
||||
response_value = values[1]
|
||||
super(ReCaptchaField, self).clean(response_value)
|
||||
if not challenge_value:
|
||||
raise util.ValidationError(
|
||||
_(u'The CAPTCHA challenge is missing.'))
|
||||
elif not response_value:
|
||||
raise util.ValidationError(
|
||||
_(u'The CAPTCHA solution is missing.'))
|
||||
elif self._check_recaptcha(challenge_value, response_value):
|
||||
return challenge_value
|
||||
else:
|
||||
raise util.ValidationError(
|
||||
_(u'The CAPTCHA solution was incorrect.'))
|
||||
|
||||
|
||||
class RegexField(Html5Mixin, django.forms.RegexField):
|
||||
|
||||
@@ -4,15 +4,13 @@ Created on 08.05.2011
|
||||
@author: christian
|
||||
"""
|
||||
from django.db import models
|
||||
from django.db.models import ForeignKey, Model, SET_NULL # @UnusedImport
|
||||
from django.db.models import SET_DEFAULT, ManyToManyField # @UnusedImport
|
||||
from django.db.models import ForeignKey # @UnusedImport
|
||||
from django.utils import six
|
||||
|
||||
from . import forms, widgets
|
||||
|
||||
|
||||
class BooleanField(models.BooleanField):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'form_class': forms.BooleanField}
|
||||
defaults.update(kwargs)
|
||||
@@ -20,7 +18,6 @@ class BooleanField(models.BooleanField):
|
||||
|
||||
|
||||
class CharField(models.CharField):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'form_class': forms.CharField}
|
||||
defaults.update(kwargs)
|
||||
@@ -28,7 +25,6 @@ class CharField(models.CharField):
|
||||
|
||||
|
||||
class DateField(models.DateField):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {
|
||||
'form_class': forms.DateField}
|
||||
@@ -37,7 +33,6 @@ class DateField(models.DateField):
|
||||
|
||||
|
||||
class DateTimeField(models.DateTimeField):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'form_class': forms.DateTimeField}
|
||||
defaults.update(kwargs)
|
||||
@@ -52,7 +47,6 @@ class EmailField(models.EmailField):
|
||||
|
||||
|
||||
class FileField(models.FileField):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'form_class': forms.FileField}
|
||||
defaults.update(kwargs)
|
||||
@@ -60,7 +54,6 @@ class FileField(models.FileField):
|
||||
|
||||
|
||||
class ForeignKey(models.ForeignKey):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
db = kwargs.pop('using', None)
|
||||
if isinstance(self.rel.to, six.string_types):
|
||||
@@ -83,7 +76,6 @@ class ImageField(models.ImageField):
|
||||
|
||||
|
||||
class IntegerField(models.IntegerField):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'form_class': forms.IntegerField}
|
||||
defaults.update(kwargs)
|
||||
@@ -91,7 +83,6 @@ class IntegerField(models.IntegerField):
|
||||
|
||||
|
||||
class NullBooleanField(models.NullBooleanField):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'form_class': forms.BooleanField}
|
||||
defaults.update(kwargs)
|
||||
@@ -99,7 +90,6 @@ class NullBooleanField(models.NullBooleanField):
|
||||
|
||||
|
||||
class PositiveSmallIntegerField(models.PositiveSmallIntegerField):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {
|
||||
'form_class': forms.IntegerField,
|
||||
@@ -111,7 +101,6 @@ class PositiveSmallIntegerField(models.PositiveSmallIntegerField):
|
||||
|
||||
|
||||
class PositiveIntegerField(models.IntegerField):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {
|
||||
'form_class': forms.IntegerField,
|
||||
@@ -122,7 +111,6 @@ class PositiveIntegerField(models.IntegerField):
|
||||
|
||||
|
||||
class SlugField(models.SlugField):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'form_class': forms.SlugField}
|
||||
defaults.update(kwargs)
|
||||
@@ -130,9 +118,7 @@ class SlugField(models.SlugField):
|
||||
|
||||
|
||||
class TextField(models.TextField):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
|
||||
defaults = {
|
||||
'form_class': forms.CharField,
|
||||
'widget': widgets.Textarea
|
||||
@@ -142,7 +128,6 @@ class TextField(models.TextField):
|
||||
|
||||
|
||||
class URLField(models.URLField):
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'form_class': forms.URLField}
|
||||
defaults.update(kwargs)
|
||||
|
||||
@@ -4,6 +4,7 @@ Created on 05.08.2011
|
||||
@author: christian
|
||||
"""
|
||||
from django.http import Http404
|
||||
|
||||
from . import registry
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ from django.utils.safestring import mark_safe
|
||||
|
||||
|
||||
class AutoCompleteWidget(widgets.TextInput):
|
||||
|
||||
def __init__(self, lookup_class, attrs=None, *args, **kwargs):
|
||||
self.lookup_class = lookup_class
|
||||
self.allow_new = kwargs.pop('allow_new', False)
|
||||
@@ -29,7 +28,8 @@ class AutoCompleteWidget(widgets.TextInput):
|
||||
self.qs.update(qs_dict)
|
||||
|
||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||
attrs = super(AutoCompleteWidget, self).build_attrs(extra_attrs, **kwargs) # @IgnorePep8
|
||||
attrs = super(AutoCompleteWidget, self).build_attrs(extra_attrs,
|
||||
**kwargs) # @IgnorePep8
|
||||
url = self.lookup_class.url()
|
||||
if self.qs:
|
||||
url = '%s?%s' % (url, urlencode(self.qs))
|
||||
@@ -40,9 +40,9 @@ class AutoCompleteWidget(widgets.TextInput):
|
||||
|
||||
|
||||
class AutoComboboxWidget(AutoCompleteWidget):
|
||||
|
||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||
attrs = super(AutoComboboxWidget, self).build_attrs(extra_attrs, **kwargs) # @IgnorePep8
|
||||
attrs = super(AutoComboboxWidget, self).build_attrs(extra_attrs,
|
||||
**kwargs) # @IgnorePep8
|
||||
attrs[u'data-selectable-type'] = 'combobox'
|
||||
return attrs
|
||||
|
||||
@@ -66,7 +66,8 @@ class DateTimeInput(widgets.MultiWidget):
|
||||
A Widget that splits datetime input into two <input type="text"> boxes.
|
||||
"""
|
||||
|
||||
def __init__(self, attrs=None, date_format='%Y-%m-%d', time_format='%H:%M'): # @IgnorePep8
|
||||
def __init__(self, attrs=None, date_format='%Y-%m-%d',
|
||||
time_format='%H:%M'): # @IgnorePep8
|
||||
widgets = (
|
||||
DateInput(attrs=attrs, date_format=date_format),
|
||||
TimeInput(attrs=attrs, time_format=time_format)
|
||||
@@ -92,7 +93,8 @@ class CheckboxInput(widgets.CheckboxInput):
|
||||
result = False
|
||||
if result:
|
||||
final_attrs['checked'] = 'checked'
|
||||
if not (value is True or value is False or value is None or value == ''): # @IgnorePep8
|
||||
if not (
|
||||
value is True or value is False or value is None or value == ''): # @IgnorePep8
|
||||
# Only add the 'value' attribute if a value is non-empty.
|
||||
final_attrs['value'] = force_unicode(value)
|
||||
|
||||
@@ -132,12 +134,13 @@ class ReCaptchaInput(widgets.Widget):
|
||||
src="https://www.google.com/recaptcha/api/challenge?k=%(public_key)s">
|
||||
</script><noscript><iframe
|
||||
src="https://www.google.com/recaptcha/api/noscript?k=%(public_key)s"
|
||||
height="300" width="500" frameborder="0"></iframe><br /><textarea
|
||||
height="130" width="500"></iframe><br /><textarea
|
||||
name="recaptcha_challenge_field" rows="3" cols="40"></textarea><input
|
||||
type="hidden" name="recaptcha_response_field" value="manual_challenge">
|
||||
</noscript>"""
|
||||
return mark_safe(javascript % {'public_key':
|
||||
settings.RECAPTCHA_PUBLIC_KEY, 'theme': 'red'})
|
||||
settings.RECAPTCHA_PUBLIC_KEY,
|
||||
'theme': 'red'})
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
return [data.get(self.recaptcha_challenge_name, None),
|
||||
@@ -145,13 +148,11 @@ class ReCaptchaInput(widgets.Widget):
|
||||
|
||||
|
||||
class SelectableMultiWidget(widgets.MultiWidget):
|
||||
|
||||
def update_query_parameters(self, qs_dict):
|
||||
self.widgets[0].update_query_parameters(qs_dict)
|
||||
|
||||
|
||||
class Textarea(widgets.Widget):
|
||||
|
||||
def __init__(self, attrs=None):
|
||||
# The 'rows' and 'cols' attributes are required for HTML correctness.
|
||||
default_attrs = {'cols': '40', 'rows': '10'}
|
||||
@@ -163,7 +164,9 @@ class Textarea(widgets.Widget):
|
||||
value = '' if value is None else value
|
||||
final_attrs = self.build_attrs(attrs, name=name)
|
||||
return mark_safe(u'<textarea%s>%s</textarea>' % (flatatt(final_attrs),
|
||||
conditional_escape(force_unicode(value))))
|
||||
conditional_escape(
|
||||
force_unicode(
|
||||
value))))
|
||||
|
||||
|
||||
class TextInput(widgets.TextInput):
|
||||
@@ -192,7 +195,6 @@ class URLInput(widgets.Input):
|
||||
|
||||
|
||||
class LookupMultipleHiddenInput(widgets.MultipleHiddenInput):
|
||||
|
||||
def __init__(self, lookup_class, *args, **kwargs):
|
||||
self.lookup_class = lookup_class
|
||||
super(LookupMultipleHiddenInput, self).__init__(*args, **kwargs)
|
||||
@@ -221,21 +223,22 @@ class LookupMultipleHiddenInput(widgets.MultipleHiddenInput):
|
||||
return mark_safe(u'\n'.join(inputs))
|
||||
|
||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||
attrs = super(LookupMultipleHiddenInput, self).build_attrs(extra_attrs, **kwargs) # @IgnorePep8
|
||||
attrs = super(LookupMultipleHiddenInput, self).build_attrs(extra_attrs,
|
||||
**kwargs) # @IgnorePep8
|
||||
attrs[u'data-selectable-type'] = 'hidden-multiple'
|
||||
return attrs
|
||||
|
||||
|
||||
class AutoCompleteSelectMultipleWidget(SelectableMultiWidget):
|
||||
|
||||
def __init__(self, lookup_class, *args, **kwargs):
|
||||
self.lookup_class = lookup_class
|
||||
widgets = [
|
||||
AutoCompleteWidget(lookup_class, allow_new=False,
|
||||
attrs={u'data-selectable-multiple': 'true'}),
|
||||
attrs={u'data-selectable-multiple': 'true'}),
|
||||
LookupMultipleHiddenInput(lookup_class)
|
||||
]
|
||||
super(AutoCompleteSelectMultipleWidget, self).__init__(widgets, *args, **kwargs) # @IgnorePep8
|
||||
super(AutoCompleteSelectMultipleWidget, self).__init__(widgets, *args,
|
||||
**kwargs) # @IgnorePep8
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
return self.widgets[1].value_from_datadict(data, files, name + '_1')
|
||||
@@ -244,19 +247,20 @@ class AutoCompleteSelectMultipleWidget(SelectableMultiWidget):
|
||||
if value and not hasattr(value, '__iter__'):
|
||||
value = [value]
|
||||
value = [u'', value]
|
||||
return super(AutoCompleteSelectMultipleWidget, self).render(name, value, attrs) # @IgnorePep8
|
||||
return super(AutoCompleteSelectMultipleWidget, self).render(name, value,
|
||||
attrs) # @IgnorePep8
|
||||
|
||||
|
||||
class AutoComboboxSelectMultipleWidget(SelectableMultiWidget):
|
||||
|
||||
def __init__(self, lookup_class, *args, **kwargs):
|
||||
self.lookup_class = lookup_class
|
||||
widgets = [
|
||||
AutoComboboxWidget(lookup_class, allow_new=False,
|
||||
attrs={u'data-selectable-multiple': 'true'}),
|
||||
attrs={u'data-selectable-multiple': 'true'}),
|
||||
LookupMultipleHiddenInput(lookup_class)
|
||||
]
|
||||
super(AutoComboboxSelectMultipleWidget, self).__init__(widgets, *args, **kwargs) # @IgnorePep8
|
||||
super(AutoComboboxSelectMultipleWidget, self).__init__(widgets, *args,
|
||||
**kwargs) # @IgnorePep8
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
return self.widgets[1].value_from_datadict(data, files, name + '_1')
|
||||
@@ -265,11 +269,11 @@ class AutoComboboxSelectMultipleWidget(SelectableMultiWidget):
|
||||
if value and not hasattr(value, '__iter__'):
|
||||
value = [value]
|
||||
value = [u'', value]
|
||||
return super(AutoComboboxSelectMultipleWidget, self).render(name, value, attrs) # @IgnorePep8
|
||||
return super(AutoComboboxSelectMultipleWidget, self).render(name, value,
|
||||
attrs) # @IgnorePep8
|
||||
|
||||
|
||||
class AutoCompleteSelectWidget(SelectableMultiWidget):
|
||||
|
||||
def __init__(self, lookup_class, *args, **kwargs):
|
||||
self.lookup_class = lookup_class
|
||||
self.allow_new = kwargs.pop('allow_new', False)
|
||||
@@ -277,7 +281,8 @@ class AutoCompleteSelectWidget(SelectableMultiWidget):
|
||||
AutoCompleteWidget(lookup_class, allow_new=self.allow_new),
|
||||
widgets.HiddenInput(attrs={u'data-selectable-type': 'hidden'})
|
||||
]
|
||||
super(AutoCompleteSelectWidget, self).__init__(widget_set, *args, **kwargs) # @IgnorePep8
|
||||
super(AutoCompleteSelectWidget, self).__init__(widget_set, *args,
|
||||
**kwargs) # @IgnorePep8
|
||||
|
||||
def decompress(self, value):
|
||||
if value:
|
||||
@@ -294,7 +299,6 @@ class AutoCompleteSelectWidget(SelectableMultiWidget):
|
||||
|
||||
|
||||
class AutoComboboxSelectWidget(SelectableMultiWidget):
|
||||
|
||||
def __init__(self, lookup_class, *args, **kwargs):
|
||||
self.lookup_class = lookup_class
|
||||
self.allow_new = kwargs.pop('allow_new', False)
|
||||
@@ -302,7 +306,8 @@ class AutoComboboxSelectWidget(SelectableMultiWidget):
|
||||
AutoComboboxWidget(lookup_class, allow_new=self.allow_new),
|
||||
widgets.HiddenInput(attrs={u'data-selectable-type': 'hidden'})
|
||||
]
|
||||
super(AutoComboboxSelectWidget, self).__init__(widget_set, *args, **kwargs) # @IgnorePep8
|
||||
super(AutoComboboxSelectWidget, self).__init__(widget_set, *args,
|
||||
**kwargs) # @IgnorePep8
|
||||
|
||||
def decompress(self, value):
|
||||
if value:
|
||||
|
||||
@@ -8,13 +8,19 @@ from BeautifulSoup import BeautifulSoup
|
||||
|
||||
class HtmlCleaner(object):
|
||||
ACCEPTABLE_ELEMENTS = ['a', 'abbr', 'acronym', 'address', 'area', 'b',
|
||||
'big', 'blockquote', 'br', 'button', 'caption', 'center', 'cite',
|
||||
'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl',
|
||||
'dt', 'em', 'font', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i',
|
||||
'img', 'ins', 'kbd', 'label', 'legend', 'li', 'map', 'menu', 'ol',
|
||||
'p', 'pre', 'q', 's', 'samp', 'small', 'span', 'strike',
|
||||
'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th',
|
||||
'thead', 'tr', 'tt', 'u', 'ul', 'var']
|
||||
'big', 'blockquote', 'br', 'button', 'caption',
|
||||
'center', 'cite',
|
||||
'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir',
|
||||
'div', 'dl',
|
||||
'dt', 'em', 'font', 'h1', 'h2', 'h3', 'h4', 'h5',
|
||||
'h6', 'hr', 'i',
|
||||
'img', 'ins', 'kbd', 'label', 'legend', 'li', 'map',
|
||||
'menu', 'ol',
|
||||
'p', 'pre', 'q', 's', 'samp', 'small', 'span',
|
||||
'strike',
|
||||
'strong', 'sub', 'sup', 'table', 'tbody', 'td',
|
||||
'tfoot', 'th',
|
||||
'thead', 'tr', 'tt', 'u', 'ul', 'var']
|
||||
|
||||
ACCEPTABELE_ATTRIBUTES = [
|
||||
'abbr', 'accept', 'accept-charset', 'accesskey',
|
||||
|
||||
@@ -22,10 +22,10 @@ class Command(BaseCommand):
|
||||
help = _("Reads raw CSS from stdin, and writes compressed CSS to stdout.")
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option('-w', '--wrap',
|
||||
type='int',
|
||||
default=None,
|
||||
metavar='N',
|
||||
help="Wrap output to approximately N chars per line."),
|
||||
type='int',
|
||||
default=None,
|
||||
metavar='N',
|
||||
help="Wrap output to approximately N chars per line."),
|
||||
)
|
||||
|
||||
def remove_comments(self, css):
|
||||
@@ -65,7 +65,6 @@ class Command(BaseCommand):
|
||||
"""Remove unnecessary whitespace characters."""
|
||||
|
||||
def pseudoclasscolon(css):
|
||||
|
||||
"""
|
||||
Prevents 'p :link' from becoming 'p:link'.
|
||||
|
||||
@@ -157,13 +156,15 @@ class Command(BaseCommand):
|
||||
def condense_hex_colors(self, css):
|
||||
"""Shorten colors from #AABBCC to #ABC where possible."""
|
||||
|
||||
regex = re.compile(r"([^\"'=\s])(\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])")
|
||||
regex = re.compile(
|
||||
r"([^\"'=\s])(\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])")
|
||||
match = regex.search(css)
|
||||
while match:
|
||||
first = match.group(3) + match.group(5) + match.group(7)
|
||||
second = match.group(4) + match.group(6) + match.group(8)
|
||||
if first.lower() == second.lower():
|
||||
css = css.replace(match.group(), match.group(1) + match.group(2) + '#' + first)
|
||||
css = css.replace(match.group(),
|
||||
match.group(1) + match.group(2) + '#' + first)
|
||||
match = regex.search(css, match.end() - 3)
|
||||
else:
|
||||
match = regex.search(css, match.end())
|
||||
@@ -235,7 +236,8 @@ class Command(BaseCommand):
|
||||
css = ''
|
||||
"""Read each .css file in the css_input directory,
|
||||
and append their content"""
|
||||
for file_name in fnmatch.filter(sorted(os.listdir(css_input)), '*.css'):
|
||||
for file_name in fnmatch.filter(sorted(os.listdir(css_input)),
|
||||
'*.css'):
|
||||
print ' Adding: %s' % file_name
|
||||
with open(os.path.join(css_input, file_name), 'r') as css_file:
|
||||
css += css_file.read()
|
||||
|
||||
@@ -3,13 +3,13 @@ Created on 06.06.2011
|
||||
|
||||
@author: christian
|
||||
"""
|
||||
import os
|
||||
import fnmatch
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils.translation import ugettext as _
|
||||
from optparse import make_option
|
||||
import os, re, fnmatch
|
||||
from scss import parser
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@@ -22,13 +22,13 @@ class Command(BaseCommand):
|
||||
def handle(self, *args, **options):
|
||||
CSS_ROOT = os.path.join(settings.STATICFILES_DIRS[0], 'css')
|
||||
for site in Site.objects.all():
|
||||
css_input = os.path.join(CSS_ROOT, site.domain)
|
||||
css_input = os.path.join(CSS_ROOT, site.domain)
|
||||
css_output = '%s/%s.css' % (CSS_ROOT, site.domain.replace('.', '_'))
|
||||
|
||||
print _("Compressing CSS for %s") % site.name
|
||||
print "Input Dir: %s" % css_input
|
||||
print "Output File: %s" % css_output
|
||||
|
||||
|
||||
try:
|
||||
os.makedirs(css_input)
|
||||
except OSError:
|
||||
@@ -36,11 +36,12 @@ class Command(BaseCommand):
|
||||
css = ''
|
||||
"""Read each .css file in the css_input directory,
|
||||
and append their content"""
|
||||
for file_name in fnmatch.filter(sorted(os.listdir(css_input)), '*.css'):
|
||||
for file_name in fnmatch.filter(sorted(os.listdir(css_input)),
|
||||
'*.css'):
|
||||
print ' Adding: %s' % file_name
|
||||
with open(os.path.join(css_input, file_name), 'r') as css_file:
|
||||
css += css_file.read()
|
||||
with open(css_output, 'w') as css_file:
|
||||
css_file.write(self.compress(css))
|
||||
#file.write(css)
|
||||
# file.write(css)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import logging
|
||||
|
||||
from django.core import mail
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.sites.models import Site
|
||||
@@ -66,7 +67,7 @@ class MassMailer(object):
|
||||
for recipient in self.recipients:
|
||||
mail_context['recipient'] = recipient
|
||||
mail_to = "%s %s <%s>" % (recipient.first_name,
|
||||
recipient.last_name, recipient.email)
|
||||
recipient.last_name, recipient.email)
|
||||
message = mail.EmailMultiAlternatives(
|
||||
subject=self.subject,
|
||||
body=self.txt_template.render(mail_context),
|
||||
@@ -92,7 +93,7 @@ class MassMailer(object):
|
||||
return True
|
||||
else:
|
||||
self.log.info('Sending eMail "%s" to %d people', self.subject,
|
||||
len(self.mail_queue))
|
||||
len(self.mail_queue))
|
||||
self.log.debug(self.recipients)
|
||||
|
||||
self.open_smtp_connection()
|
||||
|
||||
@@ -20,13 +20,16 @@ class LoginRequiredMixin(object):
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if request.user.is_authenticated():
|
||||
return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
|
||||
return super(LoginRequiredMixin, self).dispatch(request, *args,
|
||||
**kwargs)
|
||||
elif self.raise_exception: # if an exception was desired
|
||||
return http.HttpResponseForbidden() # return a forbidden response.
|
||||
else:
|
||||
messages.error(request, _("You need to be logged in"))
|
||||
path = urlquote(request.get_full_path())
|
||||
return http.HttpResponseRedirect("%s?%s=%s" % (self.login_url, self.redirect_field_name, path))
|
||||
return http.HttpResponseRedirect(
|
||||
"%s?%s=%s" % (self.login_url, self.redirect_field_name, path))
|
||||
|
||||
|
||||
class PermissionRequiredMixin(object):
|
||||
"""
|
||||
@@ -60,18 +63,23 @@ class PermissionRequiredMixin(object):
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
# Verify class settings
|
||||
if self.permission_required is None or len(self.permission_required.split(".")) != 2:
|
||||
raise ImproperlyConfigured("'PermissionRequiredMixin' requires 'permission_required' attribute to be set.")
|
||||
if self.permission_required is None or len(
|
||||
self.permission_required.split(".")) != 2:
|
||||
raise ImproperlyConfigured(
|
||||
"'PermissionRequiredMixin' requires 'permission_required' attribute to be set.")
|
||||
has_permission = request.user.has_perm(self.permission_required)
|
||||
|
||||
if has_permission:
|
||||
return super(PermissionRequiredMixin, self).dispatch(request, *args, **kwargs)
|
||||
return super(PermissionRequiredMixin, self).dispatch(request, *args,
|
||||
**kwargs)
|
||||
elif self.raise_exception:
|
||||
return http.HttpResponseForbidden()
|
||||
else:
|
||||
messages.warning(request, self.permission_failed_message)
|
||||
path = urlquote(request.get_full_path())
|
||||
return http.HttpResponseRedirect("%s?%s=%s" % (self.login_url, self.redirect_field_name, path))
|
||||
return http.HttpResponseRedirect(
|
||||
"%s?%s=%s" % (self.login_url, self.redirect_field_name, path))
|
||||
|
||||
|
||||
class SuperuserRequiredMixin(object):
|
||||
"""
|
||||
@@ -83,10 +91,13 @@ class SuperuserRequiredMixin(object):
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if request.user.is_superuser: # If the user is a standard user,
|
||||
return super(SuperuserRequiredMixin, self).dispatch(request, *args, **kwargs)
|
||||
return super(SuperuserRequiredMixin, self).dispatch(request, *args,
|
||||
**kwargs)
|
||||
elif self.raise_exception: # *and* if an exception was desired
|
||||
return http.HttpResponseForbidden() # return a forbidden response.
|
||||
else:
|
||||
messages.error(request, _("You don't have the permissions for this"))
|
||||
messages.error(request,
|
||||
_("You don't have the permissions for this"))
|
||||
path = urlquote(request.get_full_path())
|
||||
return http.HttpResponseRedirect("%s?%s=%s" % (self.login_url, self.redirect_field_name, path))
|
||||
return http.HttpResponseRedirect(
|
||||
"%s?%s=%s" % (self.login_url, self.redirect_field_name, path))
|
||||
|
||||
@@ -12,6 +12,7 @@ import markdown
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter(name='markdown', is_safe=True)
|
||||
def markdown_filter(value, arg=''):
|
||||
"""
|
||||
@@ -41,7 +42,8 @@ def markdown_filter(value, arg=''):
|
||||
if extensions and extensions[0] == "safe":
|
||||
extensions = extensions[1:]
|
||||
return mark_safe(markdown.markdown(
|
||||
force_text(value), extensions, safe_mode=True, enable_attributes=False))
|
||||
force_text(value), extensions, safe_mode=True,
|
||||
enable_attributes=False))
|
||||
else:
|
||||
return mark_safe(markdown.markdown(
|
||||
force_text(value), extensions, safe_mode=False))
|
||||
|
||||
Reference in New Issue
Block a user