From 7e55faa05c5f5f0aacaebe8da367e80595224374 Mon Sep 17 00:00:00 2001 From: Christian Berg Date: Sat, 9 Jan 2016 22:55:26 +0100 Subject: [PATCH] =?UTF-8?q?Fehler=20bei=20Vergabe=20von=20Bonuspunkte=20ko?= =?UTF-8?q?rrigiert.=20Kommentare=20f=C3=BCr=20Bonuspunkte=20werden=20jetz?= =?UTF-8?q?t=20als=20Kommentar=20beim=20Spieler=20hinterlassen,=20nicht=20?= =?UTF-8?q?als=20Kommentar=20in=20der=20Hanchan.=20FIXED:=203=5Fin=5Fa=5Fr?= =?UTF-8?q?ow=20counter=20wurde=20nicht=20zur=C3=BCckgesetzt=20wenn=20Bonu?= =?UTF-8?q?spunkte=20vergeben=20wurden.=20FIXED:=20Durchschnittliche=20Pla?= =?UTF-8?q?tzierung=20w=C3=A4hrend=20eines=20Events=20wurde=20nur=20als=20?= =?UTF-8?q?Ganzzahl=20berechnet.=20Wird=20nun=20als=20Fie=C3=9Fkomma=20ber?= =?UTF-8?q?echnet=20und=20gesichert.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + src/aggregator/__init__.py | 1 - src/aggregator/admin.py | 21 - src/aggregator/feeds.py | 25 - src/aggregator/management/__init__.py | 1 - .../management/commands/__init__.py | 1 - .../management/commands/update-feeds.py | 21 - src/aggregator/migrations/0001_initial.py | 45 -- src/aggregator/models.py | 114 ---- src/aggregator/sitemaps.py | 16 - src/aggregator/templatetags/aggregator.py | 31 - src/content/context_processors.py | 7 +- src/content/feeds.py | 2 - .../management/commands/syncfacebook.py | 7 +- src/content/migrations/0001_initial.py | 131 +++- .../migrations/0002_auto_20150823_2232.py | 38 ++ src/content/models.py | 17 +- src/content/news_urls.py | 2 +- .../templates/content/article_archive.html | 35 +- .../templates/content/article_detail.html | 10 +- src/content/templatetags/fieldset_extras.py | 1 + src/content/tests.py | 1 + src/content/urls.py | 2 +- src/content/views.py | 12 +- src/events/admin.py | 8 +- src/events/context_processors.py | 26 +- src/events/forms.py | 4 +- .../urls.py => events/gallery_urls.py} | 2 +- src/events/migrations/0001_initial.py | 39 +- .../migrations/0002_auto_20150818_2139.py | 6 +- .../migrations/0003_auto_20150823_2232.py | 20 + .../migrations/0004_auto_20150901_2204.py | 91 +++ .../migrations/0005_auto_20150907_2021.py | 20 + src/events/models.py | 228 +++++-- src/events/static/js/mousetrap.js | 11 + .../templates/events/event_archive.html | 26 +- src/events/templates/events/event_detail.html | 42 +- src/events/templates/events/event_list.html | 35 +- src/events/templates/events/event_site.html | 6 +- .../templates/events/eventseries_form.html | 9 +- .../events}/photo_confirm_delete.html | 13 +- src/events/templates/events/photo_detail.html | 85 +++ .../templates/events}/photo_gallery.html | 4 +- src/events/templates/events/photo_list.html | 45 ++ .../templates/events}/photo_upload.html | 21 +- src/events/tests.py | 1 + src/events/urls.py | 5 +- src/events/views.py | 83 +-- src/gallery/__init__.py | 1 - src/gallery/forms.py | 42 -- src/gallery/locale/de/LC_MESSAGES/django.po | 154 ----- src/gallery/migrations/0001_initial.py | 41 -- src/gallery/migrations/__init__.py | 0 src/gallery/models.py | 171 ----- .../templates/gallery/photo_detail.html | 57 -- src/gallery/templates/gallery/photo_list.html | 47 -- src/gallery/views.py | 142 ---- src/kasu/image_models.py | 18 +- src/kasu/static/css/kasu.css | 5 +- src/kasu/static/img/buttonbar.gif | Bin 663 -> 0 bytes src/kasu/static/img/favicon.ico | Bin 15086 -> 15086 bytes src/kasu/static/img/google_maps.png | Bin 10637 -> 0 bytes src/kasu/static/img/google_plus.png | Bin 3907 -> 0 bytes src/kasu/static/img/usernav-bg.png | Bin 1021 -> 0 bytes src/kasu/static/less/common.less | 428 ++++++------ src/kasu/static/less/desktop.less | 515 +++++---------- .../animated.less | 0 .../bordered-pulled.less | 0 .../{font-aweseome => font-awesome}/core.less | 0 .../fixed-width.less | 0 .../font-awesome.less | 0 .../icons.less | 0 .../larger.less | 0 .../{font-aweseome => font-awesome}/list.less | 0 .../mixins.less | 0 .../{font-aweseome => font-awesome}/path.less | 0 .../rotated-flipped.less | 0 .../stacked.less | 0 .../variables.less | 0 src/kasu/static/less/header_nav.less | 25 +- src/kasu/static/less/kasu.less | 30 +- src/kasu/static/less/mobile.less | 622 +++++++----------- src/kasu/static/less/print.less | 134 ++-- src/kasu/templates/base.html | 75 ++- src/kasu/templates/comments/form.html | 13 +- src/kasu/templates/comments/list.html | 12 +- src/kasu/templates/comments/preview.html | 11 +- src/kasu/templates/form.html | 15 +- src/kasu/templates/index.html | 19 +- src/kasu/templates/paginator.html | 27 +- src/kasu/urls.py | 27 +- src/kasu/utils.py | 9 +- src/kasu/wsgi.py | 2 - src/mahjong_ranking/admin.py | 8 +- src/mahjong_ranking/forms.py | 34 +- .../locale/de/LC_MESSAGES/django.mo | Bin 4423 -> 4832 bytes .../locale/de/LC_MESSAGES/django.po | 6 +- .../management/commands/export-ranking.py | 41 ++ .../management/commands/random-ranking.py | 3 - src/mahjong_ranking/managers.py | 24 +- src/mahjong_ranking/middleware.py | 19 +- .../migrations/0001_initial.py | 157 +++++ ...0002_kyudanranking_legacy_hanchan_count.py | 19 + .../migrations/__init__.py | 0 src/mahjong_ranking/models.py | 287 +++++--- .../mahjong_ranking/eventhanchan_list.html | 69 +- .../mahjong_ranking/eventranking_list.html | 22 +- .../hanchan_confirm_delete.html | 5 +- .../mahjong_ranking/hanchan_form.html | 27 +- .../mahjong_ranking/kyudanranking_list.html | 12 +- .../mahjong_ranking/ladder_redbox.html | 36 + .../mahjong_ranking/player_dan_score.html | 6 +- .../mahjong_ranking/player_invalid_score.html | 2 +- .../mahjong_ranking/player_kyu_score.html | 2 +- .../mahjong_ranking/player_ladder_score.html | 2 +- .../mahjong_ranking/seasonranking_list.html | 44 +- src/mahjong_ranking/tests.py | 1 + src/mahjong_ranking/urls.py | 44 +- src/mahjong_ranking/views.py | 70 +- src/maistar_ranking/forms.py | 2 +- .../migrations/0001_initial.py | 36 +- .../migrations/0002_auto_20150823_2232.py | 19 + .../migrations/0003_auto_20150901_2204.py | 19 + src/maistar_ranking/models.py | 3 - src/maistar_ranking/settings.py | 2 - .../templates/maistar_ranking/game_list.html | 13 +- .../maistar_ranking/player_game_list.html | 5 +- .../maistar_ranking/ranking_list.html | 10 +- src/maistar_ranking/urls.py | 21 +- src/maistar_ranking/views.py | 8 +- src/manage.py | 4 +- src/membership/admin.py | 15 +- src/membership/forms.py | 14 +- .../locale/de/LC_MESSAGES/django.mo | Bin 6739 -> 6725 bytes .../locale/de/LC_MESSAGES/django.po | 10 +- .../commands/cleanup-registration.py | 1 - src/membership/migrations/0001_initial.py | 81 ++- .../migrations/0003_auto_20150820_2122.py | 3 +- .../0004_remove_membership_comment.py | 18 + .../migrations/0005_auto_20150901_2204.py | 23 + src/membership/models.py | 20 +- src/membership/specs.py | 2 +- .../membership/activation_error.html | 2 +- .../membership/membership_detail.html | 6 +- src/membership/urls.py | 8 +- src/membership/views.py | 2 +- src/utils/forms.py | 1 + src/utils/html5/base.py | 4 +- src/utils/html5/forms.py | 2 + src/utils/html5/models.py | 14 + src/utils/html5/widgets.py | 13 +- src/utils/management/commands/compresscss.py | 14 +- .../management/commands/scss-compiler.py | 4 +- src/utils/massmailer.py | 1 + src/utils/templatetags/__init__.py | 0 src/utils/templatetags/markup.py | 49 -- sync.sh | 3 +- 157 files changed, 2571 insertions(+), 2835 deletions(-) delete mode 100644 src/aggregator/__init__.py delete mode 100644 src/aggregator/admin.py delete mode 100644 src/aggregator/feeds.py delete mode 100644 src/aggregator/management/__init__.py delete mode 100755 src/aggregator/management/commands/__init__.py delete mode 100644 src/aggregator/management/commands/update-feeds.py delete mode 100644 src/aggregator/migrations/0001_initial.py delete mode 100644 src/aggregator/models.py delete mode 100644 src/aggregator/sitemaps.py delete mode 100644 src/aggregator/templatetags/aggregator.py create mode 100644 src/content/migrations/0002_auto_20150823_2232.py rename src/{gallery/urls.py => events/gallery_urls.py} (99%) create mode 100644 src/events/migrations/0003_auto_20150823_2232.py create mode 100644 src/events/migrations/0004_auto_20150901_2204.py create mode 100644 src/events/migrations/0005_auto_20150907_2021.py create mode 100644 src/events/static/js/mousetrap.js rename src/{gallery/templates/gallery => events/templates/events}/photo_confirm_delete.html (53%) create mode 100644 src/events/templates/events/photo_detail.html rename src/{gallery/templates/gallery => events/templates/events}/photo_gallery.html (76%) create mode 100644 src/events/templates/events/photo_list.html rename src/{gallery/templates/gallery => events/templates/events}/photo_upload.html (67%) delete mode 100644 src/gallery/__init__.py delete mode 100644 src/gallery/forms.py delete mode 100644 src/gallery/locale/de/LC_MESSAGES/django.po delete mode 100644 src/gallery/migrations/0001_initial.py delete mode 100644 src/gallery/migrations/__init__.py delete mode 100644 src/gallery/models.py delete mode 100644 src/gallery/templates/gallery/photo_detail.html delete mode 100644 src/gallery/templates/gallery/photo_list.html delete mode 100644 src/gallery/views.py delete mode 100644 src/kasu/static/img/buttonbar.gif delete mode 100644 src/kasu/static/img/google_maps.png delete mode 100644 src/kasu/static/img/google_plus.png delete mode 100644 src/kasu/static/img/usernav-bg.png rename src/kasu/static/less/{font-aweseome => font-awesome}/animated.less (100%) rename src/kasu/static/less/{font-aweseome => font-awesome}/bordered-pulled.less (100%) rename src/kasu/static/less/{font-aweseome => font-awesome}/core.less (100%) rename src/kasu/static/less/{font-aweseome => font-awesome}/fixed-width.less (100%) rename src/kasu/static/less/{font-aweseome => font-awesome}/font-awesome.less (100%) rename src/kasu/static/less/{font-aweseome => font-awesome}/icons.less (100%) rename src/kasu/static/less/{font-aweseome => font-awesome}/larger.less (100%) rename src/kasu/static/less/{font-aweseome => font-awesome}/list.less (100%) rename src/kasu/static/less/{font-aweseome => font-awesome}/mixins.less (100%) rename src/kasu/static/less/{font-aweseome => font-awesome}/path.less (100%) rename src/kasu/static/less/{font-aweseome => font-awesome}/rotated-flipped.less (100%) rename src/kasu/static/less/{font-aweseome => font-awesome}/stacked.less (100%) rename src/kasu/static/less/{font-aweseome => font-awesome}/variables.less (100%) create mode 100644 src/mahjong_ranking/management/commands/export-ranking.py create mode 100644 src/mahjong_ranking/migrations/0001_initial.py create mode 100644 src/mahjong_ranking/migrations/0002_kyudanranking_legacy_hanchan_count.py rename src/{aggregator => mahjong_ranking}/migrations/__init__.py (100%) create mode 100644 src/mahjong_ranking/templates/mahjong_ranking/ladder_redbox.html create mode 100644 src/maistar_ranking/migrations/0002_auto_20150823_2232.py create mode 100644 src/maistar_ranking/migrations/0003_auto_20150901_2204.py create mode 100644 src/membership/migrations/0004_remove_membership_comment.py create mode 100644 src/membership/migrations/0005_auto_20150901_2204.py delete mode 100644 src/utils/templatetags/__init__.py delete mode 100644 src/utils/templatetags/markup.py diff --git a/.gitignore b/.gitignore index 7d8cc0b..b358153 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ __pycache__/ .Python env/ build/ +upload/ develop-eggs/ dist/ downloads/ diff --git a/src/aggregator/__init__.py b/src/aggregator/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/src/aggregator/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/aggregator/admin.py b/src/aggregator/admin.py deleted file mode 100644 index dd7243f..0000000 --- a/src/aggregator/admin.py +++ /dev/null @@ -1,21 +0,0 @@ -from django.contrib import admin - -from models import Feed, FeedItem - - -admin.site.register( - Feed, - list_display=["title", "public_url", "last_update", 'is_functional'], - list_filter=["is_functional"], - ordering=["title"], - search_fields=["title", "public_url"], - list_per_page=500, -) - -admin.site.register( - FeedItem, - list_display=['title', 'feed', 'date_modified'], - list_filter=['feed'], - search_fields=['feed__title', 'feed__public_url', 'title'], - date_heirarchy=['date_modified'], -) diff --git a/src/aggregator/feeds.py b/src/aggregator/feeds.py deleted file mode 100644 index 463f753..0000000 --- a/src/aggregator/feeds.py +++ /dev/null @@ -1,25 +0,0 @@ -import django.contrib.syndication.views - -from .models import FeedItem - - -# noinspection PyMethodMayBeStatic -class LatestFeedItems(django.contrib.syndication.views.Feed): - link = "http://aol.animanga.at/" - description = "Aktuelle Nachrichten aus der Austrian Otaku League" - title = "AOL - Newsfeed" - - def items(self): - return FeedItem.objects.get_recent_items() - - def item_title(self, item): - return "%s: %s" % (item.feed.title, item.title) - - def item_description(self, item): - return item.summary - - def item_author_name(self, item): - return item.feed.title - - def item_pubdate(self, item): - return item.date_modified diff --git a/src/aggregator/management/__init__.py b/src/aggregator/management/__init__.py deleted file mode 100644 index 6f01d36..0000000 --- a/src/aggregator/management/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# - diff --git a/src/aggregator/management/commands/__init__.py b/src/aggregator/management/commands/__init__.py deleted file mode 100755 index 792d600..0000000 --- a/src/aggregator/management/commands/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/src/aggregator/management/commands/update-feeds.py b/src/aggregator/management/commands/update-feeds.py deleted file mode 100644 index 7e71b17..0000000 --- a/src/aggregator/management/commands/update-feeds.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Update feeds for Django community page. Requires Mark Pilgrim's excellent -Universal Feed Parser (http://feedparser.org) -""" - -from django.core.management.base import BaseCommand - -from aggregator.models import Feed - - -class Command(BaseCommand): - help = "Updates all RSS Feeds" - - def handle(self, *args, **options): - verbose = int(options['verbosity']) > 0 - for feed in Feed.objects.filter(is_functional=True): - if verbose: - print - print "%s - URL: %s" % (feed.title, feed.feed_url) - print '=' * 80 - feed.parse() diff --git a/src/aggregator/migrations/0001_initial.py b/src/aggregator/migrations/0001_initial.py deleted file mode 100644 index 82e4d31..0000000 --- a/src/aggregator/migrations/0001_initial.py +++ /dev/null @@ -1,45 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import utils.html5.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('sites', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Feed', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', utils.html5.models.CharField(max_length=500)), - ('feed_url', utils.html5.models.URLField(unique=True, max_length=255)), - ('public_url', utils.html5.models.URLField(max_length=255)), - ('last_update', utils.html5.models.DateTimeField(null=True, blank=True)), - ('is_functional', utils.html5.models.BooleanField(default=True)), - ('site', utils.html5.models.ForeignKey(to='sites.Site')), - ], - options={ - 'ordering': ('title',), - }, - ), - migrations.CreateModel( - name='FeedItem', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', utils.html5.models.CharField(max_length=500)), - ('link', utils.html5.models.URLField(max_length=500)), - ('guid', utils.html5.models.CharField(unique=True, max_length=255, db_index=True)), - ('summary', utils.html5.models.TextField(blank=True)), - ('date_modified', utils.html5.models.DateTimeField()), - ('feed', utils.html5.models.ForeignKey(related_name='feed_items', to='aggregator.Feed')), - ], - options={ - 'ordering': ('-date_modified',), - }, - ), - ] diff --git a/src/aggregator/models.py b/src/aggregator/models.py deleted file mode 100644 index ad22620..0000000 --- a/src/aggregator/models.py +++ /dev/null @@ -1,114 +0,0 @@ -""" -Created on 05.02.2011 - -@author: christian -""" - -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 -import django.db.models -import feedparser - -from utils.html5 import models - - -class FeedManager(django.db.models.Manager): - def active(self): - site = settings.SITE_ID - feeds = self.filter(is_functional=True, site=site) - for feed in feeds: - if feed.last_update: - feed_age = timezone.now() - feed.last_update - if feed_age > timedelta(hours=12): - feed.parse() - else: - feed.parse() - return feeds - - -class FeedItemManager(django.db.models.Manager): - def recent_items(self, max_items=10, site=None): - site = site or settings.SITE_ID - return self.select_related().filter(feed__site=site)[:max_items] - - -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) - public_url = models.URLField(max_length=255) - last_update = models.DateTimeField(blank=True, null=True) - is_functional = models.BooleanField(default=True) - objects = FeedManager() - - def __unicode__(self): - return self.title - - def parse(self): - parsed_feed = feedparser.parse(self.feed_url) - html_parser = HTMLParser.HTMLParser() - - if parsed_feed.bozo and type( - parsed_feed.bozo_exception) == urllib2.URLError: - self.is_functional = False - return self.save() - - for feed_entry in parsed_feed.entries: - title = html_parser.unescape(feed_entry.title) - if not title: - continue - - link = feed_entry.link - guid = feed_entry.get("id", link) - summary = html_parser.unescape( - feed_entry.get("summary", feed_entry.get( - "description", - 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()) - - 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() - - class Meta: - ordering = ("title",) - - -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) - guid = models.CharField(max_length=255, unique=True, db_index=True) - summary = models.TextField(blank=True) - date_modified = models.DateTimeField() - objects = FeedItemManager() - - class Meta: - ordering = ("-date_modified",) - - def __unicode__(self): - return self.title - - def get_absolute_url(self): - return self.link diff --git a/src/aggregator/sitemaps.py b/src/aggregator/sitemaps.py deleted file mode 100644 index a447596..0000000 --- a/src/aggregator/sitemaps.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.contrib.sitemaps import Sitemap - -from models import FeedItem - - - -# noinspection PyMethodMayBeStatic -class FeedItemSitemap(Sitemap): - changefreq = "never" - priority = 0.5 - - def items(self): - return FeedItem.objects.get_recent_items() - - def lastmod(self, obj): - return obj.date_modified diff --git a/src/aggregator/templatetags/aggregator.py b/src/aggregator/templatetags/aggregator.py deleted file mode 100644 index d9f4d79..0000000 --- a/src/aggregator/templatetags/aggregator.py +++ /dev/null @@ -1,31 +0,0 @@ -from django import template - -from models import Feed - - -class FeedListNode(template.Node): - def __init__(self, varname): - self.varname = varname - - def render(self, context): - context[self.varname] = Feed.objects.filter(is_defunct=False) - return '' - - -# noinspection PyUnusedLocal -def do_get_feed_list(parser, token): - """ - {% get_feed_list as feed_list %} - """ - bits = token.contents.split() - if len(bits) != 3: - raise template.TemplateSyntaxError, \ - "'%s' tag takes two arguments" % bits[0] - if bits[1] != "as": - raise template.TemplateSyntaxError, \ - "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) diff --git a/src/content/context_processors.py b/src/content/context_processors.py index c83929b..fb29cd1 100644 --- a/src/content/context_processors.py +++ b/src/content/context_processors.py @@ -15,12 +15,12 @@ def content_menus(request): current_path = request.path_info[1:request.path_info.rfind('.')] # erzeuge das Top-Level Menü - top_menu_items = [] top_level_pages = cache.get('top_level_pages') if top_level_pages is None: top_level_pages = models.Page.objects.filter(parent=None) top_level_pages = top_level_pages.exclude(slug='index') top_level_pages = top_level_pages.order_by('position') + top_level_pages = top_level_pages.prefetch_related('subpages') cache.set('top_level_pages', top_level_pages, 360) for item in top_level_pages: if current_path.startswith(item.path): @@ -28,7 +28,6 @@ def content_menus(request): current_top_page = item else: item.active = False - top_menu_items.append(item) # Entdecke die aktuell geöffnete Seite all_pages = cache.get('all_pages') @@ -43,8 +42,8 @@ def content_menus(request): break current_path = current_path[0:current_path.rfind('.')] - return {'top_menu_items': top_menu_items, + return {'top_menu_items': top_level_pages, 'current_top_page': current_top_page, 'current_path': current_path, 'current_page': current_page - } + } diff --git a/src/content/feeds.py b/src/content/feeds.py index b1fb423..87110e6 100644 --- a/src/content/feeds.py +++ b/src/content/feeds.py @@ -9,8 +9,6 @@ from django.utils.feedgenerator import Rss201rev2Feed from models import Article - -# noinspection PyMethodMayBeStatic class LatestNews(Feed): link = "http://www.kasu.at/" description = _("Current news from Kasu") diff --git a/src/content/management/commands/syncfacebook.py b/src/content/management/commands/syncfacebook.py index bc9024e..d64a096 100644 --- a/src/content/management/commands/syncfacebook.py +++ b/src/content/management/commands/syncfacebook.py @@ -12,12 +12,11 @@ import json class Command(BaseCommand): help = "Synchornisiert den Facenbook Feed für die Anzeige auf der Homepage" - def handle(self, *args, **options): #graph_api = facebook.GraphAPI(settings.FACEBOOK_ACCESS_TOKEN) #facebook_page = graph_api.get_object(settings.FACEBOOK_APP_ID+'/feed/') - #print facebook_page - #print graph_api.get_connections(facebook_page['id'], ) + # print facebook_page + # print graph_api.get_connections(facebook_page['id'], ) self.login() def login(self): @@ -31,4 +30,4 @@ class Command(BaseCommand): print request response = request.read() print response - print json.loads(response) \ No newline at end of file + print json.loads(response) diff --git a/src/content/migrations/0001_initial.py b/src/content/migrations/0001_initial.py index d12d159..79532a4 100644 --- a/src/content/migrations/0001_initial.py +++ b/src/content/migrations/0001_initial.py @@ -7,7 +7,6 @@ from django.conf import settings class Migration(migrations.Migration): - dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] @@ -16,17 +15,36 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Article', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('headline_de', models.CharField(max_length=255, verbose_name='Schlagzeile')), - ('headline_en', models.CharField(max_length=255, verbose_name=b'Headline', blank=True)), + ('id', models.AutoField( + verbose_name='ID', + serialize=False, auto_created=True, + primary_key=True)), + ('headline_de', models.CharField( + max_length=255, verbose_name='Schlagzeile')), + ('headline_en', models.CharField( + max_length=255, verbose_name=b'Headline', blank=True)), ('content_de', models.TextField(verbose_name='Inhalt')), - ('content_en', models.TextField(verbose_name=b'Content', blank=True)), - ('image', models.ImageField(upload_to=b'news/', null=True, verbose_name='Bild', blank=True)), - ('slug', models.SlugField(unique_for_month=b'date_created', verbose_name='Slug')), - ('status', models.SmallIntegerField(default=1, verbose_name='Status', choices=[(-1, 'Zur\xfcckgewiesen'), (0, 'Wartend...'), (1, 'Ver\xf6ffentlicht')])), - ('date_created', models.DateTimeField(verbose_name='Erstellt', blank=True)), - ('date_modified', models.DateTimeField(auto_now=True, verbose_name='Bearbeitet')), - ('author', models.ForeignKey(verbose_name='Autor', to=settings.AUTH_USER_MODEL)), + ('content_en', models.TextField( + verbose_name=b'Content', blank=True)), + ('image', models.ImageField( + upload_to=b'news/', + null=True, verbose_name='Bild', + blank=True)), + ('slug', models.SlugField( + unique_for_month=b'date_created', verbose_name='Slug')), + ('status', + models.SmallIntegerField( + default=1, verbose_name='Status', + choices=[ + (-1, 'Zur\xfcckgewiesen'), + (0, 'Wartend...'), + (1, 'Ver\xf6ffentlicht')])), + ('date_created', models.DateTimeField( + verbose_name='Erstellt', blank=True)), + ('date_modified', models.DateTimeField( + auto_now=True, verbose_name='Bearbeitet')), + ('author', models.ForeignKey( + verbose_name='Autor', to=settings.AUTH_USER_MODEL)), ], options={ 'ordering': ('-date_created',), @@ -37,12 +55,22 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Category', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name_de', models.CharField(max_length=80, verbose_name='Name')), - ('name_en', models.CharField(max_length=80, verbose_name='Name', blank=True)), - ('description_de', models.TextField(verbose_name='Beschreibung')), - ('description_en', models.TextField(verbose_name='Beschreibung', blank=True)), - ('image', models.ImageField(upload_to=b'news/categories/', null=True, verbose_name='Bild', blank=True)), + ('id', models.AutoField( + verbose_name='ID', + serialize=False, auto_created=True, + primary_key=True)), + ('name_de', + models.CharField(max_length=80, verbose_name='Name')), + ('name_en', models.CharField( + max_length=80, verbose_name='Name', blank=True)), + ('description_de', + models.TextField(verbose_name='Beschreibung')), + ('description_en', models.TextField( + verbose_name='Beschreibung', blank=True)), + ('image', models.ImageField( + upload_to=b'news/categories/', + null=True, verbose_name='Bild', + blank=True)), ('slug', models.SlugField(unique=True, verbose_name='Slug')), ], options={ @@ -54,23 +82,57 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Page', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('menu_name_de', models.CharField(help_text='Ein kurzer Name f\xfcr den Men\xfceintrag', max_length=255, verbose_name=b'Men\xc3\xbc Name')), - ('menu_name_en', models.CharField(help_text='Ein kurzer Name f\xfcr den Men\xfceintrag', max_length=255, verbose_name=b'Menu Name', blank=True)), - ('title_de', models.CharField(help_text='Der Titel erscheint im HTML Header', max_length=255, verbose_name=b'Titel')), - ('title_en', models.CharField(help_text='Der Titel erscheint im HTML Header', max_length=255, verbose_name=b'Title', blank=True)), + ('id', models.AutoField( + verbose_name='ID', + serialize=False, auto_created=True, + primary_key=True)), + ('menu_name_de', models.CharField( + help_text='Ein kurzer Name f\xfcr den Men\xfceintrag', + max_length=255, verbose_name=b'Men\xc3\xbc Name')), + ('menu_name_en', models.CharField( + help_text='Ein kurzer Name f\xfcr den Men\xfceintrag', + max_length=255, verbose_name=b'Menu Name', blank=True)), + ('title_de', models.CharField( + help_text='Der Titel erscheint im HTML Header', + max_length=255, verbose_name=b'Titel')), + ('title_en', models.CharField( + help_text='Der Titel erscheint im HTML Header', + max_length=255, verbose_name=b'Title', blank=True)), ('slug', models.SlugField(verbose_name='Slug')), - ('path', models.CharField(verbose_name='Pfad', unique=True, max_length=100, editable=False, db_index=True)), - ('position', models.PositiveSmallIntegerField(null=True, verbose_name='Position', blank=True)), - ('status', models.SmallIntegerField(default=0, verbose_name='Status', choices=[(-1, 'Zur\xfcckgewiesen'), (0, 'Wartend...'), (1, 'Ver\xf6ffentlicht')])), - ('content_type', models.IntegerField(choices=[(0, 'Django View'), (1, 'HTML'), (2, 'PDF')])), - ('content_de', models.TextField(verbose_name=b'Inhalt', blank=True)), - ('content_en', models.TextField(verbose_name=b'Content', blank=True)), - ('enable_comments', models.BooleanField(default=True, verbose_name='Kommentare m\xf6glich')), - ('template', models.CharField(default=b'content/page.html', max_length=100, verbose_name='Vorlage')), - ('pdf_de', models.FileField(null=True, upload_to=b'pdf/de/', blank=True)), - ('pdf_en', models.FileField(null=True, upload_to=b'pdf/en/', blank=True)), - ('parent', models.ForeignKey(related_name='subpages', on_delete=django.db.models.deletion.SET_NULL, blank=True, to='content.Page', null=True)), + ('path', models.CharField( + verbose_name='Pfad', unique=True, + max_length=100, editable=False, + db_index=True)), + ('position', models.PositiveSmallIntegerField( + null=True, verbose_name='Position', blank=True)), + ('status', + models.SmallIntegerField( + default=0, verbose_name='Status', + choices=[ + (-1, 'Zur\xfcckgewiesen'), + (0, 'Wartend...'), + (1, 'Ver\xf6ffentlicht')])), + ('content_type', models.IntegerField(choices=[ + (0, 'Django View'), (1, 'HTML'), (2, 'PDF')])), + ('content_de', + models.TextField(verbose_name=b'Inhalt', blank=True)), + ('content_en', models.TextField( + verbose_name=b'Content', blank=True)), + ('enable_comments', models.BooleanField( + default=True, verbose_name='Kommentare m\xf6glich')), + ('template', models.CharField( + default=b'content/page.html', + max_length=100, + verbose_name='Vorlage')), + ('pdf_de', models.FileField( + null=True, upload_to=b'pdf/de/', blank=True)), + ('pdf_en', models.FileField( + null=True, upload_to=b'pdf/en/', blank=True)), + ('parent', models.ForeignKey( + related_name='subpages', + on_delete=django.db.models.deletion.SET_NULL, + blank=True, to='content.Page', + null=True)), ], options={ 'ordering': ['parent__id', 'position'], @@ -81,7 +143,8 @@ class Migration(migrations.Migration): migrations.AddField( model_name='article', name='category', - field=models.ForeignKey(verbose_name='Kategorie', to='content.Category'), + field=models.ForeignKey( + verbose_name='Kategorie', to='content.Category'), ), migrations.AlterUniqueTogether( name='page', diff --git a/src/content/migrations/0002_auto_20150823_2232.py b/src/content/migrations/0002_auto_20150823_2232.py new file mode 100644 index 0000000..8625ebc --- /dev/null +++ b/src/content/migrations/0002_auto_20150823_2232.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import ckeditor.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('content', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='article', + name='content_de', + field=ckeditor.fields.RichTextField(verbose_name='Inhalt'), + ), + migrations.AlterField( + model_name='article', + name='content_en', + field=ckeditor.fields.RichTextField( + verbose_name=b'Content', blank=True), + ), + migrations.AlterField( + model_name='page', + name='content_de', + field=ckeditor.fields.RichTextField( + verbose_name=b'Inhalt', blank=True), + ), + migrations.AlterField( + model_name='page', + name='content_en', + field=ckeditor.fields.RichTextField( + verbose_name=b'Content', blank=True), + ), + ] diff --git a/src/content/models.py b/src/content/models.py index 9d91d16..b80590d 100644 --- a/src/content/models.py +++ b/src/content/models.py @@ -11,9 +11,6 @@ from django.utils.safestring import mark_safe from django.utils.translation import get_language, ugettext as _ from django.core.exceptions import ValidationError from ckeditor.fields import RichTextField - - -from kasu.image_models import ImageModel from utils import STATUS_CHOICES, STATUS_WAITING, STATUS_PUBLISHED, \ cleaner @@ -42,11 +39,15 @@ def get_upload_path(instance, filename): class ArticleManager(models.Manager): + + def get_queryset(self): + return super(ArticleManager, self).get_queryset().select_related('author', 'category') + def published(self): return self.filter(status=STATUS_PUBLISHED, date_created__lte=now()) -class Article(ImageModel): +class Article(models.Model): headline_de = models.CharField(_('Headline'), max_length=255) headline_en = models.CharField('Headline', max_length=255, blank=True) content_de = RichTextField(_('Content')) @@ -80,11 +81,11 @@ class Article(ImageModel): return self.headline @property - def posting_image(self): + def get_image(self): if self.image: - return self.article + return self.image else: - return self.category.article + return self.category.image def get_absolute_url(self): kwargs = { @@ -219,7 +220,7 @@ class Page(models.Model): verbose_name_plural = _('Pages') -class Category(ImageModel): +class Category(models.Model): name_de = models.CharField(_('Name'), max_length=80) name_en = models.CharField(_('Name'), max_length=80, blank=True) description_de = models.TextField(_('Description')) diff --git a/src/content/news_urls.py b/src/content/news_urls.py index ea50f3e..6ad91f0 100644 --- a/src/content/news_urls.py +++ b/src/content/news_urls.py @@ -27,4 +27,4 @@ urlpatterns = patterns( ArticleYearArchive.as_view(), name='article-archive'), url(r'^(?P[\-\d\w]+)/(?P[\d]{4})/(?P[\d]+)/$', ArticleMonthArchive.as_view(), name='article-archive'), -) \ No newline at end of file +) diff --git a/src/content/templates/content/article_archive.html b/src/content/templates/content/article_archive.html index 691d277..a9fa5c2 100644 --- a/src/content/templates/content/article_archive.html +++ b/src/content/templates/content/article_archive.html @@ -1,32 +1,33 @@ {% extends "base.html" %} -{% load i18n comments %} +{% load i18n comments thumbnail %} {% block title %} {% trans 'Article Archive' %} {% if active_category %} - {{active_category.name}}{% endif %} - {% if month %}{{ month|date:'F Y' }}{% elif year %}{{year}}{% endif %} + {% if month %}{{ month|date:'F Y' }}{% elif year %}{{year}}{% endif %} {% endblock %} -{% block meta_title %} - {% trans 'Article Archive' %} - {% if active_category %} - {{active_category.name}}{% endif %} - {% if month %}{{ month|date:'F Y' }}{% elif year %}{{year}}{% endif %} -{% endblock %} +{% block jumbotron_background %}{% spaceless %} + {% if active_category %} + {{ active_category.image.url }} + {% else %} + {{STATIC_URL}}img/teaser/{{current_top_page.slug}}.jpg + {% endif %} +{% endspaceless %}{% endblock %} -{% block jumbotron_background %}{% if active_category %}{{ active_category.image.url }}{% else %}{{STATIC_URL}}img/teaser/{{current_top_page.slug}}.jpg{% endif %}{% endblock %} - -{% block teaser %}

+{% block teaser %} +

{% trans 'Article Archive' %} {% if active_category %} - {{active_category.name}}{% endif %} {% if month %}{{ month|date:'F Y' }}{% elif year %}{{year}}{% endif %} -

-
- {% if active_category %} + +
+ {% if active_category %}

{{ active_category.description }}

- {% else %} + {% else %} {{current_page.content|safe}} - {% endif %} -
+ {% endif %} +
{% endblock %} {% block redbox %} @@ -67,7 +68,7 @@
  • {{ article.author }}
  • {{comment_count}} {% trans "comments" %}
  • - {{article.category}}: + {{article.category}}: {{article.content|truncatewords_html:50}} diff --git a/src/content/templates/content/article_detail.html b/src/content/templates/content/article_detail.html index 2b95cdb..1a4b606 100644 --- a/src/content/templates/content/article_detail.html +++ b/src/content/templates/content/article_detail.html @@ -1,5 +1,5 @@ {% extends "base.html" %} -{% load i18n comments %} +{% load i18n comments thumbnail %} {% block title %}{{ article.headline }}{% endblock %} @@ -9,9 +9,9 @@ - + - + {% endblock %} {% block itemscope %}itemscope itemtype="http://schema.org/Article"{% endblock %} @@ -21,7 +21,7 @@
    • {% trans 'Author' %}:
    • -
    • {% trans 'Created on' %}:
    • +
    • {% trans 'Created on' %}:
    • {% trans "Category"%}: {{article.category.name}}
    @@ -29,7 +29,7 @@ {% block maincontent %}
    - {{article.category.name}} + {{article.category.name}} {{ article.content }}

    diff --git a/src/content/templatetags/fieldset_extras.py b/src/content/templatetags/fieldset_extras.py index bec20a2..4c398b9 100644 --- a/src/content/templatetags/fieldset_extras.py +++ b/src/content/templatetags/fieldset_extras.py @@ -13,6 +13,7 @@ register = template.Library() class FieldSetNode(template.Node): + def __init__(self, form_variable, variable_name, fields): self.fields = fields self.variable_name = variable_name diff --git a/src/content/tests.py b/src/content/tests.py index 501deb7..75f2f59 100644 --- a/src/content/tests.py +++ b/src/content/tests.py @@ -9,6 +9,7 @@ from django.test import TestCase class SimpleTest(TestCase): + def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. diff --git a/src/content/urls.py b/src/content/urls.py index 1bb05c1..acd3041 100644 --- a/src/content/urls.py +++ b/src/content/urls.py @@ -12,4 +12,4 @@ urlpatterns = patterns( 'content.views', url(r'^image_list.js$', ImageList.as_view(), name='content-image-list'), url(r'^link_list.js$', PageList.as_view(), name='content-page-list'), -) \ No newline at end of file +) diff --git a/src/content/views.py b/src/content/views.py index 49c22a2..34f359c 100644 --- a/src/content/views.py +++ b/src/content/views.py @@ -8,15 +8,16 @@ from django.utils.translation import ugettext as _ from django.views import generic from . import models, forms -from aggregator.models import Feed from events.models import Photo from utils.mixins import PermissionRequiredMixin class ArticleArchiveMixin(object): + def get_category(self, queryset): try: - self.category = models.Category.objects.get(slug=self.kwargs['category']) + self.category = models.Category.objects.get( + slug=self.kwargs['category']) return queryset.filter(category=self.category) except models.Category.DoesNotExist: raise Http404(_("This Category does not exist.")) @@ -28,7 +29,6 @@ class ArticleArchiveMixin(object): context = super(ArticleArchiveMixin, self).get_context_data(**kwargs) context['categories'] = models.Category.objects.all() context['active_category'] = self.category - context['feeds'] = Feed.objects.active() return context @@ -99,6 +99,7 @@ class ArticleForm(PermissionRequiredMixin, generic.UpdateView): class ImageList(generic.View): # noinspection PyMethodMayBeStatic + def get(self, kwargs): image_list = [] response = HttpResponse(content_type='text/javascript') @@ -146,6 +147,7 @@ class PageEditForm(PermissionRequiredMixin, generic.UpdateView): class PageHtml(generic.DetailView): + def get_object(self, queryset=None): try: return models.Page.objects.get(path=self.kwargs['path'], @@ -160,6 +162,7 @@ class PageHtml(generic.DetailView): class PagePdf(generic.DeleteView): + def get_object(self, queryset=None): try: return models.Page.objects.get(path=self.kwargs['path'], @@ -183,6 +186,7 @@ class PagePdf(generic.DeleteView): class PageList(generic.View): # noinspection PyMethodMayBeStatic + def get(self, kwargs): response = HttpResponse(content_type='text/javascript') response.write('var tinyMCELinkList = new Array(') @@ -212,7 +216,7 @@ class StartPage(generic.TemplateView): context = { 'title': page.title, 'content': page.content, - 'random_photo': random_photo.callout, + 'random_photo': random_photo, 'recent_article_list': models.Article.objects.published()[:3], 'recent_comment_list': recent_comment_list, } diff --git a/src/events/admin.py b/src/events/admin.py index fffbc70..53d045a 100644 --- a/src/events/admin.py +++ b/src/events/admin.py @@ -5,7 +5,6 @@ Created on 19.09.2011 """ # import stuff we need from django from django.contrib import admin -from imagekit.admin import AdminThumbnail from django.utils.translation import gettext as _ from events.models import Event, Photo, Location @@ -19,7 +18,7 @@ class EventInline(admin.TabularInline): class EventAdmin(admin.ModelAdmin): list_display = ('name', 'start', 'end', 'location',) - list_editable = ('start', 'end', 'location',) + list_editable = ('start', 'end', 'location') readonly_fields = ('event_series',) date_hierarchy = 'start' search_fields = ('name', 'description') @@ -32,12 +31,11 @@ class LocationAdmin(admin.ModelAdmin): class PhotoAdmin(admin.ModelAdmin): - admin_thumbnail = AdminThumbnail(image_field='thumbnail') + # admin_thumbnail = AdminThumbnail(image_field='thumbnail') fields = ('image', 'event', 'name', 'description', - ('anchor_horizontal', 'anchor_vertical'), ('photographer', 'created_date')) list_filter = ('event', 'on_startpage',) - list_display = ('admin_thumbnail', 'image', 'name', 'event', + list_display = ('image', 'image', 'name', 'event', 'photographer', 'on_startpage') list_display_links = ('image',) list_editable = ('on_startpage', 'name', 'event', 'photographer') diff --git a/src/events/context_processors.py b/src/events/context_processors.py index 1e78731..0708049 100644 --- a/src/events/context_processors.py +++ b/src/events/context_processors.py @@ -4,12 +4,30 @@ Created on 30.09.2011 @author: christian """ -from . import models +from django.core.cache import cache +from .models import Event def upcoming_events(request): + current_event = cache.get('current_event', False) + next_event = cache.get('next_event', False) + upcoming_events = cache.get('upcoming_events', False) + + if current_event == False: + current_event = Event.objects.current_event() + cache.set('current_event', current_event, 360) + if next_event == False: + next_event = Event.objects.next_event() + cache.set('next_event', next_event, 360) + + if not upcoming_events and current_event: + upcoming_events = Event.objects.upcoming(limit=3) + cache.set('upcoming_events', upcoming_events, 360) + elif not upcoming_events: + upcoming_events = Event.objects.upcoming()[1:4] + cache.set('upcoming_events', upcoming_events, 360) return { - 'current_event': models.Event.objects.current_event(), - 'next_event': models.Event.objects.next_event(), - 'upcoming_events': models.Event.objects.upcoming(), + 'current_event': current_event, + 'next_event': next_event, + 'upcoming_events': upcoming_events } diff --git a/src/events/forms.py b/src/events/forms.py index e2e0dda..2eeecfe 100644 --- a/src/events/forms.py +++ b/src/events/forms.py @@ -38,7 +38,6 @@ class EditPhotoForm(forms.ModelForm): class Meta(object): model = models.Photo fields = ('event', 'name', 'description', 'photographer', - 'anchor_horizontal', 'anchor_vertical', 'created_date', 'on_startpage') @@ -59,4 +58,5 @@ class EventForm(forms.ModelForm): model = models.Event exclude = ('event_count', 'event_series', ) -EventSeriesFormset = forms.inlineformset_factory(models.Event, models.Event, fields=('start', 'end'), form=EventForm) \ No newline at end of file +EventSeriesFormset = forms.inlineformset_factory( + models.Event, models.Event, fields=('start', 'end'), form=EventForm) diff --git a/src/gallery/urls.py b/src/events/gallery_urls.py similarity index 99% rename from src/gallery/urls.py rename to src/events/gallery_urls.py index 174fdbd..f681e53 100644 --- a/src/gallery/urls.py +++ b/src/events/gallery_urls.py @@ -15,4 +15,4 @@ urlpatterns = patterns( url(r'^delete/(?P[\d]+)/$', DeleteEventPhoto.as_view(), name='delete-event-photo'), url(r'^upload/$', EventPhotoUpload.as_view(), name='event-photo-upload'), -) \ No newline at end of file +) diff --git a/src/events/migrations/0001_initial.py b/src/events/migrations/0001_initial.py index d4e667a..9b6ff67 100644 --- a/src/events/migrations/0001_initial.py +++ b/src/events/migrations/0001_initial.py @@ -16,16 +16,23 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Event', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('id', models.AutoField(verbose_name='ID', + serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=255, verbose_name='Name')), - ('description', models.TextField(verbose_name='Beschreibung', blank=True)), + ('description', models.TextField( + verbose_name='Beschreibung', blank=True)), ('start', models.DateTimeField(verbose_name='Beginn')), - ('end', models.DateTimeField(null=True, verbose_name='Ende', blank=True)), + ('end', models.DateTimeField( + null=True, verbose_name='Ende', blank=True)), ('url', models.URLField(verbose_name='Homepage', blank=True)), - ('image', models.ImageField(storage=utils.OverwriteStorage(), upload_to=events.models.get_upload_path, null=True, verbose_name='Bild', blank=True)), - ('is_tournament', models.BooleanField(default=False, help_text='Diese Veranstaltung ist ein Turnier, es gelten andere Regeln f\xfcr das Kyu Ranking.', verbose_name='Turnier')), - ('photo_count', models.PositiveIntegerField(default=0, editable=False)), - ('event_series', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, editable=False, to='events.Event', blank=True, help_text='Wenn dieser Termin zu einer Veranstaltungsreihe geh\xf6rt werden Ort, Beschreibung, Bild und Homepage von dem hier angegebenen Event \xfcbernommen.', null=True, verbose_name='Veranstaltungsreihen')), + ('image', models.ImageField(storage=utils.OverwriteStorage( + ), upload_to=events.models.get_upload_path, null=True, verbose_name='Bild', blank=True)), + ('is_tournament', models.BooleanField(default=False, + help_text='Diese Veranstaltung ist ein Turnier, es gelten andere Regeln f\xfcr das Kyu Ranking.', verbose_name='Turnier')), + ('photo_count', models.PositiveIntegerField( + default=0, editable=False)), + ('event_series', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, editable=False, to='events.Event', blank=True, + help_text='Wenn dieser Termin zu einer Veranstaltungsreihe geh\xf6rt werden Ort, Beschreibung, Bild und Homepage von dem hier angegebenen Event \xfcbernommen.', null=True, verbose_name='Veranstaltungsreihen')), ], options={ 'ordering': ('-start', '-end'), @@ -36,15 +43,21 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Location', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('id', models.AutoField(verbose_name='ID', + serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=200, verbose_name='Name')), - ('description', models.TextField(verbose_name='Beschreibung', blank=True)), - ('image', models.ImageField(storage=utils.OverwriteStorage(), upload_to=events.models.get_upload_path, null=True, verbose_name='Bild', blank=True)), + ('description', models.TextField( + verbose_name='Beschreibung', blank=True)), + ('image', models.ImageField(storage=utils.OverwriteStorage( + ), upload_to=events.models.get_upload_path, null=True, verbose_name='Bild', blank=True)), ('url', models.URLField(verbose_name='Homepage', blank=True)), - ('postal_code', models.CharField(max_length=6, verbose_name='Postleitzahl')), - ('street_address', models.CharField(max_length=127, verbose_name='Stra\xdfe')), + ('postal_code', models.CharField( + max_length=6, verbose_name='Postleitzahl')), + ('street_address', models.CharField( + max_length=127, verbose_name='Stra\xdfe')), ('locality', models.CharField(max_length=127, verbose_name='Ort')), - ('country', models.CharField(max_length=2, verbose_name='Land', choices=[(b'GB', 'Vereinigtes K\xf6nigreich'), (b'AF', 'Afghanistan'), (b'AX', 'Aland Islands'), (b'AL', 'Albanien'), (b'DZ', 'Algerien'), (b'AS', 'Amerikanisch-Samoa'), (b'AD', 'Andorra'), (b'AO', 'Angola'), (b'AI', 'Anguilla'), (b'AQ', 'Antarktika'), (b'AG', 'Antigua und Barbuda'), (b'AR', 'Argentinien'), (b'AM', 'Armenien'), (b'AW', 'Aruba'), (b'AU', 'Australien'), (b'AT', '\xd6sterreich'), (b'AZ', 'Aserbaidschan'), (b'BS', 'Bahamas'), (b'BH', 'Bahrein'), (b'BD', 'Bangladesch'), (b'BB', 'Barbados'), (b'BY', 'Wei\xdfrussland'), (b'BE', 'Belgien'), (b'BZ', 'Belize'), (b'BJ', 'Benin'), (b'BM', 'Bermuda'), (b'BT', 'Bhutan'), (b'BO', 'Bolivien'), (b'BA', 'Bosnien und Herzegowina'), (b'BW', 'Botswana'), (b'BV', 'Bouvet Island'), (b'BR', 'Brasilien'), (b'IO', 'British Indian Ocean Territory'), (b'BN', 'Brunei Darussalam'), (b'BG', 'Bulgarien'), (b'BF', 'Burkina Faso'), (b'BI', 'Burundi'), (b'KH', 'Kambodscha'), (b'CM', 'Kamerun'), (b'CA', 'Kanada'), (b'CV', 'Cape Verde'), (b'KY', 'Cayman Islands'), (b'CF', 'Zentralafrikanische Republik'), (b'TD', 'Tschad'), (b'CL', 'Chile'), (b'CN', 'China'), (b'CX', 'Christmas Island'), (b'CC', 'Cocos (Keeling) Islands'), (b'CO', 'Kolumbien'), (b'KM', 'Komoren'), (b'CG', 'Kongo'), (b'CD', 'Kongo, Demokratische Republik'), (b'CK', 'Cook-Inseln'), (b'CR', 'Costa Rica'), (b'CI', "Cote d'Ivoire"), (b'HR', 'Kroatien'), (b'CU', 'Kuba'), (b'CY', 'Zypern'), (b'CZ', 'Tschechische Republik'), (b'DK', 'D\xe4nemark'), (b'DJ', 'Dschibuti'), (b'DM', 'Dominica'), (b'DO', 'Dominikanische Republik'), (b'EC', 'Ecuador'), (b'EG', '\xc4gypten'), (b'SV', 'El Salvador'), (b'GQ', '\xc4quatorial-Guinea'), (b'ER', 'Eritrea'), (b'EE', 'Estland'), (b'ET', '\xc4thiopien'), (b'FK', 'Falklandinseln (Malvinas)'), (b'FO', 'F\xe4r\xf6er-Inseln'), (b'FJ', 'Fidschi'), (b'FI', 'Finnland'), (b'FR', 'Frankreich'), (b'GF', 'Franz\xf6sisch-Guayana'), (b'PF', 'Franz\xf6sisch-Polynesien'), (b'TF', 'Franz\xf6sisch S\xfcdliche Territorien'), (b'GA', 'Gabun'), (b'GM', 'Gambia'), (b'GE', 'Georgia'), (b'DE', 'Deutschland'), (b'GH', 'Ghana'), (b'GI', 'Gibraltar'), (b'GR', 'Griechenland'), (b'GL', 'Gr\xf6nland'), (b'GD', 'Grenada'), (b'GP', 'Guadeloupe'), (b'GU', 'Guam'), (b'GT', 'Guatemala'), (b'GG', 'Guernsey'), (b'GN', 'Guinea'), (b'GW', 'Guinea-Bissau'), (b'GY', 'Guyana'), (b'HT', 'Haiti'), (b'HM', 'Heard und McDonald Inseln'), (b'VA', 'Heiliger Stuhl (Vatikanstadt)'), (b'HN', 'Honduras'), (b'HK', 'Hongkong'), (b'HU', 'Ungarn'), (b'IS', 'Island'), (b'IN', 'Indien'), (b'ID', 'Indonesien'), (b'IR', 'Iran, Islamische Republik'), (b'IQ', 'Irak'), (b'IE', 'Irland'), (b'IM', 'Isle of Man'), (b'IL', 'Israel'), (b'IT', 'Italien'), (b'JM', 'Jamaika'), (b'JP', 'Japan'), (b'JE', 'Jersey'), (b'JO', 'Jordan'), (b'KZ', 'Kasachstan'), (b'KE', 'Kenia'), (b'KI', 'Kiribati'), (b'KP', 'Korea, Demokratische Volksrepublik'), (b'KR', 'Korea, Republik'), (b'KW', 'Kuwait'), (b'KG', 'Kirgisistan'), (b'LA', 'Lao Demokratischen Volksrepublik'), (b'LV', 'Lettland'), (b'LB', 'Libanon'), (b'LS', 'Lesotho'), (b'LR', 'Liberia'), (b'LY', 'Libyen'), (b'LI', 'Liechtenstein'), (b'LT', 'Litauen'), (b'LU', 'Luxemburg'), (b'MO', 'Macao'), (b'MK', 'Mazedonien, die ehemalige jugoslawische Republik'), (b'MG', 'Madagaskar'), (b'MW', 'Malawi'), (b'MY', 'Malaysia'), (b'MV', 'Malediven'), (b'ML', 'Mali'), (b'MT', 'Malta'), (b'MH', 'Marshall Islands'), (b'MQ', 'Martinique'), (b'MR', 'Mauretanien'), (b'MU', 'Mauritius'), (b'YT', 'Mayotte'), (b'MX', 'Mexiko'), (b'FM', 'Mikronesien, F\xf6derierte Staaten von'), (b'MD', 'Moldawien'), (b'MC', 'Monaco'), (b'MN', 'Mongolei'), (b'ME', 'Montenegro'), (b'MS', 'Montserrat'), (b'MA', 'Marokko'), (b'MZ', 'Mosambik'), (b'MM', 'Myanmar'), (b'NA', 'Namibia'), (b'NR', 'Nauru'), (b'NP', 'Nepal'), (b'NL', 'Niederlande'), (b'AN', 'Niederl\xe4ndische Antillen'), (b'NC', 'Neukaledonien'), (b'NZ', 'New Zealand'), (b'NI', 'Nicaragua'), (b'NE', 'Niger'), (b'NG', 'Nigeria'), (b'NU', 'Niue'), (b'NF', 'Norfolk Island'), (b'MP', 'Northern Mariana Islands'), (b'NO', 'Norwegen'), (b'OM', 'Oman'), (b'PK', 'Pakistan'), (b'PW', 'Palau'), (b'PS', 'Pal\xe4stinensische Autonomiegebiete'), (b'PA', 'Panama'), (b'PG', 'Papua-Neuguinea'), (b'PY', 'Paraguay'), (b'PE', 'Peru'), (b'PH', 'Philippinen'), (b'PN', 'Pitcairn'), (b'PL', 'Polen'), (b'PT', 'Portugal'), (b'PR', 'Puerto Rico'), (b'QA', 'Katar'), (b'RE', 'Wiedervereinigung'), (b'RO', 'Rum\xe4nien'), (b'RU', 'Russischen F\xf6deration'), (b'RW', 'Ruanda'), (b'BL', 'Saint Barthelemy'), (b'SH', 'Saint Helena'), (b'KN', 'Saint Kitts und Nevis'), (b'LC', 'Santa Lucia'), (b'MF', 'Santa Martin'), (b'PM', 'Saint Pierre und Miquelon'), (b'VC', 'Saint Vincent und die Grenadinen'), (b'WS', 'Samoa'), (b'SM', 'San Marino'), (b'ST', 'Sao Tome und Principe'), (b'SA', 'Saudi-Arabien'), (b'SN', 'Senegal'), (b'RS', 'Serbien'), (b'SC', 'Seychellen'), (b'SL', 'Sierra Leone'), (b'SG', 'Singapur'), (b'SK', 'Slowakei'), (b'SI', 'Slowenien'), (b'SB', 'Salomon-Inseln'), (b'SO', 'Somalia'), (b'ZA', 'S\xfcdafrika'), (b'GS', 'S\xfcdgeorgien und die S\xfcdlichen Sandwichinseln'), (b'ES', 'Spanien'), (b'LK', 'Sri Lanka'), (b'SD', 'Sudan'), (b'SR', 'Suriname'), (b'SJ', 'Svalbard und Jan Mayen'), (b'SZ', 'Swaziland'), (b'SE', 'Schweden'), (b'CH', 'Schweiz'), (b'SY', 'Arabische Republik Syrien'), (b'TW', 'Taiwan, Province of China'), (b'TJ', 'Tadschikistan'), (b'TZ', 'Tansania, Vereinigte Republik'), (b'TH', 'Thailand'), (b'TL', 'Timor-Leste'), (b'TG', 'Togo'), (b'TK', 'Tokelau'), (b'TO', 'Tonga'), (b'TT', 'Trinidad und Tobago'), (b'TN', 'Tunesien'), (b'TR', 'T\xfcrkei'), (b'TM', 'Turkmenistan'), (b'TC', 'Turks-und Caicosinseln'), (b'TV', 'Tuvalu'), (b'UG', 'Uganda'), (b'UA', 'Ukraine'), (b'AE', 'Vereinigte Arabische Emirate'), (b'US', 'Vereinigte Staaten'), (b'UM', 'United States Minor Outlying Islands'), (b'UY', 'Uruguay'), (b'UZ', 'Usbekistan'), (b'VU', 'Vanuatu'), (b'VE', 'Venezuela'), (b'VN', 'Vietnam'), (b'VG', 'Virgin Islands, British'), (b'VI', 'Virgin Islands, US'), (b'WF', 'Wallis und Futuna'), (b'EH', 'Westsahara'), (b'YE', 'Jemen'), (b'ZM', 'Sambia'), (b'ZW', 'Zimbabwe')])), + ('country', models.CharField(max_length=2, verbose_name='Land', choices=[(b'GB', 'Vereinigtes K\xf6nigreich'), (b'AF', 'Afghanistan'), (b'AX', 'Aland Islands'), (b'AL', 'Albanien'), (b'DZ', 'Algerien'), (b'AS', 'Amerikanisch-Samoa'), (b'AD', 'Andorra'), (b'AO', 'Angola'), (b'AI', 'Anguilla'), (b'AQ', 'Antarktika'), (b'AG', 'Antigua und Barbuda'), (b'AR', 'Argentinien'), (b'AM', 'Armenien'), (b'AW', 'Aruba'), (b'AU', 'Australien'), (b'AT', '\xd6sterreich'), (b'AZ', 'Aserbaidschan'), (b'BS', 'Bahamas'), (b'BH', 'Bahrein'), (b'BD', 'Bangladesch'), (b'BB', 'Barbados'), (b'BY', 'Wei\xdfrussland'), (b'BE', 'Belgien'), (b'BZ', 'Belize'), (b'BJ', 'Benin'), (b'BM', 'Bermuda'), (b'BT', 'Bhutan'), (b'BO', 'Bolivien'), (b'BA', 'Bosnien und Herzegowina'), (b'BW', 'Botswana'), (b'BV', 'Bouvet Island'), (b'BR', 'Brasilien'), (b'IO', 'British Indian Ocean Territory'), (b'BN', 'Brunei Darussalam'), (b'BG', 'Bulgarien'), (b'BF', 'Burkina Faso'), (b'BI', 'Burundi'), (b'KH', 'Kambodscha'), (b'CM', 'Kamerun'), (b'CA', 'Kanada'), (b'CV', 'Cape Verde'), (b'KY', 'Cayman Islands'), (b'CF', 'Zentralafrikanische Republik'), (b'TD', 'Tschad'), (b'CL', 'Chile'), (b'CN', 'China'), (b'CX', 'Christmas Island'), (b'CC', 'Cocos (Keeling) Islands'), (b'CO', 'Kolumbien'), (b'KM', 'Komoren'), (b'CG', 'Kongo'), (b'CD', 'Kongo, Demokratische Republik'), (b'CK', 'Cook-Inseln'), (b'CR', 'Costa Rica'), (b'CI', "Cote d'Ivoire"), (b'HR', 'Kroatien'), (b'CU', 'Kuba'), (b'CY', 'Zypern'), (b'CZ', 'Tschechische Republik'), (b'DK', 'D\xe4nemark'), (b'DJ', 'Dschibuti'), (b'DM', 'Dominica'), (b'DO', 'Dominikanische Republik'), (b'EC', 'Ecuador'), (b'EG', '\xc4gypten'), (b'SV', 'El Salvador'), (b'GQ', '\xc4quatorial-Guinea'), (b'ER', 'Eritrea'), (b'EE', 'Estland'), (b'ET', '\xc4thiopien'), (b'FK', 'Falklandinseln (Malvinas)'), (b'FO', 'F\xe4r\xf6er-Inseln'), (b'FJ', 'Fidschi'), (b'FI', 'Finnland'), (b'FR', 'Frankreich'), (b'GF', 'Franz\xf6sisch-Guayana'), (b'PF', 'Franz\xf6sisch-Polynesien'), (b'TF', 'Franz\xf6sisch S\xfcdliche Territorien'), (b'GA', 'Gabun'), (b'GM', 'Gambia'), (b'GE', 'Georgia'), (b'DE', 'Deutschland'), (b'GH', 'Ghana'), (b'GI', 'Gibraltar'), (b'GR', 'Griechenland'), (b'GL', 'Gr\xf6nland'), (b'GD', 'Grenada'), (b'GP', 'Guadeloupe'), (b'GU', 'Guam'), (b'GT', 'Guatemala'), (b'GG', 'Guernsey'), (b'GN', 'Guinea'), (b'GW', 'Guinea-Bissau'), (b'GY', 'Guyana'), (b'HT', 'Haiti'), (b'HM', 'Heard und McDonald Inseln'), (b'VA', 'Heiliger Stuhl (Vatikanstadt)'), (b'HN', 'Honduras'), (b'HK', 'Hongkong'), (b'HU', 'Ungarn'), (b'IS', 'Island'), (b'IN', 'Indien'), (b'ID', 'Indonesien'), (b'IR', 'Iran, Islamische Republik'), (b'IQ', 'Irak'), (b'IE', 'Irland'), (b'IM', 'Isle of Man'), (b'IL', 'Israel'), (b'IT', 'Italien'), (b'JM', 'Jamaika'), (b'JP', 'Japan'), (b'JE', 'Jersey'), (b'JO', 'Jordan'), (b'KZ', 'Kasachstan'), (b'KE', 'Kenia'), (b'KI', 'Kiribati'), (b'KP', 'Korea, Demokratische Volksrepublik'), (b'KR', 'Korea, Republik'), (b'KW', 'Kuwait'), (b'KG', 'Kirgisistan'), (b'LA', 'Lao Demokratischen Volksrepublik'), (b'LV', 'Lettland'), (b'LB', 'Libanon'), ( + b'LS', 'Lesotho'), (b'LR', 'Liberia'), (b'LY', 'Libyen'), (b'LI', 'Liechtenstein'), (b'LT', 'Litauen'), (b'LU', 'Luxemburg'), (b'MO', 'Macao'), (b'MK', 'Mazedonien, die ehemalige jugoslawische Republik'), (b'MG', 'Madagaskar'), (b'MW', 'Malawi'), (b'MY', 'Malaysia'), (b'MV', 'Malediven'), (b'ML', 'Mali'), (b'MT', 'Malta'), (b'MH', 'Marshall Islands'), (b'MQ', 'Martinique'), (b'MR', 'Mauretanien'), (b'MU', 'Mauritius'), (b'YT', 'Mayotte'), (b'MX', 'Mexiko'), (b'FM', 'Mikronesien, F\xf6derierte Staaten von'), (b'MD', 'Moldawien'), (b'MC', 'Monaco'), (b'MN', 'Mongolei'), (b'ME', 'Montenegro'), (b'MS', 'Montserrat'), (b'MA', 'Marokko'), (b'MZ', 'Mosambik'), (b'MM', 'Myanmar'), (b'NA', 'Namibia'), (b'NR', 'Nauru'), (b'NP', 'Nepal'), (b'NL', 'Niederlande'), (b'AN', 'Niederl\xe4ndische Antillen'), (b'NC', 'Neukaledonien'), (b'NZ', 'New Zealand'), (b'NI', 'Nicaragua'), (b'NE', 'Niger'), (b'NG', 'Nigeria'), (b'NU', 'Niue'), (b'NF', 'Norfolk Island'), (b'MP', 'Northern Mariana Islands'), (b'NO', 'Norwegen'), (b'OM', 'Oman'), (b'PK', 'Pakistan'), (b'PW', 'Palau'), (b'PS', 'Pal\xe4stinensische Autonomiegebiete'), (b'PA', 'Panama'), (b'PG', 'Papua-Neuguinea'), (b'PY', 'Paraguay'), (b'PE', 'Peru'), (b'PH', 'Philippinen'), (b'PN', 'Pitcairn'), (b'PL', 'Polen'), (b'PT', 'Portugal'), (b'PR', 'Puerto Rico'), (b'QA', 'Katar'), (b'RE', 'Wiedervereinigung'), (b'RO', 'Rum\xe4nien'), (b'RU', 'Russischen F\xf6deration'), (b'RW', 'Ruanda'), (b'BL', 'Saint Barthelemy'), (b'SH', 'Saint Helena'), (b'KN', 'Saint Kitts und Nevis'), (b'LC', 'Santa Lucia'), (b'MF', 'Santa Martin'), (b'PM', 'Saint Pierre und Miquelon'), (b'VC', 'Saint Vincent und die Grenadinen'), (b'WS', 'Samoa'), (b'SM', 'San Marino'), (b'ST', 'Sao Tome und Principe'), (b'SA', 'Saudi-Arabien'), (b'SN', 'Senegal'), (b'RS', 'Serbien'), (b'SC', 'Seychellen'), (b'SL', 'Sierra Leone'), (b'SG', 'Singapur'), (b'SK', 'Slowakei'), (b'SI', 'Slowenien'), (b'SB', 'Salomon-Inseln'), (b'SO', 'Somalia'), (b'ZA', 'S\xfcdafrika'), (b'GS', 'S\xfcdgeorgien und die S\xfcdlichen Sandwichinseln'), (b'ES', 'Spanien'), (b'LK', 'Sri Lanka'), (b'SD', 'Sudan'), (b'SR', 'Suriname'), (b'SJ', 'Svalbard und Jan Mayen'), (b'SZ', 'Swaziland'), (b'SE', 'Schweden'), (b'CH', 'Schweiz'), (b'SY', 'Arabische Republik Syrien'), (b'TW', 'Taiwan, Province of China'), (b'TJ', 'Tadschikistan'), (b'TZ', 'Tansania, Vereinigte Republik'), (b'TH', 'Thailand'), (b'TL', 'Timor-Leste'), (b'TG', 'Togo'), (b'TK', 'Tokelau'), (b'TO', 'Tonga'), (b'TT', 'Trinidad und Tobago'), (b'TN', 'Tunesien'), (b'TR', 'T\xfcrkei'), (b'TM', 'Turkmenistan'), (b'TC', 'Turks-und Caicosinseln'), (b'TV', 'Tuvalu'), (b'UG', 'Uganda'), (b'UA', 'Ukraine'), (b'AE', 'Vereinigte Arabische Emirate'), (b'US', 'Vereinigte Staaten'), (b'UM', 'United States Minor Outlying Islands'), (b'UY', 'Uruguay'), (b'UZ', 'Usbekistan'), (b'VU', 'Vanuatu'), (b'VE', 'Venezuela'), (b'VN', 'Vietnam'), (b'VG', 'Virgin Islands, British'), (b'VI', 'Virgin Islands, US'), (b'WF', 'Wallis und Futuna'), (b'EH', 'Westsahara'), (b'YE', 'Jemen'), (b'ZM', 'Sambia'), (b'ZW', 'Zimbabwe')])), ], options={ 'verbose_name': 'Veranstaltungsort', diff --git a/src/events/migrations/0002_auto_20150818_2139.py b/src/events/migrations/0002_auto_20150818_2139.py index 41265c7..5f5f288 100644 --- a/src/events/migrations/0002_auto_20150818_2139.py +++ b/src/events/migrations/0002_auto_20150818_2139.py @@ -18,11 +18,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='event', name='mahjong_season', - field=models.PositiveSmallIntegerField(null=True, verbose_name='Mahjong Season', blank=True), + field=models.PositiveSmallIntegerField( + null=True, verbose_name='Mahjong Season', blank=True), ), migrations.AddField( model_name='event', name='mahjong_tournament', - field=models.BooleanField(default=False, help_text='Diese Veranstaltung ist ein Turnier, es gelten andere Regeln f\xfcr das Kyu Ranking.', verbose_name='Mahjong Tournament'), + field=models.BooleanField( + default=False, help_text='Diese Veranstaltung ist ein Turnier, es gelten andere Regeln f\xfcr das Kyu Ranking.', verbose_name='Mahjong Tournament'), ), ] diff --git a/src/events/migrations/0003_auto_20150823_2232.py b/src/events/migrations/0003_auto_20150823_2232.py new file mode 100644 index 0000000..29cae68 --- /dev/null +++ b/src/events/migrations/0003_auto_20150823_2232.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0002_auto_20150818_2139'), + ] + + operations = [ + migrations.AlterField( + model_name='event', + name='mahjong_tournament', + field=models.BooleanField( + default=False, help_text='Diese Veranstaltung ist ein Turnier, es gelten andere Regeln f\xfcr das Kyu Ranking.', verbose_name='Mahjong Turnier'), + ), + ] diff --git a/src/events/migrations/0004_auto_20150901_2204.py b/src/events/migrations/0004_auto_20150901_2204.py new file mode 100644 index 0000000..c981e16 --- /dev/null +++ b/src/events/migrations/0004_auto_20150901_2204.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import ckeditor.fields +import events.models +import easy_thumbnails.fields +import django.db.models.deletion +import utils +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('events', '0003_auto_20150823_2232'), + ] + + operations = [ + migrations.CreateModel( + name='Photo', + fields=[ + ('id', models.AutoField(verbose_name='ID', + serialize=False, auto_created=True, primary_key=True)), + ('name', models.CharField(max_length=100, + verbose_name='Name', blank=True)), + ('image', easy_thumbnails.fields.ThumbnailerImageField( + upload_to=events.models.get_upload_path, storage=utils.OverwriteStorage(), verbose_name='Bild')), + ('description', models.TextField(max_length=300, + verbose_name='Beschreibung', blank=True)), + ('on_startpage', models.BooleanField(default=False, + help_text='Display this Photo on the Startpage Teaser', verbose_name='Startpage')), + ('created_date', models.DateTimeField(verbose_name='Published on')), + ('views', models.PositiveIntegerField(default=0, + verbose_name='Number of views', editable=False)), + ], + options={ + 'ordering': ['created_date'], + 'db_table': 'events_photo', + 'verbose_name': 'Veranstaltungsbild', + 'verbose_name_plural': 'Event Images', + 'get_latest_by': 'created_date', + }, + ), + migrations.AlterModelOptions( + name='event', + options={'ordering': ( + 'start', 'end'), 'verbose_name': 'Termin', 'verbose_name_plural': 'Termine'}, + ), + migrations.AlterField( + model_name='event', + name='description', + field=ckeditor.fields.RichTextField( + verbose_name='Beschreibung', blank=True), + ), + migrations.AlterField( + model_name='event', + name='event_series', + field=models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to='events.Event', + help_text='Wenn dieser Termin zu einer Veranstaltungsreihe geh\xf6rt werden Ort, Beschreibung, Bild und Homepage von dem hier angegebenen Event \xfcbernommen.', null=True, verbose_name='Veranstaltungsreihen'), + ), + migrations.AlterField( + model_name='event', + name='image', + field=easy_thumbnails.fields.ThumbnailerImageField(storage=utils.OverwriteStorage( + ), upload_to=events.models.get_upload_path, null=True, verbose_name='Bild', blank=True), + ), + migrations.AlterField( + model_name='location', + name='description', + field=ckeditor.fields.RichTextField( + verbose_name='Beschreibung', blank=True), + ), + migrations.AlterField( + model_name='location', + name='image', + field=easy_thumbnails.fields.ThumbnailerImageField(storage=utils.OverwriteStorage( + ), upload_to=events.models.get_upload_path, null=True, verbose_name='Bild', blank=True), + ), + migrations.AddField( + model_name='photo', + name='event', + field=models.ForeignKey(to='events.Event'), + ), + migrations.AddField( + model_name='photo', + name='photographer', + field=models.ForeignKey(to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/src/events/migrations/0005_auto_20150907_2021.py b/src/events/migrations/0005_auto_20150907_2021.py new file mode 100644 index 0000000..622d955 --- /dev/null +++ b/src/events/migrations/0005_auto_20150907_2021.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0004_auto_20150901_2204'), + ] + + operations = [ + migrations.AlterField( + model_name='photo', + name='on_startpage', + field=models.BooleanField( + default=False, help_text='Display this Photo on the Startpage Teaser', db_index=True, verbose_name='Startpage'), + ), + ] diff --git a/src/events/models.py b/src/events/models.py index 528ccdc..2958f30 100644 --- a/src/events/models.py +++ b/src/events/models.py @@ -1,14 +1,17 @@ # -'- Encoding: utf-8 -*- +import os +from django.conf import settings from django.core.urlresolvers import reverse from django.db import models +from django.db.models import Q from django.template.defaultfilters import slugify from django.utils.timezone import now from django.utils.translation import ugettext as _ +from ckeditor.fields import RichTextField +from easy_thumbnails.fields import ThumbnailerImageField from utils import COUNTRIES, OverwriteStorage -from gallery.models import Photo -from kasu import image_models def get_upload_path(instance, filename): @@ -22,74 +25,98 @@ def get_upload_path(instance, filename): @param filename: The filename of the uploaded image. @type filename: String """ - extension = filename[filename.rfind('.') + 1:] + filename, extension = os.path.splitext(filename.lower()) if isinstance(instance, Event): - if instance.id: - return "events/%s.%s" % (instance.id, extension) - else: - return "events/%s.%s" % (slugify(instance.name), extension) + return "events/{date:%Y-%m-%d}/{name}{ext}".format( + date=instance.start, + name=slugify(instance.name), + ext=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) + return "events/locations/{name}{ext}".format( + name=slugify(instance.name), + ext=extension + ) elif isinstance(instance, Photo): - return "events/%s/%s" % (instance.event.id, filename) + return "events/{date:%Y-%m-%d}/{name}{ext}".format( + date=instance.event.start, + name=filename, + ext=extension + ) class EventManager(models.Manager): + + def get_queryset(self): + return super(EventManager, self).get_queryset().select_related('location') + 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: + except IndexError: return None def next_event(self): try: return self.filter(start__gt=now()).order_by('start', 'end')[0] - except: + except IndexError: return None def archive(self): return self.filter(start__lt=now()) - def upcoming(self, limit=3): + def latest_events(self, num=3): + return self.filter(start__lt=now()).order_by('-start', '-end')[:num] + + def upcoming(self, limit=None): result = self.filter(start__gt=now()).order_by('start', 'end') if limit: - return result[1:(limit + 1)] + return result[0:limit] else: return result -class Event(image_models.ImageModel): +class Event(models.Model): name = models.CharField(_('Name'), max_length=255) - description = models.TextField(_("Description"), blank=True) + description = RichTextField(_("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) - mahjong_tournament = models.BooleanField(_('Mahjong Tournament'), default=False, + image = ThumbnailerImageField( + _("Image"), + upload_to=get_upload_path, + storage=OverwriteStorage(), + blank=True, + null=True + ) + mahjong_tournament = models.BooleanField( + _('Mahjong Tournament'), + default=False, help_text=_(u'This event is a tournament, different rules apply for \ - the kyu ranking.')) - mahjong_season = models.PositiveSmallIntegerField(_('Mahjong Season'), blank=True, null=True) + the kyu ranking.') + ) + mahjong_season = models.PositiveSmallIntegerField( + _('Mahjong Season'), + blank=True, + null=True + ) 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 \ + on_delete=models.SET_NULL, editable=True, + 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',) + ordering = ('start', 'end',) def __unicode__(self): try: @@ -122,23 +149,13 @@ class Event(image_models.ImageModel): } return reverse('event-form', kwargs=kwargs) - def get_callout(self): + def get_image(self): if self.image: - return self.callout - elif self.photo_set.count(): - return self.photo_set.all().order_by('?')[0].callout + return self.image + elif self.photo_count: + return self.photo_set.all().order_by('?')[0].image 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 + return self.location.image else: return None @@ -159,11 +176,16 @@ class Event(image_models.ImageModel): hanchan.save() -class Location(image_models.ImageModel): +class Location(models.Model): 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) + description = RichTextField(_("Description"), blank=True) + image = ThumbnailerImageField( + _("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) @@ -183,7 +205,111 @@ class Location(image_models.ImageModel): return ','.join(address) -models.signals.post_save.connect(image_models.regenerate_image_cache, - sender=Event) -models.signals.post_save.connect(image_models.regenerate_image_cache, - sender=Location) \ No newline at end of file +class PhotoManager(models.Manager): + + def get_random(self, startpage=True): + if startpage: + queryset = self.filter(on_startpage=True) + else: + queryset = self.all().order_by('?')[0] + try: + return queryset.order_by('?')[0] + except IndexError: + return Photo() + + +class Photo(models.Model): + name = models.CharField(_("Name"), max_length=100, blank=True) + image = ThumbnailerImageField(_("Image"), upload_to=get_upload_path, + storage=OverwriteStorage()) + event = models.ForeignKey('events.Event') + description = models.TextField( + _("Description"), + max_length=300, + blank=True + ) + photographer = models.ForeignKey(settings.AUTH_USER_MODEL) + on_startpage = models.BooleanField( + _("Startpage"), + default=False, + db_index=True, + help_text=_('Display this Photo on the Startpage Teaser') + ) + created_date = models.DateTimeField(_("Published on")) + views = models.PositiveIntegerField( + _("Number of views"), + editable=False, + default=0 + ) + objects = PhotoManager() + metadata = None + 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') + + def __unicode__(self): + return os.path.basename(self.image.name) + + def rotate(self, rotate): + # TODO: Eine vernüftigte Methode ohne viele Abhängigkeiten finden um + # die Bilder bei Bedarf zu drehen. + if rotate == 'clockwise': + pass + elif rotate == 'counter-clockwise': + pass + self.save() + + def save(self, **kwargs): + super(Photo, self).save(**kwargs) + self.event.save() + + def get_absolute_url(self): + return reverse( + 'event-photo', + kwargs={'event': self.event.id, 'pk': self.id} + ) + + @property + def next_photo(self): + queryset = Photo.objects.filter(created_date__gt=self.created_date) + queryset = queryset.order_by('created_date') + try: + if self.event.event_series: + return queryset.filter( + Q(event=self.event) | + Q(event=self.event.event_series) | + Q(event__event_series=self.event.event_series) + )[0] + else: + return queryset.filter( + Q(event=self.event) | + Q(event__event_series=self.event) + )[0] + except IndexError: + return None + return self.get_next_by_created_date(event=self.event) + + @property + def previous_photo(self): + queryset = Photo.objects.filter(created_date__lt=self.created_date) + queryset = queryset.order_by('-created_date') + try: + if self.event.event_series: + return queryset.filter( + Q(event=self.event) | + Q(event=self.event.event_series) | + Q(event__event_series=self.event.event_series) + )[0] + else: + return queryset.filter( + Q(event=self.event) | + Q(event__event_series=self.event) + )[0] + except IndexError: + return None + return self.get_previous_by_created_date(event=self.event) diff --git a/src/events/static/js/mousetrap.js b/src/events/static/js/mousetrap.js new file mode 100644 index 0000000..bec2fc8 --- /dev/null +++ b/src/events/static/js/mousetrap.js @@ -0,0 +1,11 @@ +/* mousetrap v1.5.3 craig.is/killing/mice */ +(function(C,r,g){function t(a,b,h){a.addEventListener?a.addEventListener(b,h,!1):a.attachEvent("on"+b,h)}function x(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return l[a.which]?l[a.which]:p[a.which]?p[a.which]:String.fromCharCode(a.which).toLowerCase()}function D(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function u(a){return"shift"==a||"ctrl"==a||"alt"==a|| +"meta"==a}function y(a,b){var h,c,e,g=[];h=a;"+"===h?h=["+"]:(h=h.replace(/\+{2}/g,"+plus"),h=h.split("+"));for(e=0;em||l.hasOwnProperty(m)&&(k[l[m]]=m)}e=k[h]?"keydown":"keypress"}"keypress"==e&&g.length&&(e="keydown");return{key:c,modifiers:g,action:e}}function B(a,b){return null===a||a===r?!1:a===b?!0:B(a.parentNode,b)}function c(a){function b(a){a= +a||{};var b=!1,n;for(n in q)a[n]?b=!0:q[n]=0;b||(v=!1)}function h(a,b,n,f,c,h){var g,e,l=[],m=n.type;if(!d._callbacks[a])return[];"keyup"==m&&u(a)&&(b=[a]);for(g=0;g":".","?":"/","|":"\\"},z={option:"alt",command:"meta","return":"enter", +escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},k;for(g=1;20>g;++g)l[111+g]="f"+g;for(g=0;9>=g;++g)l[g+96]=g;c.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};c.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};c.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};c.prototype.reset=function(){this._callbacks={};this._directMap= +{};return this};c.prototype.stopCallback=function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")||B(b,this.target)?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};c.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};c.init=function(){var a=c(r),b;for(b in a)"_"!==b.charAt(0)&&(c[b]=function(b){return function(){return a[b].apply(a,arguments)}}(b))};c.init();C.Mousetrap=c;"undefined"!==typeof module&&module.exports&&(module.exports= +c);"function"===typeof define&&define.amd&&define(function(){return c})})(window,document); \ No newline at end of file diff --git a/src/events/templates/events/event_archive.html b/src/events/templates/events/event_archive.html index b03d80e..706dfcd 100644 --- a/src/events/templates/events/event_archive.html +++ b/src/events/templates/events/event_archive.html @@ -1,5 +1,5 @@ {% extends "events/page.html" %} -{% load i18n comments%} +{% load i18n comments thumbnail %} {% block title %} {% trans 'Event Archive' %} {% if month %}{{ month|date:'F Y' }} {% else %}{% if year %}{{year}}{% endif %}{% endif %} @@ -32,18 +32,23 @@

    {{ month.grouper }}

    {% for event in month.list %} {% get_comment_count for event as comment_count %} -{% trans 'Event Image' %} -
    -

    {{ event.name }}

    -

    - {{ event.start|date }} +

    + {% trans 'Event Image' %} + +

    {{ event.name }}

    +

    + + + {% if event.end %} - {% trans "from" %} {{ event.start|time:'H:i' }} {% trans "to" %} {{ event.end|time:'H:i' }} + {% trans "from" %} {{ event.start|time:'H:i' }} {% trans "to" %} {{ event.end|time:'H:i' }} {% else %} - {{ event.start|time:'H:i' }} + {{ event.start|time:'H:i' }} {% endif %}

    @@ -59,7 +64,6 @@

    {% endif %}
    -{% if forloop.counter|divisibleby:2 %}
    {% endif %} {% endfor %} {% endfor %} {% endblock %} diff --git a/src/events/templates/events/event_detail.html b/src/events/templates/events/event_detail.html index 58557fe..de03b0d 100644 --- a/src/events/templates/events/event_detail.html +++ b/src/events/templates/events/event_detail.html @@ -1,5 +1,5 @@ {% extends "events/page.html" %} -{% load i18n django_markdown comments %} +{% load i18n comments thumbnail %} {% block title %}{{ event.name }}{% endblock %} @@ -8,19 +8,19 @@ - {% if event.description %}{% endif %} + {% if event.description %}{% endif %} {% endblock %} {% block extra_head %} {% endblock %} -{% block jumbotron_background %} {{ event.get_callout.url }} {% endblock %} +{% block jumbotron_background %} {{ event.get_image|thumbnail_url:'callout' }} {% endblock %} {% block teaser %}

    {{event.name}}

    {% if event.description %} -
    {{event.description|markdown|truncatewords_html:75}}
    +
    {{event.description|truncatewords_html:75|safe}}
    {% endif %} {% endblock %} @@ -48,10 +48,8 @@
  • {{ event.photo_count }} {% trans 'Photos' %}
  • {{ event.hanchan_set.count }} {% trans "Hanchans" %}
  • {{ event.maistargame_set.count }} {% trans "Mai-Star Games" %}
  • - - {% if event.mahjong_tournament %} -
  • {% trans "Tournament Ranking" %}
  • - {% endif %} +
  • {% trans "Event Ranking" %}
  • {% endblock %} @@ -81,16 +79,30 @@
    {% if event.description %} - {{event.description|markdown}} + {{event.description|safe}} {% else %} - {{event.location.description|markdown}} + {{event.location.description|safe}} {% endif %}

    diff --git a/src/events/templates/events/event_list.html b/src/events/templates/events/event_list.html index 14bb214..6e59210 100644 --- a/src/events/templates/events/event_list.html +++ b/src/events/templates/events/event_list.html @@ -1,5 +1,5 @@ {% extends "events/page.html" %} -{% load i18n comments%} +{% load i18n comments thumbnail %} {% block title %}{% trans "Upcoming Events" %}{% endblock %} {% block teaser%}

    {% trans "Upcoming Events" %}

    {% endblock %} @@ -10,22 +10,33 @@

    {{ month.grouper }}

    {% for event in month.list %} {% get_comment_count for event as comment_count %} -  {% trans 'Event Image' %} -
    +
    {% if perms.events.change_event %} - - {% endif %} - {% if perms.events.add_photo %} - + {% endif %} + {% trans 'Event Image' %} +

    {{ event.name }}

      -
    • -
    • {{ event.location }}
    • -
    • {{ comment_count }}
    • +
    • + +
    • +
    • + {{ event.start|time:'H:i' }} +
    • + +
    • + {{ event.location }} +
    • +
    • + {{ comment_count }} +
    -

    {{event.description|truncatewords_html:20}}

    +

    {{event.description|truncatewords_html:25|safe}}

    {% if forloop.counter|divisibleby:2 %}
    {% endif %} {% endfor %} diff --git a/src/events/templates/events/event_site.html b/src/events/templates/events/event_site.html index f92bc76..7b23b1b 100644 --- a/src/events/templates/events/event_site.html +++ b/src/events/templates/events/event_site.html @@ -1,5 +1,5 @@ {% extends "base.html" %} -{% load i18n django_markdown%} +{% load i18n %} {% block title %}{{ event.name }}{% endblock %} @@ -11,12 +11,12 @@ {% if event.description %}{% endif %} {% endblock %} -{% block jumbotron_background %}{{ event.get_callout.url }}')}{% endblock %} +{% block jumbotron_background %} {{ event.get_image|thumbnail_url:'callout' }} {% endblock %} {% block teaser %}

    {{event.name}}

    - {% if event.description %}{{event.description|markdown}}{% else %}{{event.location.description|markdown}}{% endif %} + {% if event.description %}{{event.description}}{% else %}{{event.location.description}}{% endif %}
    {% endblock %} diff --git a/src/events/templates/events/eventseries_form.html b/src/events/templates/events/eventseries_form.html index 70f1c7b..d454af4 100644 --- a/src/events/templates/events/eventseries_form.html +++ b/src/events/templates/events/eventseries_form.html @@ -1,15 +1,8 @@ {% extends "events/event_detail.html" %} -{% load i18n django_markdown comments %} +{% load i18n comments %} {% block title %}{{ event.name }}{% endblock %} -{% block teaser %} -

    {{event.name}}

    - {% if event.description %} -
    {{event.description|markdown|truncatewords_html:75}}
    - {% endif %} -{% endblock %} - {% block event_content %}

    Hier kommt eine Tabelle rein!

    diff --git a/src/gallery/templates/gallery/photo_confirm_delete.html b/src/events/templates/events/photo_confirm_delete.html similarity index 53% rename from src/gallery/templates/gallery/photo_confirm_delete.html rename to src/events/templates/events/photo_confirm_delete.html index ba444c1..a162d6a 100644 --- a/src/gallery/templates/gallery/photo_confirm_delete.html +++ b/src/events/templates/events/photo_confirm_delete.html @@ -1,5 +1,5 @@ {% extends "events/event_detail.html" %} -{% load i18n comments %} +{% load i18n comments thumbnail %} {% block maincontent %} @@ -8,13 +8,16 @@

    Dieses Photo wirklich löschen?

    Sind Sie sicher, dass Sie das Bild “{{photo.name}}” löschen wollen?

    - {{photo.name}} + {{photo.name}}

     

    - {% trans 'Cancel' %} {% trans 'Cancel' %} -

    diff --git a/src/events/templates/events/photo_detail.html b/src/events/templates/events/photo_detail.html new file mode 100644 index 0000000..42ee6e6 --- /dev/null +++ b/src/events/templates/events/photo_detail.html @@ -0,0 +1,85 @@ +{% extends "events/event_detail.html" %} +{% load i18n comments thumbnail %} + +{% block title %} {{ photo.name }} - {{ photo.event.name }} {% endblock %} + +{% block extra_head %} + + +{% endblock %} + +{% block javascript %} +if ($('a.previous').attr('href')) { + Mousetrap.bind('left', function() { window.location = $('a.previous').attr('href'); }); +} +if ($('a.next').attr('href')) { + Mousetrap.bind('right', function() { window.location = $('a.next').attr('href'); }); +} +{% endblock %} + +{% block teaser %} +

    {{event.name}} - {{ photo.name }}

    + {% if event.description %} +
    {{event.description|truncatewords_html:75}}
    + {% endif %} +{% endblock %} + +{% block opengraph %} + + + + +{% if photo.description %}{% endif %} +{% endblock %} + +{% block maincontent %} +
    + {{photo.name}} + {% if photo.previous_photo %} + + {% endif %} + {% if photo.next_photo %} + + {% endif %} +
    +
    +
      +
    • {% trans 'Photographer' %}: {{ photo.photographer }}
    • +
    • {% trans 'Date' %} {{ photo.created_date }}
    • +
    +

    {{ photo.description }}

    +
    +

    + {% trans 'share on' %}: + + Facebook + + + Google+ + + + Twitter + +

    +{% endblock %} + +{% block comment %} + {% render_comment_list for photo %} + {% render_comment_form for photo %} +{% endblock %} + +{% block buttonbar %} +{% if perms.events.change_photo %} + + {% csrf_token %} +

    + {% trans 'download' %} + + + +

    +
    +{% endif %} +{% endblock %} + + diff --git a/src/gallery/templates/gallery/photo_gallery.html b/src/events/templates/events/photo_gallery.html similarity index 76% rename from src/gallery/templates/gallery/photo_gallery.html rename to src/events/templates/events/photo_gallery.html index 163a7d3..e0509a6 100644 --- a/src/gallery/templates/gallery/photo_gallery.html +++ b/src/events/templates/events/photo_gallery.html @@ -1,11 +1,11 @@ {% extends "base.html" %} -{% load i18n %} +{% load i18n thumbnail %} {% block maincontent %} {% for event in event_list %} {% empty %}

    Sorry da kommt erst was hin!

    diff --git a/src/events/templates/events/photo_list.html b/src/events/templates/events/photo_list.html new file mode 100644 index 0000000..3a0cfc4 --- /dev/null +++ b/src/events/templates/events/photo_list.html @@ -0,0 +1,45 @@ +{% extends "events/event_detail.html" %} +{% load i18n thumbnail %} + +{% block title %}{{event.name}}{% endblock %} + +{% block opengraph %} + + + + +{% if event.description %}{% endif %} +{% endblock %} + +{% block maincontent %} + +{% for photo in photo_list %} +
    + + {% if perms.events.delete_photo %} + + + + {% endif %} +
    +{% endfor %} + + +{% if perms.events.add_photo %} +
    + {% csrf_token %} +
    + Photos hochladen + {% include "form.html"%} +

    + + +

    +
    +
    +{% endif %} +{% endblock %} + +{% block buttonbar %}{% endblock %} + + diff --git a/src/gallery/templates/gallery/photo_upload.html b/src/events/templates/events/photo_upload.html similarity index 67% rename from src/gallery/templates/gallery/photo_upload.html rename to src/events/templates/events/photo_upload.html index 8acbe9c..6165b78 100644 --- a/src/gallery/templates/gallery/photo_upload.html +++ b/src/events/templates/events/photo_upload.html @@ -1,16 +1,16 @@ {% extends "base.html" %} -{% load i18n comments %} +{% load i18n comments thumbnail %} {% block maincontent %} {% for event in event_list %} {% get_comment_count for event as comment_count %} {% ifchanged %}

    {{ event.start|date:'F Y' }}

    {% endifchanged %}
    - +

    {{ event.name }}

    -{% trans 'Start' %} + {{ event.start|date }} {% if event.end %} {% trans "from" %} {{ event.start|time:'H:i' }} {% trans "to" %} {{ event.end|time:'H:i' }} @@ -20,16 +20,19 @@
    {% if event.description %}

    {{event.description}}

    {% endif %}

    {% if perms.events.add_photo %} - {%trans + + + {%trans "Upload" %} {% endif %}

    @@ -42,8 +45,8 @@ {% include "form.html" %}

    diff --git a/src/events/tests.py b/src/events/tests.py index 501deb7..75f2f59 100644 --- a/src/events/tests.py +++ b/src/events/tests.py @@ -9,6 +9,7 @@ from django.test import TestCase class SimpleTest(TestCase): + def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. diff --git a/src/events/urls.py b/src/events/urls.py index f2b8d58..4a965c1 100644 --- a/src/events/urls.py +++ b/src/events/urls.py @@ -1,11 +1,11 @@ # -*- encoding: utf-8 -*- from django.conf.urls import patterns, url - +from django.views.generic import RedirectView from .views import * urlpatterns = patterns( '', - url(r'^$', UpcomingEvents.as_view(), name='upcoming-events'), + url(r'^$', RedirectView.as_view(url='/events/upcoming/', permanent=True)), url(r'^(?P[\d]{4})/$', EventArchiveYear.as_view(), name='event-archive'), url(r'^(?P[\d]{4})/(?P[\d]+)/$', EventArchiveMonth.as_view(), @@ -18,4 +18,5 @@ urlpatterns = patterns( EventForm.as_view(), name='event-form'), url(r'^add/$', EventForm.as_view(), name='event-form'), url(r'^archive/$', EventArchiveIndex.as_view(), name='event-archive'), + url(r'^upcoming/$', UpcomingEvents.as_view(), name='upcoming-events'), ) diff --git a/src/events/views.py b/src/events/views.py index 1071fbb..aa371af 100644 --- a/src/events/views.py +++ b/src/events/views.py @@ -2,6 +2,7 @@ # Create your views here. from datetime import timedelta +from django.db.models import Q from django.contrib.auth.decorators import permission_required from django.contrib.auth import get_user_model from django.core.urlresolvers import reverse @@ -13,22 +14,23 @@ 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 class DeleteEventPhoto(generic.DeleteView): model = models.Photo - """ - def get_object(self, queryset=None): - return models.Photo.objects.get(pk=self.kwargs['pk']) - """ 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) @@ -39,10 +41,9 @@ class EventArchiveIndex(generic.ArchiveIndexView): context_object_name = 'event_list' date_field = 'start' model = models.Event - queryset = model.objects.all() + ordering = ('-start', '-end') paginate_by = 15 - def get_context_data(self, **kwargs): context = generic.ArchiveIndexView.get_context_data(self, **kwargs) context['is_archive'] = True @@ -53,6 +54,7 @@ class EventArchiveMonth(generic.MonthArchiveView): date_field = 'start' make_object_list = True model = models.Event + ordering = ('start', 'end') month_format = '%m' paginate_by = 15 template_name = 'events/event_archive.html' @@ -64,9 +66,10 @@ class EventArchiveMonth(generic.MonthArchiveView): class EventArchiveYear(generic.YearArchiveView): + model = models.Event date_field = 'start' make_object_list = True - model = models.Event + ordering = ('start', 'end') paginate_by = 15 template_name = 'events/event_archive.html' year_format = '%Y' @@ -117,27 +120,13 @@ class EventForm(PermissionRequiredMixin, generic.UpdateView): return models.Event() -class EventSeriesForm(EventDetailMixin, PermissionRequiredMixin, InlineFormSetView): - model = models.Event - inline_model = models.Event - fk_name = 'event_series' - fields = ('start', 'end') - form_class = forms.EventForm - extra = 3 - permission_required = 'events.add_event' - template_name = 'events/eventseries_form.html' - - def get_object(self, queryset=None): - self.event = models.Event.objects.get(pk=self.kwargs['pk']) - if self.event.event_series: - self.event = self.event.event_series - return self.event - - class EventGallery(generic.ListView): template_name = 'events/photo_gallery.html' - queryset = models.Event.objects.filter(start__lt=timezone.now(), - photo_count__gt=0) + queryset = models.Event.objects.filter( + start__lt=timezone.now(), + event_series__isnull=True, + photo_count__gt=0 + ) paginate_by = 12 @@ -180,7 +169,10 @@ class EventPhoto(generic.UpdateView): def get_context_data(self, **kwargs): context = super(EventPhoto, self).get_context_data() - event = models.Event.objects.get(id=self.kwargs['event']) + try: + event = models.Event.objects.get(id=self.kwargs['event']) + except models.Event.DoesNotExist: + event = self.object.event context['event'] = event return context @@ -209,7 +201,10 @@ class EventPhotoList(generic.ListView): def get_queryset(self): try: self.event = models.Event.objects.get(id=self.kwargs['event']) - return models.Photo.objects.filter(event=self.event) + return models.Photo.objects.filter( + Q(event=self.event) | + Q(event__event_series=self.event) + ) except models.Event.DoesNotExist: raise Http404(_('Event does not exist')) @@ -261,20 +256,28 @@ class EventPhotoUpload(generic.FormView): return redirect('event-photo-list', event=self.event.id) def read_exif(self, photo): - exif_data = pyexiv2.ImageMetadata.from_buffer(photo.read()) - exif_data.read() - - try: - created_date = exif_data['Exif.Image.DateTime'].value - except: - created_date = self.event.start + timedelta(minutes=self.counter) - try: - description = exif_data['Exif.Image.ImageDescription'].value - except: - description = '' + created_date = self.event.start + timedelta(minutes=self.counter) + description = '' return created_date, description +class EventSeriesForm(EventDetailMixin, PermissionRequiredMixin, InlineFormSetView): + model = models.Event + inline_model = models.Event + fk_name = 'event_series' + fields = ('start', 'end') + form_class = forms.EventForm + extra = 3 + permission_required = 'events.add_event' + template_name = 'events/eventseries_form.html' + + def get_object(self, queryset=None): + self.event = models.Event.objects.get(pk=self.kwargs['pk']) + if self.event.event_series: + self.event = self.event.event_series + return self.event + + class UpcomingEvents(generic.ListView): queryset = models.Event.objects.upcoming(limit=None) paginate_by = 16 diff --git a/src/gallery/__init__.py b/src/gallery/__init__.py deleted file mode 100644 index 4b31e84..0000000 --- a/src/gallery/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__author__ = 'christian' diff --git a/src/gallery/forms.py b/src/gallery/forms.py deleted file mode 100644 index ae00c98..0000000 --- a/src/gallery/forms.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -Created on 03.10.2011 - -@author: christian -""" -from django import forms -from django.utils.translation import ugettext as _ -from django.contrib.auth import get_user_model - -from . import models -from events.models import Event - - -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(Event.objects.all(), required=True, ) - upload = forms.FileField( - label=_('Images'), - required=True, - widget=forms.widgets.ClearableFileInput( - attrs={ - 'multiple': 'multiple', - 'accept': "image/gif,image/png,image/jpeg" - } - ) - ) - - -class EditPhotoForm(forms.ModelForm): - error_css_class = 'error' - required_css_class = 'required' - - class Meta(object): - model = models.Photo - fields = ('event', 'name', 'description', 'photographer', - 'anchor_horizontal', 'anchor_vertical', - 'created_date', 'on_startpage') diff --git a/src/gallery/locale/de/LC_MESSAGES/django.po b/src/gallery/locale/de/LC_MESSAGES/django.po deleted file mode 100644 index 8da5edb..0000000 --- a/src/gallery/locale/de/LC_MESSAGES/django.po +++ /dev/null @@ -1,154 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-22 23:28+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: forms.py:23 -msgid "Images" -msgstr "" - -#: models.py:44 -msgid "Name" -msgstr "" - -#: models.py:45 -msgid "Image" -msgstr "" - -#: models.py:48 -msgid "horizontal Anchorpoint" -msgstr "" - -#: models.py:55 -msgid "vertical Anchorpoint" -msgstr "" - -#: models.py:64 -msgid "Description" -msgstr "" - -#: models.py:70 -msgid "Startpage" -msgstr "" - -#: models.py:72 -msgid "Display this Photo on the Startpage Teaser" -msgstr "" - -#: models.py:74 -msgid "Published on" -msgstr "" - -#: models.py:76 -msgid "Number of views" -msgstr "" - -#: models.py:88 -msgid "Event Image" -msgstr "" - -#: models.py:89 -msgid "Event Images" -msgstr "" - -#: templates/gallery/photo_confirm_delete.html:16 -msgid "Cancel" -msgstr "" - -#: templates/gallery/photo_confirm_delete.html:17 -#: templates/gallery/photo_confirm_delete.html:18 -msgid "Delete" -msgstr "" - -#: templates/gallery/photo_detail.html:19 -msgid "previous" -msgstr "" - -#: templates/gallery/photo_detail.html:27 -msgid "Share on Google+" -msgstr "" - -#: templates/gallery/photo_detail.html:28 -msgid "Share on Twitter" -msgstr "" - -#: templates/gallery/photo_detail.html:29 -msgid "Share on Facebook" -msgstr "" - -#: templates/gallery/photo_detail.html:33 -msgid "Photographer" -msgstr "" - -#: templates/gallery/photo_detail.html:34 -msgid "on" -msgstr "" - -#: templates/gallery/photo_detail.html:48 -msgid "download" -msgstr "" - -#: templates/gallery/photo_detail.html:51 -msgid "save" -msgstr "" - -#: templates/gallery/photo_list.html:20 -msgid "delete" -msgstr "" - -#: templates/gallery/photo_list.html:37 -msgid "reset" -msgstr "" - -#: templates/gallery/photo_list.html:38 templates/gallery/photo_upload.html:46 -msgid "upload" -msgstr "" - -#: templates/gallery/photo_upload.html:13 -msgid "Start" -msgstr "" - -#: templates/gallery/photo_upload.html:16 -msgid "from" -msgstr "" - -#: templates/gallery/photo_upload.html:16 -msgid "to" -msgstr "" - -#: templates/gallery/photo_upload.html:23 -msgid "Location" -msgstr "" - -#: templates/gallery/photo_upload.html:25 -#: templates/gallery/photo_upload.html:26 -msgid "Comments" -msgstr "" - -#: templates/gallery/photo_upload.html:27 -#: templates/gallery/photo_upload.html:28 -msgid "Photos" -msgstr "" - -#: templates/gallery/photo_upload.html:32 -msgid "Upload" -msgstr "" - -#: views.py:80 -msgid "Event does not exist" -msgstr "" diff --git a/src/gallery/migrations/0001_initial.py b/src/gallery/migrations/0001_initial.py deleted file mode 100644 index 8531916..0000000 --- a/src/gallery/migrations/0001_initial.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import gallery.models -import utils -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - ('events', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Photo', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=100, verbose_name='Name', blank=True)), - ('image', models.ImageField(upload_to=gallery.models.get_upload_path, storage=utils.OverwriteStorage(), verbose_name='Bild')), - ('anchor_horizontal', models.FloatField(blank=True, help_text=b'Der Ankerpunkt ist der interessante Teil des Bildes, welcher nie abgeschnitten werden darf', null=True, verbose_name='horizontal Anchorpoint', choices=[(1e-08, 'Links'), (0.5, 'Mitte'), (1, 'Rechts')])), - ('anchor_vertical', models.FloatField(blank=True, help_text=b'Wenn kein Ankerpunkt von Hand (horizontal und vertikal) festgelegt wird, versucht die Software diesen selbst zu erraten.', null=True, verbose_name='vertical Anchorpoint', choices=[(1e-08, 'Oben'), (0.5, 'Mitte'), (1, 'Unten')])), - ('description', models.TextField(max_length=300, verbose_name='Beschreibung', blank=True)), - ('on_startpage', models.BooleanField(default=False, help_text='Display this Photo on the Startpage Teaser', verbose_name='Startpage')), - ('created_date', models.DateTimeField(verbose_name='Published on')), - ('views', models.PositiveIntegerField(default=0, verbose_name='Number of views', editable=False)), - ('event', models.ForeignKey(to='events.Event')), - ('photographer', models.ForeignKey(to=settings.AUTH_USER_MODEL)), - ], - options={ - 'ordering': ['created_date'], - 'db_table': 'events_photo', - 'verbose_name': 'Veranstaltungsbild', - 'verbose_name_plural': 'Event Images', - 'get_latest_by': 'created_date', - }, - ), - ] diff --git a/src/gallery/migrations/__init__.py b/src/gallery/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/gallery/models.py b/src/gallery/models.py deleted file mode 100644 index 929d3bd..0000000 --- a/src/gallery/models.py +++ /dev/null @@ -1,171 +0,0 @@ -# -'- Encoding: utf-8 -*- - -import os - -from django.conf import settings -from django.core.urlresolvers import reverse -from django.db import models -from django.utils.translation import ugettext as _ -import pyexiv2 - -from utils import OverwriteStorage -from kasu import image_models - - -def get_upload_path(instance, filename): - """ - Generates the desired file path and filename for an uploaded Image. - With this function Django can save the uploaded images to subfolders that - also have a meaning for humans. - - @param instance: an Django Object for which the Image has been uploaded. - @type instance: a instace of an models.Model sub-class. - @param filename: The filename of the uploaded image. - @type filename: String - """ - extension = filename[filename.rfind('.') + 1:] - if isinstance(instance, Photo): - return "events/%s/%s" % (instance.event.id, filename) - - -class PhotoManager(models.Manager): - def get_random(self, startpage=True): - if startpage: - queryset = self.filter(on_startpage=True) - else: - queryset = self.all().order_by('?')[0] - try: - return queryset.order_by('?')[0] - except IndexError: - return Photo() - - -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=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=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('events.Event') - description = models.TextField( - _("Description"), - max_length=300, - blank=True - ) - photographer = models.ForeignKey(settings.AUTH_USER_MODEL) - on_startpage = models.BooleanField( - _("Startpage"), - default=False, - help_text=_('Display this Photo on the Startpage Teaser') - ) - created_date = models.DateTimeField(_("Published on")) - views = models.PositiveIntegerField( - _("Number of views"), - editable=False, - default=0 - ) - objects = PhotoManager() - metadata = None - 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') - - - def __unicode__(self): - return os.path.basename(self.image.name) - - def read_metadata(self): - image_path = os.path.join(settings.MEDIA_ROOT, self.image.name) - self.metadata = pyexiv2.ImageMetadata(image_path) - self.metadata.read() - try: - self.orientation = self.metadata['Exif.Image.Orientation'].value - except: - self.orientation = 1 - - def save_metadata(self): - if not self.metadata: - self.read_metadata() - self.metadata['Exif.Image.DateTime'] = self.created_date - self.metadata['Exif.Image.ImageDescription'] = self.description - self.metadata['Exif.Image.Artist'] = self.photographer.username - self.metadata['Exif.Image.Orientation'] = self.orientation or 1 - self.metadata.write() - - def rotate(self, rotate): - """ - Sets an the Exif tag in an image to set the right direction. - This provides lossless image rotation. - @param rotate: 'clockwise' or 'counter-clockwise' the direction in - which we should rotate the image in 90° steps. - """ - if not self.metadata: - self.read_metadata() - if rotate == 'clockwise': - if self.orientation == 1: - self.orientation = 6 - elif self.orientation == 6: - self.orientation = 3 - elif self.orientation == 3: - self.orientation = 8 - else: - self.orientation = 1 - elif rotate == 'counter-clockwise': - if self.orientation == 1: - self.orientation = 8 - elif self.orientation == 8: - self.orientation = 3 - elif self.orientation == 3: - self.orientation = 6 - else: - self.orientation = 1 - self.save() - - def get_absolute_url(self): - return reverse( - 'event-photo', - kwargs={'event': self.event.id, 'pk': self.id} - ) - - @property - def next_photo(self): - return self.get_next_by_created_date(event=self.event) - - @property - def previous_photo(self): - return self.get_previous_by_created_date(event=self.event) - - def save(self, **kwargs): - """ - Triggers to save related Event to save. This should force an update for - the denormalized Photo count. - """ - super(Photo, self).save() - self.save_metadata() - - -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) diff --git a/src/gallery/templates/gallery/photo_detail.html b/src/gallery/templates/gallery/photo_detail.html deleted file mode 100644 index e7392b5..0000000 --- a/src/gallery/templates/gallery/photo_detail.html +++ /dev/null @@ -1,57 +0,0 @@ -{% extends "gallery/photo_list.html" %} -{% load i18n comments %} - -{% block title %} {{ photo.name }} - {{ photo.event.name }} {% endblock %} - -{% block opengraph %} - - - - -{% if photo.description %}{% endif %} -{% endblock %} - -{% block maincontent %} -

    {{photo.event.name}} » {{ photo.name }}

    -
    - {{photo.name}} - {% if photo.previous_photo %} - - {% endif %} - {% if photo.next_photo %} - - {% endif %} -
    -

    {{ photo.description }}

    - - -
      -
    • {% trans 'Photographer' %}: {{ photo.photographer }}
    • -
    • {% trans 'on' %} {{ photo.created_date }}
    • -
    -{% endblock %} - -{% block comment %} - {% render_comment_list for photo %} - {% render_comment_form for photo %} -{% endblock %} - -{% block buttonbar %} -{% if perms.events.change_photo %} -
    - {% csrf_token %} -

    - {% trans 'download' %} - - - -

    -
    -{% endif %} -{% endblock %} - - diff --git a/src/gallery/templates/gallery/photo_list.html b/src/gallery/templates/gallery/photo_list.html deleted file mode 100644 index 6a8c611..0000000 --- a/src/gallery/templates/gallery/photo_list.html +++ /dev/null @@ -1,47 +0,0 @@ -{% extends "events/event_detail.html" %} - -{% load i18n %} - -{% block title %}{{event.name}}{% endblock %} - -{% block opengraph %} - - - - -{% if event.description %}{% endif %} -{% endblock %} - -{% block maincontent %} - {% if perms.events.delete_photo %} - {% for photo in photo_list %} -
    - - -
    - {% endfor %} - {% else %} - {% for photo in photo_list %} - - {% endfor %} - {% endif %} - -{% if perms.events.add_photo %} -
    -
    - {% csrf_token %} -
    - Photos hochladen - {% include "form.html"%} -

    - - -

    -
    -
    -{% endif %} -{% endblock %} - -{% block buttonbar %}{% endblock %} - - diff --git a/src/gallery/views.py b/src/gallery/views.py deleted file mode 100644 index a70e921..0000000 --- a/src/gallery/views.py +++ /dev/null @@ -1,142 +0,0 @@ -# -*- encoding: utf-8 -*- -# Create your views here. -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 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 -import pyexiv2 - -from events.models import Event -from .models import Photo -from . import forms - - -class DeleteEventPhoto(generic.DeleteView): - 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 EventGallery(generic.ListView): - template_name = 'gallery/photo_gallery.html' - queryset = Event.objects.filter(start__lt=timezone.now(), photo_count__gt=0) - paginate_by = 12 - - -class EventPhoto(generic.UpdateView): - form_class = forms.EditPhotoForm - model = Photo - template_name = 'gallery/photo_detail.html' - - def get_context_data(self, **kwargs): - context = super(EventPhoto, self).get_context_data() - 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 = Photo.objects.get(pk=kwargs['pk']) - photo.rotate(request.POST['rotate']) - # return redirect(photo.get_absolute_url()) - return self.get(request) - else: - return generic.UpdateView.post(self, request, *args, **kwargs) - - -class EventPhotoList(generic.ListView): - context_object_name = 'photo_list' - paginate_by = 36 - - 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}) - return context - - def get_queryset(self): - try: - 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 = 'gallery/photo_upload.html' - - @method_decorator(permission_required('events.add_photo')) - def dispatch(self, *args, **kwargs): - return super(EventPhotoUpload, self).dispatch(*args, **kwargs) - - def get_context_data(self, **kwargs): - context = generic.FormView.get_context_data(self, **kwargs) - context['event_list'] = Event.objects.archive()[:12] - return context - - def get_initial(self): - """ - Set the current logged in user a default value for the photographer. - """ - return { - 'photographer': self.request.user, - } - - def post(self, *args, **kwargs): - """ - - """ - 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 = Photo( - event=self.event, - photographer=photographer, - image=upload, - name=name, - created_date=created_date, - description=description - ) - photo.save() - self.counter += 1 - return redirect('event-photo-list', event=self.event.id) - - def read_exif(self, photo): - exif_data = pyexiv2.ImageMetadata.from_buffer(photo.read()) - exif_data.read() - - try: - created_date = exif_data['Exif.Image.DateTime'].value - except: - created_date = self.event.start + timedelta(minutes=self.counter) - try: - description = exif_data['Exif.Image.ImageDescription'].value - except: - description = '' - return created_date, description - diff --git a/src/kasu/image_models.py b/src/kasu/image_models.py index 5968fe6..258b952 100644 --- a/src/kasu/image_models.py +++ b/src/kasu/image_models.py @@ -10,19 +10,6 @@ from imagekit.models import ImageSpecField import imagekit -CHOICES_HORIZONTAL = ( - (0.00000001, _('left')), - (0.5, _('center')), - (1, _('right')) -) - -CHOICES_VERTICAL = ( - (0.00000001, _('top')), - (0.5, _('middle')), - (1, _('bottom')) -) - - class ArticleImage(imagekit.ImageSpec): format = 'JPEG' width = 200 @@ -96,11 +83,10 @@ def regenerate_image_cache(sender, instance=None, created=False, raw=False, Reganerate the images. """ if instance.image: - print instance.image - print instance.display - print instance.callout for cached_image in (instance.article, instance.callout, instance.display, instance.thumbnail): try: os.remove(cached_image.path) + except IOError: + pass except OSError: pass diff --git a/src/kasu/static/css/kasu.css b/src/kasu/static/css/kasu.css index 25bb161..f44ca8f 100644 --- a/src/kasu/static/css/kasu.css +++ b/src/kasu/static/css/kasu.css @@ -1,4 +1 @@ -@font-face{font-family:Philosopher;font-weight:400;font-style:normal;src:url(../fonts/philosopher.woff) format('woff'),url(../fonts/philosopher.ttf) format('truetype')}@font-face{font-family:'Amerika Sans';font-weight:400;font-style:normal;src:url(../fonts/amerikasans.woff) format('woff'),url(../fonts/amerikasans.ttf) format('truetype')}@font-face{font-family:FontAwesome;src:url(../fonts/fontawesome-webfont.eot?v=4.4.0);src:url(../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0) format('embedded-opentype'),url(../fonts/fontawesome-webfont.woff2?v=4.4.0) format('woff2'),url(../fonts/fontawesome-webfont.woff?v=4.4.0) format('woff'),url(../fonts/fontawesome-webfont.ttf?v=4.4.0) format('truetype'),url(../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular) format('svg');font-weight:400;font-style:normal}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.clear,.clearfix{clear:both}.clear{display:block;height:0;overflow:hidden;visibility:hidden;width:0}#display{position:relative;text-align:center;top:0}.grid_1,.grid_10,.grid_11,.grid_12,.grid_2,.grid_3,.grid_4,.grid_5,.grid_6,.grid_7,.grid_8,.grid_9{box-sizing:border-box;display:inline;float:left;margin:0 10px;position:relative}.grid_1{width:60px}.grid_2{width:140px}.grid_3{width:220px}.grid_4{width:300px}.grid_5{width:380px}.more_link,input[type=number],.buttonbar{text-align:right}.more_link{clear:left}.error,ul.errorlist li,a:hover{color:#a40000}a:hover{text-decoration:underline}a:link{color:#204a87;font-weight:700;text-decoration:none}a:visited{color:#5c3566}#redbox a.button:link,#redbox a.button:visited,a.button,button{background:linear-gradient(to bottom,#f9f9f9 5%,#e9e9e9 100%);border:1px solid #d3d7cf;border-radius:5px;box-shadow:inset 0 1px 0 0 #fff;color:#2e3436;display:inline-block;font:700 12pt Philosopher,sans-serif;margin:.25em;padding:.25em;text-decoration:none;text-shadow:1px 1px 1px #fff}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section{display:block}h1 a:link,h1 a:visited,h2 a:link,h2 a:visited,h3 a:link,h3 a:visited,h4 a:link,h4 a:visited,h5 a:link,h5 a:visited,h6 a:link,h6 a:visited{color:#bc0a19;font-weight:400;text-decoration:none}.player,h1,h2,h3,h4,h5,h6{color:#bc0a19;font-family:'Amerika Sans',sans-serif;font-variant:small-caps;font-weight:400;letter-spacing:-1px;margin:1em 0 .5em;text-shadow:1px 1px 1px #888;vertical-align:baseline}.player{margin:0}a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,time,tt,u,ul,var,video{border:0;margin:0;padding:0;vertical-align:baseline}p{margin-bottom:.5em}img.partner,img.posting_image{border:1px solid #babdb6;display:block;float:left;height:120px;margin:0 20px 0 0;padding:4px;width:200px}img.partner:nth-of-type(odd){float:right}input{font:400 12pt Philosopher,sans-serif}input[maxlength="255"]{width:100%}input[readonly=readonly],input[readonly]{background:0 0;border:none;color:#2e3436}.cke_chrome{border:0!important}.cke_wysiwyg_div{padding:1em 0!important}li{margin-bottom:.2em}.thumbnail{background:url(../img/thumbnail-bg.png) top left no-repeat;border:0;display:block;float:left;height:140px;margin:5px;padding:5px;position:relative;width:140px}ul.tabs{background:linear-gradient(to bottom,#000 0,#45484d 100%);border-radius:10px 10px 0 0;list-style:none;margin:1em 0 0;padding:.5em 0 0;width:100%}ul.tabs li{background:linear-gradient(to bottom,#fa665a 5%,#d34639 100%);border-radius:5px 15px 0 0;display:inline-block;margin:0 -5px 0 10px}ul.tabs li a{color:#fff;display:inline-block;padding:.5em 1em;text-decoration:none;text-shadow:1px 1px 1px #2e3436}ul.tabs li.active{background:#fff}ul.tabs li.active a{color:#bc0a19;text-decoration:none;text-shadow:1px 1px 1px #888}div.tab_container{background-color:#fff;padding-top:1em}ol{list-style:cjk-ideographic;padding-left:2em}table{border-collapse:collapse;border-spacing:0;margin:0 auto 1em;width:100%}table td,table th{padding:2px;vertical-align:middle}table td{border-bottom:1px solid #d3d7cf;border-top:1px solid #d3d7cf}table th{background:#a40000;color:#fff}table th a:link,table th a:visited{color:#FFF}table tr:nth-child(2n+1){background-color:#eeeeec}table tr:hover{background-color:#eedcdc}ul{list-style:circle;padding-left:30px}ul.info{list-style:none;margin-bottom:.5em;padding-left:0}ul.info li{display:inline-block;margin-right:10px}.buttonbar{background:linear-gradient(to bottom,#45484d 0,#000 100%);border-radius:10px}.center,.pagination{text-align:center}.pagination .current,.pagination .next,.pagination .previous,.pagination a{display:inline-block;padding:0 .5em;text-decoration:none}.pagination .next{background:0 0;float:right}.pagination .previous{background:0 0;float:left}.right{text-align:right}#redbox{background:linear-gradient(135deg,#a90329 0,#8f0222 44%,#6d0019 100%);border-radius:10px;color:#fff;padding:10px}#redbox a:link,#redbox a:visited,#redbox h2,#redbox h3{color:#fff;font-weight:400;text-shadow:1px 1px 1px #000}#redbox h2:first-of-type{color:#000;margin:-20px 0 0 10px;text-shadow:1px 1px 1px #fff}.comment,fieldset.comment{padding:0}.comment{display:table;margin-bottom:1em;width:100%}fieldset.comment legend{margin-left:15px}fieldset.comment .buttonbar{margin:0;width:100%}#bottom_buttonbar{border-radius:0 0 10px 10px;bottom:0;margin:0;position:absolute;width:100%}@media screen and (min-width:700px){#content{margin:0 10px;position:relative;width:940px}#display .next,#display .previous{display:block;height:100%;margin:0;opacity:.5;overflow:hidden;padding:0;position:absolute;text-indent:9999px;top:0;width:60px}#display .next:hover,#display .previous:hover{opacity:1;transition:all .2s ease-out}#display .next{background:url(../img/right-arrow.png) center center no-repeat;right:10px;z-index:3}#display .previous{background:url(../img/left-arrow.png) center center no-repeat;left:10px;z-index:2}#display img{box-shadow:1px 1px 5px 1px #444}#footer{margin:20px auto 0;min-height:50px;width:920px;z-index:30}#footer p{text-align:center}#footer_kranich,#maincontent{position:relative;margin:0 auto}#footer_kranich{display:block;max-width:1250px;text-align:right;top:-320px;width:100%;z-index:-2}#maincontent{background:rgba(255,255,255,.5);border-radius:10px;box-shadow:0 0 20px 1px rgba(0,0,0,.75);height:auto!important;min-height:800px;padding:10px 0 2em;width:960px;z-index:19}#messages{clear:both;list-style:none;margin:0 auto;padding:8px 0 0 30px;width:920px}#messages li.success{background:#89bd84;border:1px solid #253324;border-radius:10px;color:#253324;margin:10px;padding:10px}#navigation,.pagination{background:url(../img/navigation-bg.png) left top no-repeat;clear:both;height:56px;list-style:none;margin:0 auto;padding:8px 35px 0 25px;position:relative;width:900px;z-index:30}#navigation a,.pagination a{background:url(../img/navigation-separator.png) right center no-repeat;color:#FFF;display:block;float:left;font-weight:700;height:33px;padding:17px 15px 0;text-decoration:none}#navigation #navigation a.active,#navigation a:hover,.pagination #navigation a.active,.pagination a:hover{background:url(../img/navigation-hover.png) left top repeat-x;color:#3B3B3B}#navigation li,.pagination li{display:inline;margin:0}#jumbotron{background-color:#333;background-position:center left;background-repeat:no-repeat;border:none;border-radius:10px;clear:both;margin:0 10px 1em;min-height:300px;padding:0 1px 0 0;position:relative;width:940px;z-index:5}#jumbotron>h1,#jumbotron>h2{color:#eff0ef;left:10px;margin:0;max-width:600px;position:absolute;text-shadow:1px 1px 1px #000;top:33%}#jumbotron #teaser_text{background:rgba(0,0,0,.5);border-radius:0 0 0 10px;bottom:0;color:#FFF;display:block;left:0;padding:1em;position:absolute;width:620px}#redbox,#usernav{position:absolute;top:0;right:0}#redbox{border-radius:0 10px 10px 0;height:280px;width:280px}#usernav{background:#000;background:linear-gradient(to bottom,#45484d 0,#000 100%);border-radius:0 0 0 10px;box-shadow:-1px -1px 5px 1px rgba(0,0,0,.75);color:#FFF;font-size:14pt;padding:10px;text-align:right;z-index:50}#usernav a{color:#FFF}#usernav img{vertical-align:middle}.clearfix{zoom:1px}.clearfix:after{clear:both}.gallery{display:inline;float:left;height:200px;margin:10px;overflow:hidden;text-align:center;width:300px}.gallery .thumbnail{display:block;float:none;margin:5px auto}div.thumbnail a.delete_image{bottom:0;position:absolute;right:4px}#body{background-attachment:scroll,fixed;background-color:#fff;background-image:url(../img/kranich.png),url(../img/header_bg.jpg);background-position:center bottom,center top;background-repeat:no-repeat,no-repeat}body{font:12pt Philosopher,Georgia,serif;height:100%;line-height:1;margin:0;min-width:960px;padding:0;position:relative;vertical-align:baseline}fieldset{background:linear-gradient(135deg,#f2f5f6 0,#e3eaed 37%,#c8d7dc 100%);border:none;border-radius:10px;color:#2e3436;margin:10px 0 0;padding:0 10px 0 160px;vertical-align:top}fieldset legend{color:#a40000;font-family:'Amerika Sans',sans-serif;font-size:16pt;font-variant:small-caps;font-weight:400;margin-left:-150px;margin-top:-.1em;text-shadow:2px 2px 2px #888}fieldset div{margin:5px 0}fieldset .required{font-weight:700}fieldset .buttonbar{border-radius:0 0 10px 10px;margin:0 -10px 0 -160px}fieldset .help_text{font-size:small}fieldset .field_name{clear:left;display:inline-block;margin:0 20px 0 -160px;padding-top:3px;text-align:right;vertical-align:top;width:140px}fieldset input,fieldset textarea{border:1px solid #999;border-radius:5px;margin:0;padding:2px}fieldset input[maxlength="255"],fieldset textarea{box-sizing:border-box;max-width:760px;width:100%}fieldset ul{display:inline-block;padding:0}fieldset ul li{display:inline;list-style:none}fieldset table{display:inline-table;max-width:760px}img.partner,img.partner_right,img.posting_image{border:1px solid #babdb6;float:left;height:120px;margin:0 20px 0 0;padding:4px;width:200px}img.partner:nth-of-type(odd){float:right}.pagination .current,.pagination .next,.pagination .previous,.pagination a{color:#FFF;display:inline-block;font-weight:700;height:33px;padding:17px .5em 0;text-decoration:none}.pagination .current{color:#a40000}.pagination .disabled{color:#ccc}img.avatar{border:none;box-shadow:2px 2px 2px #888;height:60px;width:60px}.comment_picture,.comment_header{vertical-align:top;display:table-cell;padding:0 10px}.comment_picture{width:60px}.comment_header{width:140px}.comment_header h3{margin:0}.comment_text{display:table-cell;max-width:700px;padding:0 10px;width:auto}.userinfo{width:140px}.grid_6{width:460px}.grid_7{width:540px}.grid_8{width:620px}.grid_9{width:700px}.grid_10{width:780px}.grid_11{width:860px}.grid_12{width:940px}#recaptcha_widget_div{margin-top:-20px}#google_maps{border-radius:0 10px 10px 0;height:280px;left:0;padding:10px;position:relative;top:0}}@media screen and (max-width:699px){body{background:url(../img/background_mobile.png) top center no-repeat fixed;font:12pt Philosopher,Georgia,serif;margin:5px 10px}#display .grid_10{margin:0;position:relative;z-index:1}#display .next,#display .previous{display:block;height:100%;margin:0;opacity:.5;overflow:hidden;padding:0;position:absolute;text-indent:9999px;top:0;width:45px}#display .next:hover,#display .previous:hover{opacity:.9;transition:all .2s ease-out}#display .next{background:url(../img/right-arrow.png) center center no-repeat;right:0;z-index:3}#display .previous{background:url(../img/left-arrow.png) center center no-repeat;left:0;z-index:2}#footer,#topnav a,#navigation li,.toggle{text-align:center}#footer{border-top:1px solid #000}img.partner,img.posting_image{border:1px solid #babdb6;float:left;height:59px;margin:1em .5em 0 0;padding:2px;width:99px}img{height:auto;max-width:100%}#topnav a{color:#000;display:inline-block;font:400 small-caps 24pt 'Amerika Sans',sans-serif;min-width:80px;padding:5px;text-decoration:none;text-shadow:2px 2px 2px #2e3436}#topnav a.active{color:#bc0a19}#topnav a:hover{color:#FFF}#sitelogo{background:url(../img/logo_mobile.png) no-repeat;float:left;height:54px;left:5px;margin:0;padding:0;text-indent:-9999px;top:5px;width:114px;z-index:20}#teaser{background:0 0;margin-bottom:1em}#teaser_text{background:rgba(255,255,255,.5)}#navigation{background:url(../img/navigation-mobile.png) top left repeat-x #45484d;background-size:contain;margin:10px 0;padding:0}#navigation li{border-left:1px solid #fff;display:inline-block;margin:0;padding:.5em .3em .5em .5em}#navigation li:first-of-type{border:none}#navigation a{color:#FFF;font:700 12px Arial;text-decoration:none}#sitelogo a{display:block;height:54px;width:114px}#siteheader:after{clear:both;content:".";display:block;height:0;visibility:hidden}#maincontent{width:100%}#mainnav{display:block;float:right}#toggle,.toggle,.main_menu{display:none}#toggle:checked~.main_menu{display:block;opacity:1}#toggle:checked~.toggle,.toggle:hover{background:#45ABD6}.comment_picture{padding:0 10px}.toggle{-webkit-touch-callout:none;-webkit-user-select:none;cursor:pointer;position:relative;user-select:none;z-index:2}.main_menu{background:#eeeeec;border-bottom:3px solid #a40000;border-top:1px solid #a40000;margin:0;min-width:50%;padding:0;position:absolute;right:0;z-index:999}.main_menu li{display:block;list-style:none;margin:0}.main_menu li a{border-left:0 solid #eeeeec;box-sizing:border-box;color:#000;display:block;font:400 small-caps 18px 'Amerika Sans',sans-serif;line-height:1;padding:.5em 1em;text-decoration:none;transition:all .25s linear;width:100%}.main_menu li a:focus,.main_menu li a:hover{border-left:3px solid #a40000;color:#a40000}list-type:none,ul.main_dropdown{margin:0;padding:0}ul.main_dropdown li{padding:0}ul.main_dropdown a{font-size:12pt;padding-left:2em}#jumbotron{background:0 0!important}#redbox{display:block;margin-top:1em}.grid_10,.grid_11,.grid_12,.grid_6,.grid_7,.grid_8,.grid_9{clear:both;width:100%}.player,.thumbnail{float:left;position:relative}.player{-moz-box-sizing:border-box;box-sizing:border-box;display:inline;margin-left:10px;margin-right:10px;min-width:60px}.toggle{background:#a40000;border-radius:2px;box-sizing:border-box;color:#FFF;content:'Main Menu';display:block;margin:8px 0;padding:10px;transition:all .5s linear;width:150px}.thumbnail,img.thumbnail{float:left;box-shadow:2px 2px 5px #888;height:70px;margin:5px;width:70px}.thumbnail{display:block;padding:0}.thumbnail img{height:70px;width:70px}.thumbnail a.delete_image{display:none}img.thumbnail{display:block}fieldset{background:-ms-linear-gradient(-45deg,#f2f5f6 0,#e3eaed 37%,#c8d7dc 100%);background:-o-linear-gradient(-45deg,#f2f5f6 0,#e3eaed 37%,#c8d7dc 100%);background:-webkit-linear-gradient(-45deg,#f2f5f6 0,#e3eaed 37%,#c8d7dc 100%);background:-webkit-gradient(linear,left top,right bottom,color-stop(0,#f2f5f6),color-stop(37%,#e3eaed),color-stop(100%,#c8d7dc));background:-moz-linear-gradient(-45deg,#f2f5f6 0,#e3eaed 37%,#c8d7dc 100%);background:#f2f5f6;background:linear-gradient(135deg,#f2f5f6 0,#e3eaed 37%,#c8d7dc 100%);border:none;border-radius:10px;color:#2e3436;margin:10px 0 0;padding:0 10px 0 160px}fieldset legend{color:#a40000;font-family:'Amerika Sans',sans-serif;font-size:16pt;font-variant:small-caps;font-weight:400;margin-left:-150px;margin-top:-.1em;text-shadow:2px 2px 2px #888}fieldset .required{font-weight:700}fieldset .error{color:#a40000}fieldset .buttonbar{border-radius:0 0 10px 10px;margin:0 -10px 0 -160px}fieldset .help_text{font-size:small}fieldset .field_name{clear:left;display:inline-block;margin:0 20px 0 -160px;padding-top:3px;text-align:right;width:140px}fieldset input,fieldset textarea{border:1px solid #999;border-radius:5px;font-size:12pt;margin:0;padding:2px}fieldset input[maxlength="255"],fieldset textarea{box-sizing:border-box;width:99%}fieldset ul{display:inline-block;padding:0}fieldset ul li{display:inline;list-style:none}.pagination{clear:both}.pagination .current,.pagination .next,.pagination .previous,.pagination a{display:inline-block;padding:0 .5em;text-decoration:none}.gallery{float:left;height:150px;margin:10px;width:150px}.gallery h3{font-size:12pt}.comment{display:block}.comment_picture,.comment_header{display:block;float:left;vertical-align:top}.comment_picture{width:60px}.comment_header{padding:0 10px;width:140px}.comment_header h3{margin:0}.comment_text{border-top:1px solid #45484d;clear:both;display:block;margin-left:0 10px;padding-top:.5em}}@media screen and (min-width:700px){#siteheader{margin:0 auto;min-height:100px;padding:0;position:relative;width:960px;z-index:50}#sitelogo,#sitelogo a{height:110px;position:absolute;width:233px}#sitelogo{background:url(../img/logo.png) no-repeat;left:5px;margin:0;padding:0;text-indent:-9999px;top:5px;z-index:99}#sitelogo a{display:block;left:0;top:0}#mainnav{left:233px;position:absolute;top:65px}#mainnav #toggle,#mainnav .toggle{display:none}#mainnav>ul.main_menu{padding:0;position:relativ}#mainnav>ul.main_menu>li{display:block;float:left;font:400 small-caps 18px 'Amerika Sans',sans-serif;min-width:50px;padding:8px;text-align:center;text-shadow:2px 2px 2px #2e3436}#mainnav>ul.main_menu>li a{color:#000;font-weight:400;text-decoration:none}#mainnav>ul.main_menu>li a.active{color:#bc0a19}#mainnav>ul.main_menu>li a:hover{color:#FFF}#mainnav>ul.main_menu>li:first-child{padding-left:0}#mainnav>ul.main_menu>li:last-child{padding-right:0}#mainnav ul.main_menu>li>ul{background:rgba(255,255,255,.8);border-radius:10px;box-shadow:-1px 1px 5px 0 rgba(0,0,0,.75);display:none;margin-left:-10px;min-width:10em;padding:10px 0;position:absolute;top:30px;transition:all .25s linear}#mainnav ul.main_menu>li>ul li{display:block;float:none;font:400 small-caps 14pt 'Amerika Sans',sans-serif;padding:2px;text-align:left}#mainnav ul.main_menu>li>ul li a{border-radius:.5em;color:#000;display:block;padding:.5em .25em;transition:all .25s linear}#mainnav ul.main_menu>li>ul li a:hover{background:linear-gradient(135deg,#6d0019 0,#8f0222 44%,#a90329 100%);background:#bc0a19;color:#fff;transition:all .25s linear}#mainnav ul.main_menu li:hover>ul{display:block}}@media print{{margin:.5cm .5cm .5cm 1cm;orphans:3;size:portrait;widows:3}#bottom_buttonbar,#comment_form,#footer>form,#mainnav,#navigation,#usernav,aside,nav{display:none!important}#footer,article,body,.grid_10,.grid_11,.grid_12,.grid_6,.grid_8,grid_7,grid_9{width:100%}#footer{border-top:1px solid #000;padding-top:.5em;text-align:center}*{-webkit-print-color-adjust:exact;print-color-adjust:exact}a:link,a:visited{color:#000;font-weight:700}#sitelogo{background:url(../img/logo.png) top right no-repeat;background-size:contain;font-family:'Amerika Sans',Helvetica;font-size:8pt;left:0;line-height:1cm;margin:0;padding:0;top:5px;z-index:99}#jumbotron{background:0 0!important}article,body{background:#fff;color:#000;margin:0;padding:0}h1{font-size:32pt}h2,h3,h4,h5,h6{page-break-after:avoid;text-shadow:none}img{max-width:100%!important}img,ul{page-break-inside:avoid}#bottom_buttonbar,#comment_form,#comments,#footer,#footer>form,#maincontent aside,#maincontent nav,#mainnav,#navigation,#usernav,.more_link{display:none}}/*! - * Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */.fa,.fa-stack{display:inline-block}.fa{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{text-align:center;width:1.28571429em}.fa-ul{list-style-type:none;margin-left:2.14285714em;padding-left:0}.fa-ul>li{position:relative}.fa-li,.fa-stack-1x,.fa-stack-2x{text-align:center;position:absolute}.fa-li{left:-2.14285714em;top:.14285714em;width:2.14285714em}.fa-li.fa-lg{left:-1.85714286em}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right,.pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}.fa-rotate-90{-ms-transform:rotate(90deg);-webkit-transform:rotate(90deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);transform:rotate(90deg)}.fa-rotate-180{-ms-transform:rotate(180deg);-webkit-transform:rotate(180deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);transform:rotate(180deg)}.fa-rotate-270{-ms-transform:rotate(270deg);-webkit-transform:rotate(270deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);transform:rotate(270deg)}.fa-flip-horizontal{-ms-transform:scale(-1,1);-webkit-transform:scale(-1,1);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);transform:scale(-1,1)}.fa-flip-vertical{-ms-transform:scale(1,-1);-webkit-transform:scale(1,-1);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);transform:scale(1,-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{filter:none}.fa-stack{height:2em;line-height:2em;position:relative;vertical-align:middle;width:2em}.fa-stack-1x,.fa-stack-2x{left:0;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-close:before,.fa-remove:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-repeat:before,.fa-rotate-right:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-exclamation-triangle:before,.fa-warning:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-floppy-o:before,.fa-save:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-bolt:before,.fa-flash:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-chain-broken:before,.fa-unlink:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:"\f150"}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:"\f151"}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:"\f152"}.fa-eur:before,.fa-euro:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-inr:before,.fa-rupee:before{content:"\f156"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:"\f158"}.fa-krw:before,.fa-won:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-try:before,.fa-turkish-lira:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-bank:before,.fa-institution:before,.fa-university:before{content:"\f19c"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:"\f1c5"}.fa-file-archive-o:before,.fa-file-zip-o:before{content:"\f1c6"}.fa-file-audio-o:before,.fa-file-sound-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-empire:before,.fa-ge:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-paper-plane:before,.fa-send:before{content:"\f1d8"}.fa-paper-plane-o:before,.fa-send-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-bed:before,.fa-hotel:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-y-combinator:before,.fa-yc:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-television:before,.fa-tv:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"} \ No newline at end of file +@font-face{font-family:FontAwesome;src:url(../fonts/fontawesome-webfont.eot?v=4.4.0);src:url(../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0) format('embedded-opentype'),url(../fonts/fontawesome-webfont.woff2?v=4.4.0) format('woff2'),url(../fonts/fontawesome-webfont.woff?v=4.4.0) format('woff'),url(../fonts/fontawesome-webfont.ttf?v=4.4.0) format('truetype'),url(../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular) format('svg');font-weight:400;font-style:normal}@font-face{font-family:Philosopher;font-weight:400;font-style:normal;src:url(../fonts/philosopher.woff) format('woff'),url(../fonts/philosopher.ttf) format('truetype')}@font-face{font-family:'Amerika Sans';font-weight:400;font-style:normal;src:url(../fonts/amerikasans.woff) format('woff'),url(../fonts/amerikasans.ttf) format('truetype')}@page{margin:1cm 1cm 1cm 2cm;size:A4 portrait}a:hover{color:#a40000;text-decoration:underline}a:link{color:#204a87;font-weight:700;text-decoration:none}a:visited{color:#5c3566}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section{display:block}body{font:12pt Philosopher,Georgia,serif;line-height:1;vertical-align:baseline}#redbox a.button:link,#redbox a.button:visited,a.button,button{background:linear-gradient(to bottom,#f9f9f9 5%,#e9e9e9 100%);border:1px solid #d3d7cf;border-radius:5px;box-shadow:inset 0 1px 0 0 #fff;color:#2e3436;display:inline-block;font:700 12pt Philosopher,sans-serif;margin:.25em;padding:.25em;text-decoration:none;text-shadow:1px 1px 1px #fff}div.tab_container{background-color:#fff;padding-top:1em}fieldset{background:linear-gradient(135deg,#f2f5f6 0,#e3eaed 37%,#c8d7dc 100%);border:none;border-radius:10px;color:#2e3436;margin-bottom:1em;padding:0 10px 0 160px;vertical-align:top}fieldset div{margin:5px 0}fieldset legend{color:#a40000;font-family:'Amerika Sans',sans-serif;font-size:16pt;font-variant:small-caps;font-weight:400;margin-left:-150px;margin-top:-.1em;text-shadow:2px 2px 2px #888}fieldset .required{font-weight:700}fieldset .buttonbar{border-radius:0 0 10px 10px;margin:0 -10px 0 -160px}fieldset .help_text{font-size:small}fieldset .field_name{clear:left;display:inline-block;margin:0 20px 0 -160px;padding-top:3px;text-align:right;vertical-align:top;width:140px}fieldset input,fieldset textarea{border:1px solid #999;border-radius:5px;margin:0;padding:2px}fieldset input[maxlength="255"],fieldset textarea{box-sizing:border-box;max-width:760px;width:100%}fieldset ul{display:inline-block;padding:0}fieldset ul li{display:inline;list-style:none}fieldset table{display:inline-table;max-width:760px}fieldset.comment{padding:0}fieldset.comment legend{margin-left:15px}fieldset.comment .buttonbar{margin:0;width:100%}.player,h1,h2,h3,h4,h5,h6{color:#bc0a19;font-family:'Amerika Sans',sans-serif;font-variant:small-caps;font-weight:400;letter-spacing:-1px;margin:1em 0 .5em;text-shadow:1px 1px 1px #888;vertical-align:baseline}.player a:link,.player a:visited,h1 a:link,h1 a:visited,h2 a:link,h2 a:visited,h3 a:link,h3 a:visited,h4 a:link,h4 a:visited,h5 a:link,h5 a:visited,h6 a:link,h6 a:visited{color:#bc0a19;font-weight:400;text-decoration:none}ol{list-style:cjk-ideographic;padding-left:2em}table{border-collapse:collapse;border-spacing:0;margin:0 auto 1em;width:100%}table td,table th{padding:2px;vertical-align:middle}table td{border-bottom:1px solid #d3d7cf;border-top:1px solid #d3d7cf}table th{background:#a40000;color:#fff}table th a:link,table th a:visited{color:#FFF}table tr:nth-child(2n+1){background-color:#eeeeec}table tr:hover{background-color:#eedcdc}ul{list-style:circle;margin:.5em 0;padding-left:2em}ul.info{list-style:none;margin:0 0 .5em;padding-left:0}ul.info li,ul.tabs li,ul.tabs li a{display:inline-block}ul.info li{margin-right:1em}ul.tabs{background:linear-gradient(to bottom,#000 0,#45484d 100%);border-radius:10px 10px 0 0;list-style:none;margin:1em 0 0;padding:.5em 0 0;width:100%}ul.tabs li{background:linear-gradient(to bottom,#fa665a 5%,#d34639 100%);border-radius:5px 15px 0 0;margin:0 -5px 0 10px}ul.tabs li a{color:#fff;padding:.5em 1em;text-decoration:none;text-shadow:1px 1px 1px #2e3436}ul.tabs li.active{background:#fff}ul.tabs li.active a{color:#bc0a19;text-decoration:none;text-shadow:1px 1px 1px #888}#bottom_buttonbar{border-radius:0 0 10px 10px}#display{position:relative;text-align:center}#redbox{background:linear-gradient(135deg,#a90329 0,#8f0222 44%,#6d0019 100%);border-radius:10px;color:#fff;padding:10px}#redbox a:link,#redbox a:visited,#redbox h2,#redbox h3{color:#fff;font-weight:400;text-shadow:1px 1px 1px #000}#redbox h2:first-of-type{color:#000;margin:-20px 0 0 10px;text-shadow:1px 1px 1px #fff}.avatar,.avatar img{height:70px;width:70px}.avatar,.pagination{padding:0;position:relative}.avatar{box-shadow:2px 2px 5px #888;display:block;float:left;margin:0 1em 1em 0}.buttonbar,.pagination a{background:linear-gradient(to bottom,#45484d 0,#000 100%)}.buttonbar{border-radius:10px;text-align:right}.center{text-align:center}.clear,.clearfix{clear:both}.cke_chrome{border:0!important}.cke_wysiwyg_div{padding:1em 0!important}.disabled{color:#ccc}.comment{display:table;margin-bottom:1em;padding:0;width:100%}.error,ul.errorlist li{color:#a40000}.game h2{margin:.5em 0}.grid_1,.grid_10,.grid_11,.grid_12,.grid_2,.grid_3,.grid_4,.grid_5,.grid_6,.grid_7,.grid_8,.grid_9{box-sizing:border-box;display:inline;float:left;margin:0 10px;position:relative}.grid_1{width:60px}.grid_2{width:140px}.grid_3{width:220px}.grid_4{width:300px}.grid_5{width:380px}.grid_6{width:460px}.grid_7{width:540px}.grid_8{width:620px}.grid_9{width:700px}.grid_10{width:780px}.grid_11{width:860px}.grid_12{width:940px}.more_link{clear:left;text-align:right}.pagination{clear:both;margin-bottom:1em;text-align:center;z-index:30}.pagination a{border-radius:5px;display:inline-block;font-weight:700;padding:.5em 1em;text-decoration:none}.pagination a:link,.pagination a:visited{color:#fff}.pagination a.active,.pagination a:hover{color:#bc0a19}.pagination a.disabled,.pagination a.disabled:hover{color:#666}.pagination a.previous{border-radius:10px 5px 5px 10px;float:left}.pagination a.next{border-radius:5px 10px 10px 5px;float:right}.player{float:none;margin:0 auto}.right{text-align:right}.thumbnail{background:url(../img/thumbnail-bg.png) top left no-repeat;border:0;display:block;float:left;height:140px;margin:5px;padding:5px;position:relative;width:140px}@media screen and (min-width:700px){#siteheader{margin:0 auto;min-height:110px;padding:0;position:relative;width:960px;z-index:50}#sitelogo,#sitelogo a{height:110px;position:absolute;width:233px}#sitelogo{background:url(../img/logo.png) no-repeat;left:5px;margin:0;padding:0;text-indent:-9999px;top:5px;z-index:99}#sitelogo a{display:block;left:0;top:0}#mainnav{bottom:0;left:233px;position:absolute}#mainnav #toggle,#mainnav .toggle{display:none}#mainnav ul.main_menu{list-style:none;margin:0;padding:0}#mainnav ul.main_menu>li{display:inline-block;font:400 small-caps 18px 'Amerika Sans',sans-serif;min-width:50px;padding:8px;text-align:center;text-shadow:2px 2px 2px #2e3436}#mainnav ul.main_menu>li a{color:#000;font-weight:400;text-decoration:none}#mainnav ul.main_menu>li a.active{color:#bc0a19}#mainnav ul.main_menu>li a:hover{color:#FFF}#mainnav ul.main_menu>li:first-child{padding-left:0}#mainnav ul.main_menu>li:last-child{padding-right:0}#mainnav ul.main_menu>li>ul{background:rgba(255,255,255,.8);border-radius:10px;box-shadow:-1px 1px 5px 0 rgba(0,0,0,.75);display:none;margin:0 0 0 -15px;min-width:10em;padding:.25em;position:absolute;top:100%;transition:all .25s linear}#mainnav ul.main_menu>li>ul li{display:block;float:none;font:400 small-caps 14pt 'Amerika Sans',sans-serif;text-align:left}#mainnav ul.main_menu>li>ul li a{border-radius:.5em;color:#000;display:block;padding:5px;transition:all .25s linear}#mainnav ul.main_menu>li>ul li a:hover{background:linear-gradient(135deg,#a90329 0,#8f0222 44%,#6d0019 100%);background:#bc0a19;color:#fff;transition:all .25s linear}#mainnav ul.main_menu li:hover>ul{display:block}}@media screen and (max-width:699px){body{background:url(../img/background_mobile.png) top center no-repeat fixed;font:12pt Philosopher,Georgia,serif;margin:5px 10px}img{height:auto;max-width:100%}img.thumbnail{box-shadow:2px 2px 5px #888;display:block;float:left;height:70px;margin:5px;width:70px}img.partner,img.posting_image{border:1px solid #babdb6;float:left;height:59px;margin:1em .5em 0 0;padding:2px;width:99px}ul.main_dropdown{list-style-type:none;margin:0;padding:0}ul.main_dropdown li{padding:0}ul.main_dropdown a{font-size:12pt;padding-left:2em}#display .grid_10{margin:0;position:relative;z-index:1}#display .next,#display .previous{display:block;height:100%;margin:0;opacity:.5;overflow:hidden;padding:0;position:absolute;text-indent:9999px;top:0;width:45px}#display .next:hover,#display .previous:hover{opacity:.9;transition:all .2s ease-out}#display .next{background:url(../img/right-arrow.png) center center no-repeat;right:0;z-index:3}#display .previous{background:url(../img/left-arrow.png) center center no-repeat;left:0;z-index:2}#footer,#navigation li,#topnav a,.officer{text-align:center}#footer{border-top:1px solid #000}#jumbotron{background:0 0!important}#maincontent{width:100%}#mainnav{display:block;float:right}#navigation{background:url(../img/navigation-mobile.png) top left repeat-x #45484d;background-size:contain;margin:10px 0;padding:0}#navigation a{color:#FFF;font:700 12px Arial;text-decoration:none}#navigation li{border-left:1px solid #fff;display:inline-block;margin:0;padding:.5em .3em .5em .5em}#navigation li:first-of-type{border:none}#sitelogo{background:url(../img/logo_mobile.png) no-repeat;float:left;height:54px;left:5px;margin:0;padding:0;text-indent:-9999px;top:5px;width:114px;z-index:20}#sitelogo a{display:block;height:54px;width:114px}#siteheader:after{clear:both;content:"";display:block;height:0;visibility:hidden}#redbox{display:block;margin-top:1em}#teaser{background:0 0;margin-bottom:1em}#teaser_text{background:rgba(255,255,255,.5)}#topnav a,.main_menu li a{text-decoration:none;color:#000}#topnav a{display:inline-block;font:400 small-caps 24pt 'Amerika Sans',sans-serif;min-width:80px;padding:5px;text-shadow:2px 2px 2px #2e3436}#topnav a.active{color:#bc0a19}#topnav a:hover{color:#FFF}#toggle,.toggle{display:none}#toggle:checked~.main_menu{display:block;opacity:1}#toggle:checked~.toggle,.toggle:hover{background:#45abd6}.comment_picture,.comment_header{display:block;float:left;padding:0 10px;vertical-align:top}.comment_picture{width:60px}.comment{display:block}.comment_header{width:140px}.comment_header h3{margin:0}.comment_text{border-top:1px solid #45484d;clear:both;display:block;margin:0 10px;padding-top:.5em}.gallery{float:left;height:150px;margin:10px;width:150px}.gallery h3{font-size:12pt}.game img,.officer img{box-shadow:1px 1px 5px 1px #444}.game img{float:right;height:auto;margin:.5em 0 .5em 1em;width:140px}.game:nth-child(2n+1) img{float:left;margin:.5em 2em .5em 0}.grid_2{margin:1% 0;min-width:140px;width:31%}.grid_3{margin:1% 0;width:48%}.grid_10,.grid_11,.grid_12,.grid_4,.grid_5,.grid_6,.grid_7,.grid_8,.grid_9{clear:both;margin:0;width:100%}.main_menu{background:#eeeeec;border-bottom:3px solid #a40000;border-top:1px solid #a40000;display:none;margin:0;min-width:50%;padding:0;position:absolute;right:0;z-index:999}.main_menu li{display:block;list-style:none;margin:0}.main_menu li a{border-left:0 solid #eeeeec;box-sizing:border-box;display:block;font:400 small-caps 18px 'Amerika Sans',sans-serif;line-height:1;padding:.5em 1em;transition:all .25s linear;width:100%}.main_menu li a:focus,.main_menu li a:hover{border-left:3px solid #a40000;color:#a40000}.officer{box-sizing:border-box;float:left;padding:5px;width:25%}.officer img{border:0;border-radius:50%;height:100%;width:100%}.officer .function{font-size:small;margin-top:.25em}.toggle,.thumbnail{display:block;position:relative}.toggle{background:#a40000;border-radius:5px;color:#FFF;cursor:pointer;margin:8px 0;padding:10px;transition:all .5s linear;z-index:2}.thumbnail{box-shadow:2px 2px 5px #888;float:left;height:70px;margin:5px;padding:0;width:70px}.thumbnail img{height:70px;width:70px}}@media screen and (min-width:700px){body{height:100%;margin:0;min-width:960px;padding:0;position:relative}#body{background-attachment:scroll,fixed;background-color:#fff;background-image:url(../img/kranich.png),url(../img/header_bg.jpg);background-position:center bottom,center top;background-repeat:no-repeat,no-repeat}#bottom_buttonbar{bottom:0;margin:0;position:absolute;width:100%}#content{margin:0 10px;position:relative;width:940px}#display .next,#display .previous{display:block;height:100%;margin:0;opacity:.5;overflow:hidden;padding:0;position:absolute;text-indent:9999px;top:0;width:60px}#display .next:hover,#display .previous:hover{opacity:1;transition:all .2s ease-out}#display .next{background:url(../img/right-arrow.png) center center no-repeat;right:10px;z-index:3}#display .previous{background:url(../img/left-arrow.png) center center no-repeat;left:10px;z-index:2}#display img{box-shadow:1px 1px 5px 1px #444}#footer{margin:20px auto 0;min-height:50px;width:920px;z-index:30}#footer p{text-align:center}#google_maps{border-radius:0 10px 10px 0;height:280px;left:0;padding:10px;position:relative;top:0}#jumbotron{background-color:#333;background-position:center left;background-repeat:no-repeat;border:none;border-radius:10px;clear:both;margin:0 10px 1em;min-height:300px;padding:0;position:relative;width:940px;z-index:5}#jumbotron>h1,#jumbotron>h2{color:#eff0ef;left:10px;margin:0;max-width:600px;position:absolute;text-shadow:1px 1px 1px #000;top:33%}#jumbotron #teaser_text{background:rgba(0,0,0,.5);border-radius:0 0 0 10px;bottom:0;color:#FFF;display:block;left:0;padding:1em;position:absolute;width:620px}#jumbotron #teaser_text a:active,#jumbotron #teaser_text a:link,#jumbotron #teaser_text a:visited{color:#fff;text-decoration:underline}#maincontent{background:rgba(255,255,255,.5);border-radius:10px;box-shadow:0 0 20px 1px rgba(0,0,0,.75);height:auto!important;margin:0 auto;min-height:800px;padding:10px 0 2em;position:relative;width:960px;z-index:19}#messages{clear:both;list-style:none;margin:0 auto;padding:8px 0 0 30px;width:920px}#messages li.success{background:#89bd84;border:1px solid #253324;border-radius:10px;color:#253324;margin:10px;padding:10px}#navigation{background:url(../img/navigation-bg.png) left top no-repeat;clear:both;height:56px;margin:0 auto;padding:8px 35px 0 25px;position:relative;width:900px;z-index:30}#navigation a{background:url(../img/navigation-separator.png) right center no-repeat;color:#FFF;display:inline-block;font-weight:700;height:33px;padding:17px 15px 0;text-decoration:none}#navigation a.active,#navigation a:hover{background:url(../img/navigation-hover.png) left top repeat-x;color:#3B3B3B}#navigation li{display:inline}#recaptcha_widget_div{margin-top:-20px}#redbox,#usernav{position:absolute;top:0;right:0}#redbox{border-radius:0 10px 10px 0;height:280px;width:280px;z-index:100}#usernav{background:#000;background:linear-gradient(to bottom,#45484d 0,#000 100%);border-radius:0 0 0 10px;box-shadow:-1px -1px 5px 1px rgba(0,0,0,.75);color:#FFF;font-size:14pt;padding:10px;text-align:right;z-index:50}#usernav a{color:#FFF}.comment_picture,.comment_header{display:table-cell;padding:0 10px;vertical-align:top}.comment_picture{width:60px}.comment_header{width:140px}.comment_header h3{margin:0}.comment_text{display:table-cell;max-width:700px;padding:0 10px;width:auto}.gallery{display:inline;float:left;height:200px;margin:10px;overflow:hidden;text-align:center;width:300px}.gallery .thumbnail{display:block;float:none;margin:5px auto}.game img,.officer img{box-shadow:1px 1px 5px 1px #444}.game img{float:right;height:auto;margin:.5em 0 .5em 1em;width:300px}.game:nth-child(2n+1) img{float:left;margin:.5em 2em .5em 0}.officer{box-sizing:border-box;float:left;margin:5px;text-align:center;width:140px}.officer img{border:0;border-radius:50%;height:130px;width:130px}.officer .function{font-size:small;margin-top:.25em}img.partner,img.posting_image{border:1px solid #babdb6;float:left;height:120px;margin:0 1em 1em 0;padding:2px;width:200px}.thumbnail a.delete_image{bottom:0;position:absolute;right:4px}}@media print{a:link,a:visited{color:#000;font-weight:700}article,body,#footer,.grid_10,.grid_11,.grid_12,.grid_6,.grid_8,grid_7,grid_9{width:100%}article,body{background:#fff;color:#000;margin:0;padding:0}h1{font-size:32pt}h2,h3,h4,h5,h6{page-break-after:avoid;text-shadow:none}img,ul{page-break-inside:avoid}img{max-width:100%!important}#bottom_buttonbar,#comment_form,#comments,#footer,#footer>form,#maincontent aside,#maincontent nav,#mainnav,#navigation,#usernav,.more_link,aside,nav{display:none}#footer{border-top:1px solid #000;padding-top:.5em;text-align:center}#jumbotron{background:0 0!important}#sitelogo{background:url(../img/logo.png) top right no-repeat;background-size:contain;font-family:'Amerika Sans',Helvetica;font-size:8pt;left:0;line-height:1cm;margin:0;padding:0;top:5px;z-index:99}}.fa{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw,.fa-li{text-align:center}.fa-fw{width:1.28571429em}.fa-ul{list-style-type:none;margin-left:2.14285714em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2.14285714em;position:absolute;top:.14285714em;width:2.14285714em}.fa-li.fa-lg{left:-1.85714286em}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-close:before,.fa-remove:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-repeat:before,.fa-rotate-right:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-exclamation-triangle:before,.fa-warning:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-floppy-o:before,.fa-save:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-bolt:before,.fa-flash:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-chain-broken:before,.fa-unlink:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:"\f150"}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:"\f151"}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:"\f152"}.fa-eur:before,.fa-euro:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-inr:before,.fa-rupee:before{content:"\f156"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:"\f158"}.fa-krw:before,.fa-won:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-try:before,.fa-turkish-lira:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-bank:before,.fa-institution:before,.fa-university:before{content:"\f19c"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:"\f1c5"}.fa-file-archive-o:before,.fa-file-zip-o:before{content:"\f1c6"}.fa-file-audio-o:before,.fa-file-sound-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-empire:before,.fa-ge:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-paper-plane:before,.fa-send:before{content:"\f1d8"}.fa-paper-plane-o:before,.fa-send-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-bed:before,.fa-hotel:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-y-combinator:before,.fa-yc:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-television:before,.fa-tv:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"} \ No newline at end of file diff --git a/src/kasu/static/img/buttonbar.gif b/src/kasu/static/img/buttonbar.gif deleted file mode 100644 index 6c67f8a76cfbe38ecb28a0950b062d9754159c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 663 zcmZ?wbhEHbT?V#=^nI$W!N$%3Bsn;_Sh={k*txlQxcIrbIk~xkEN*^o4jvvZ9v&WE z5a8$M6%yp-0|NnmetrRdULFuFD9A4W1cHKse8T)fLPEmA!XhFfqN1W=Vq)Ur;t~=P zl9G~AQc}{=(lRnKva+&ra&ig^3aW}q%1UyIN-9c9N{UMA%F2q$%F3$BDnOv7qOJ;L zsVS?fDXM{hhL)XlQ9_YUyZcX=!U|sB3HKX=ww2jy6yX zNa^Y5=xFQe=;~_gf`GoBo}RwGt`U&X*Ei7D)iKc5Gtf6MFfcSUG%_+WF)=YUH8nFc zGdDN4u&}VSwERyxQ2Zz8T$GwvlA5AWo>`Ki;O^-gz@Ye(g^`P)onawyRW}5YtrONnbW3E%a}ELR{Fg8^AZ!1Q&N+X(juec;$vdhM@5H4 zgvN$N1qXzO1O|oqd-!_#`TG0%I=gxKxVpQ0+t|A}IypHwS=w1!+1TE*uzdK)(%j;y z#S05F^EYqJjZHtAd^9yO{{F+*!0^vsLp^;au9}03S`Rks2CPXqsMOlkuHZFCBe98v zQ&8Tf;)KAWW;b3Lvz`w(79M8hme2}0VW8N|#4e=aGQ*+xATtNIl);TihJy^uoO~Rr a4qG$=89gQ|@gCTs8O#u{*ol{g!5RQ?fVUU` diff --git a/src/kasu/static/img/favicon.ico b/src/kasu/static/img/favicon.ico index 346b94f3c5a7dc36093157604adcdc24731ac217..9a7462795a4002f577fc27257ce85c939fd68c7d 100644 GIT binary patch literal 15086 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 literal 15086 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 Vcql!SC+@+$`t*zkuu_TS{{YWc*?0f| diff --git a/src/kasu/static/img/google_maps.png b/src/kasu/static/img/google_maps.png deleted file mode 100644 index 2dd040e75768e54b836da1ec3d208a20eb5e7c20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10637 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 diff --git a/src/kasu/static/img/google_plus.png b/src/kasu/static/img/google_plus.png deleted file mode 100644 index 59f7e3a92750fc1e0a0e36ef7a72264dd2805f9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3907 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 R?r#79002ovPDHLkV1nhDsL%ia diff --git a/src/kasu/static/img/usernav-bg.png b/src/kasu/static/img/usernav-bg.png deleted file mode 100644 index 39d97da152061ad6c7b7a1d8a7498b579b51048d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1021 zcmV2XWf00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipn= z6%iC@$Kdn;000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000A6NklSF^`)(tC z0RZ6Eg>OGerxCwBV*&u+Hiqv^r&`^vF#!N@Tf?_={@OkNGXMbGy6^=60O1P&0Kyjl z0E90900>_I01&0fzCk(6a=Q~&_9Qr)s=SBGzkOeYf)0|20vieB{~;{J6m zo=&GG1^_@S&06+);cLei0D!iJZ@<4Ve9Ox!82|uU7rr;x@hzuQJH7w_v^9JuSB7sn zTaG#a0MOd-oy-s4Ni%wrl#V(80MJ&&f};-8M)VfdwZazwfOeV|#oX|nG^003;iv-u z0Idz*1xFpGjWtO!@!>WA09qTq3qIU-)`;G-B=4R)ev-q(LsJ6)pnc&xS$sRQS@@2V zynXWci#&h+o2dZ+&^kr2IDFqES(}CLcsxEYWdHzZlUc`h=Da^h@+is2N$w|kw6(SM zOU4j9{|t}A8eLoEM!-00000NkvXXu0mjf+tJKk diff --git a/src/kasu/static/less/common.less b/src/kasu/static/less/common.less index 87e701e..a205cdb 100644 --- a/src/kasu/static/less/common.less +++ b/src/kasu/static/less/common.less @@ -12,55 +12,6 @@ src: url('../fonts/amerikasans.woff') format('woff'), url('../fonts/amerikasans.ttf') format('truetype'); } -.clear { - clear: both; - display: block; - height: 0; - overflow: hidden; - visibility: hidden; - width: 0; -} -#display {position:relative;top:0px; text-align: center;} -.clearfix { - clear: both; -} -.grid_1,.grid_2,.grid_3,.grid_4,.grid_5,.grid_6,.grid_7,.grid_8,.grid_9,.grid_10,.grid_11,.grid_12 - { - display: inline; - float: left; - margin: 0px 10px; - position: relative; - box-sizing: border-box; -} - -.grid_1 { - width: 60px; -} - -.grid_2 { - width: 140px; -} - -.grid_3 { - width: 220px; -} - -.grid_4 { - width: 300px; -} - -.grid_5 { - width: 380px; -} - -.more_link { - text-align: right; - clear: left; -} -.error, ul.errorlist li { - color: #a40000; -} - a:hover { color: #a40000; text-decoration: underline; @@ -71,11 +22,13 @@ a:link { font-weight: 700; text-decoration: none; } - -a:visited { - color: #5c3566; +a:visited {color: #5c3566;} +article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section {display: block;} +body { + font: 12pt Philosopher, Georgia, serif; + line-height: 1; + vertical-align: baseline; } - button, a.button, #redbox a.button:link, #redbox a.button:visited { display: inline-block; padding: 0.25em; @@ -90,17 +43,79 @@ button, a.button, #redbox a.button:link, #redbox a.button:visited { text-decoration: none; text-shadow: 1px 1px 1px #ffffff; } +div.tab_container { + background-color:#fff; + padding-top: 1em; -article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section { - display: block; } - -h1 a:link, h2 a:link, h3 a:link, h4 a:link, h5 a:link, h6 a:link, h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited { - color: #bc0a19; +fieldset { + border: none; + color: #2e3436; + border-radius: 10px; + padding: 0 10px 0 160px; + background-color: #f2f5f6; + background: linear-gradient(135deg, #f2f5f6 0%, #e3eaed 37%, #c8d7dc 100%); + vertical-align: top; + margin-bottom: 1em; +} +fieldset div {margin: 5px 0px;} +fieldset legend { + margin-top: -.1em; + margin-left: -150px; + color: #a40000; + font-family: 'Amerika Sans', sans-serif; + font-variant: small-caps; font-weight: 400; - text-decoration: none; + font-size: 16pt; + text-shadow: 2px 2px 2px #888; } +fieldset .required {font-weight: bold;} +fieldset .buttonbar { + border-radius: 0px 0px 10px 10px; + margin: 0 -10px 0 -160px; +} +fieldset .help_text {font-size: small;} +fieldset .field_name { + text-align: right; + width: 140px; + margin: 0 20px 0 -160px; + padding-top: 3px; + display: inline-block; + clear: left; + vertical-align: top; +} +fieldset input, +fieldset textarea { + border: 1px solid #999999; + border-radius: 5px; + padding: 2px; + margin: 0; +} +fieldset input[maxlength="255"], +fieldset textarea { + box-sizing: border-box; + width: 100%; + max-width: 760px; +} +fieldset ul { + display: inline-block; + padding: 0; +} +fieldset ul li { + list-style: none; + display: inline; +} +fieldset table { + display: inline-table; + max-width: 760px; +} +fieldset.comment { + padding: 0; + legend {margin-left: 15px} + + .buttonbar {margin: 0; width: 100%} +} h1, h2, h3, h4, h5, h6, .player { color: #bc0a19; font-family: 'Amerika Sans', sans-serif; @@ -110,66 +125,57 @@ h1, h2, h3, h4, h5, h6, .player { margin: 1em 0 0.5em 0; text-shadow: 1px 1px 1px #888; vertical-align: baseline; + + a:link, a:visited { + color: #bc0a19; + font-weight: 400; + text-decoration: none; + } } - -.player { - margin:0 +ol { + list-style: cjk-ideographic; + padding-left: 2em; } +table { + border-collapse: collapse; + border-spacing: 0; + width: 100%; + margin: 0 auto 1em auto; -html, div, span, applet, object, iframe, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { - border: 0; - margin: 0; - padding: 0; - vertical-align: baseline; + td { + border-bottom: 1px solid #d3d7cf; + border-top: 1px solid #d3d7cf; + padding: 2px; + vertical-align: middle; + } + + th { + background: #a40000; + color: #fff; + padding: 2px; + vertical-align: middle; + + a:link, a:visited {color: #FFF;} + } + + tr:nth-child(2n+1) {background-color: #eeeeec;} + tr:hover {background-color: #eedcdc;} } - -p { - margin-bottom: 0.5em +ul { + list-style: circle outside; + padding-left: 2em; + margin:0.5em 0 0.5em 0 } +ul.info { + list-style: none; + margin: 0 0 0.5em 0; + padding-left: 0; -img.partner, img.posting_image { - border: 1px solid #babdb6; - display:block; - float: left; - height: 120px; - margin: 0 20px 0 0; - padding: 4px; - width: 200px; + li { + display: inline-block; + margin-right: 1em; + } } - -img.partner:nth-of-type(odd) { - float: right; -} - -input {font: normal 12pt Philosopher, sans-serif;} - -input[type=number] {text-align: right;} -input[maxlength="255"] {width: 100%} -input[readonly="readonly"], input[readonly] { - border: none; - background: transparent; - color: #2e3436; -} - -.cke_chrome {border:0 !important} -.cke_wysiwyg_div {padding: 1em 0 !important} - -li { - margin-bottom: .2em; -} - -.thumbnail { - display: block; - position: relative; - float: left; - height: 140px; - width: 140px; - padding: 5px; - border: 0; - margin: 5px; - background: transparent url('../img/thumbnail-bg.png') top left no-repeat; -} - ul.tabs { width: 100%; list-style: none; @@ -205,102 +211,9 @@ ul.tabs { } } } -div.tab_container { - background-color:#fff; - padding-top: 1em; - -} -ol { - list-style: cjk-ideographic; - padding-left: 2em; -} - -table { - border-collapse: collapse; - border-spacing: 0; - width: 100%; - margin: 0 auto 1em auto; -} - -table td { - border-bottom: 1px solid #d3d7cf; - border-top: 1px solid #d3d7cf; - padding: 2px; - vertical-align: middle; -} - -table th { - background: #a40000; - color: #fff; - padding: 2px; - vertical-align: middle; -} - -table th a:link, table th a:visited { - color: #FFF; -} - -table tr:nth-child(2n+1) { - background-color: #eeeeec; -} - -table tr:hover { - background-color: #eedcdc; -} - -ul { - list-style: circle outside; - padding-left: 30px; -} - -ul.info { - list-style: none; - margin-bottom: 0.5em; - padding-left: 0; -} - -ul.info li { - display: inline-block; - margin-right: 10px; -} - -.buttonbar { - text-align: right; - border-radius: 10px; - background-color: #000000; - background: linear-gradient(to bottom, #45484d 0%,#000000 100%); -} - -/** PAGINATOR **/ -.pagination { - text-align:center -} - -.pagination a, .pagination .current, .pagination .next, .pagination .previous { - display: inline-block; - text-decoration: none; - padding: 0 0.5em 0 0.5em; -} - -.pagination .next { - float: right; - background: none; -} - - -.pagination .previous { - float: left; - background: none; -} - - -.center { - text-align: center; -} -.right { - text-align: right; -} +#bottom_buttonbar {border-radius: 0 0 10px 10px;} +#display {position:relative; text-align: center;} #redbox { color: white; background-color: #a90329; @@ -312,25 +225,106 @@ ul.info li { h2:first-of-type {margin: -20px 0 0 10px; color: black; text-shadow: 1px 1px 1px #ffffff;} } +.avatar { + display: block; + position: relative; + float: left; + width: 70px; + height: 70px; + padding: 0; + margin: 0 1em 1em 0; + box-shadow: 2px 2px 5px #888; + + img { + height: 70px; + width: 70px; + } +} +.buttonbar { + text-align: right; + border-radius: 10px; + background-color: #000000; + background: linear-gradient(to bottom, #45484d 0%,#000000 100%); +} +.center {text-align: center;} +.clear, .clearfix {clear: both;} +.cke_chrome {border:0 !important} +.cke_wysiwyg_div {padding: 1em 0 !important} +.disabled {color: #ccc} .comment { display:table; margin-bottom: 1em; width: 100%; padding: 0; } +.error, ul.errorlist li {color: #a40000;} +.game h2 {margin: 0.5em 0;} +.grid_1,.grid_2,.grid_3,.grid_4,.grid_5,.grid_6,.grid_7,.grid_8,.grid_9,.grid_10,.grid_11,.grid_12 + { + display: inline; + float: left; + margin: 0px 10px; + position: relative; + box-sizing: border-box; +} +.grid_1 { width: 60px;} +.grid_2 {width: 140px;} +.grid_3 {width: 220px;} +.grid_4 {width: 300px;} +.grid_5 {width: 380px;} +.grid_6 {width: 460px;} +.grid_7 {width: 540px;} +.grid_8 {width: 620px;} +.grid_9 {width: 700px;} +.grid_10 {width: 780px;} +.grid_11 {width: 860px;} +.grid_12 {width: 940px;} -fieldset.comment { +.more_link {text-align: right; clear: left;} + +.pagination { + clear: both; + margin-bottom: 1em; padding: 0; + position: relative; + text-align: center; + z-index: 30; - legend {margin-left: 15px} - .buttonbar {margin: 0; width: 100%} - .buttonbar {margin: 0; width: 100%} + a { + background-color: #000000; + background: linear-gradient(to bottom, #45484d 0%,#000000 100%); + border-radius: 5px; + display: inline-block; + font-weight: bold; + padding: 0.5em 1em; + text-decoration: none; + } + + a:link, a:visited {color: #fff;} + a:hover, a.active {color: #bc0a19;} + a.disabled, a.disabled:hover {color: #666} + + a.previous { + float:left; + border-radius: 10px 5px 5px 10px; + } + a.next { + float: right; + border-radius: 5px 10px 10px 5px; + } } -#bottom_buttonbar { - position: absolute; - border-radius: 0 0 10px 10px; - bottom: 0px; - margin: 0; - width: 100% +.player {margin:0 auto; float:none} + +.right {text-align: right;} +.thumbnail { + display: block; + position: relative; + float: left; + height: 140px; + width: 140px; + padding: 5px; + border: 0; + margin: 5px; + background: transparent url('../img/thumbnail-bg.png') top left no-repeat; } diff --git a/src/kasu/static/less/desktop.less b/src/kasu/static/less/desktop.less index da55206..c95a740 100644 --- a/src/kasu/static/less/desktop.less +++ b/src/kasu/static/less/desktop.less @@ -1,12 +1,30 @@ -@media screen and (min-width: @min-desktop-width) { - - #content { +body { + position: relative; + margin: 0; + padding: 0; + min-width: 960px; + height: 100%; +} +#body { + background-color: #ffffff; + background-image: url('../img/kranich.png'), url('../img/header_bg.jpg'); + background-repeat: no-repeat, no-repeat; + background-position: center bottom, center top; + background-attachment: scroll, fixed; +} +#bottom_buttonbar { + position: absolute; + bottom: 0px; + margin: 0; + width: 100% +} +#content { width: 940px; margin: 0px 10px; position: relative; - } - - #display .next, #display .previous { +} +#display .next, +#display .previous { display: block; position: absolute; top: 0px; @@ -17,120 +35,43 @@ text-indent: 9999px; overflow: hidden; opacity: .5; - } - - #display .next:hover, #display .previous:hover { +} +#display .next:hover, +#display .previous:hover { opacity: 1; transition: all 0.2s ease-out; - } - - #display .next { +} +#display .next { background: transparent url(../img/right-arrow.png) no-repeat center center; right: 10px; z-index: 3; - } - - #display .previous { +} +#display .previous { background: transparent url(../img/left-arrow.png) no-repeat center center; left: 10px; z-index: 2 - } - - #display img { - box-shadow: 1px 1px 5px 1px #444; - } - - #footer { +} +#display img {box-shadow: 1px 1px 5px 1px #444;} +#footer { width: 920px; - min-height:50px; + min-height: 50px; margin: 20px auto 0 auto; z-index: 30; - } - #footer p {text-align: center;} - - #footer_kranich { - display: block; - position: relative; - max-width:1250px; - width:100%; - margin:0 auto; - top: -320px; - text-align: right; - z-index: -2; - } - - - - #maincontent { - margin: 0 auto; - height: auto !important; - width: 960px; - min-height: 800px; - padding: 10px 0 2em 0; - position: relative; - z-index: 19; - border-radius: 10px; - background:rgba(255, 255, 255, 0.5); - box-shadow: 0px 0px 20px 1px rgba(0,0,0,0.75); - } - - #messages { - clear: both; - margin: 0 auto; - padding: 8px 0 0 30px; - width: 920px; - list-style: none; - } - - #messages li.success { - color: #253324; - background: #89bd84; - border: 1px solid #253324; - border-radius:10px; - margin: 10px; - padding: 10px; - } - - #navigation, .pagination { - clear: both; - position: relative; - background: url(../img/navigation-bg.png) no-repeat left top; - height: 56px; - list-style: none; - margin: 0 auto; - padding: 8px 35px 0px 25px; - width: 900px; - z-index: 30; - - - a { - background: url(../img/navigation-separator.png) no-repeat right center; - color: #FFF; - display: block; - float: left; - font-weight: bold; - height: 33px; - padding: 17px 15px 0; - text-decoration: none; - } - - a:hover, #navigation a.active { - background: url(../img/navigation-hover.png) repeat-x left top; - color: #3B3B3B; - } - - li { - display: inline; - margin: 0; - } + p {text-align: center;} +} +#google_maps { + position: relative; + top: 0px; + left: 0px; + height: 280px; + padding: 10px; + border-radius: 0px 10px 10px 0px; } - - #jumbotron { clear: both; position: relative; - padding: 0 1px 0 0; + padding: 0; width: 940px; margin: 0 10px 1em 10px; z-index: 5; @@ -140,7 +81,6 @@ background-repeat: no-repeat; background-color: #333; background-position: center left; - > h2, > h1 { color: #eff0ef; text-shadow: 1px 1px 1px #000; @@ -150,7 +90,6 @@ max-width: 600px; margin: 0; } - #teaser_text { display: block; position: absolute; @@ -158,12 +97,69 @@ bottom: 0px; width: 620px; color: #FFF; - background: rgba(0, 0, 0, 0.5);; + background: rgba(0, 0, 0, 0.5); padding: 1em; border-radius: 0px 0px 0px 10px; + + a:link, a:active, a:visited {color: #fff; text-decoration:underline;} } } +#maincontent { + margin: 0 auto; + height: auto !important; + width: 960px; + min-height: 800px; + padding: 10px 0 2em 0; + position: relative; + z-index: 19; + border-radius: 10px; + background: rgba(255, 255, 255, 0.5); + box-shadow: 0px 0px 20px 1px rgba(0, 0, 0, 0.75); +} +#messages { + clear: both; + margin: 0 auto; + padding: 8px 0 0 30px; + width: 920px; + list-style: none; + li.success { + color: #253324; + background: #89bd84; + border: 1px solid #253324; + border-radius: 10px; + margin: 10px; + padding: 10px; + } +} +#navigation { + clear: both; + background: url(../img/navigation-bg.png) no-repeat left top; + height: 56px; + margin: 0 auto; + padding: 8px 35px 0px 25px; + position: relative; + width: 900px; + z-index: 30; + + a { + background: url(../img/navigation-separator.png) no-repeat right center; + color: #FFF; + display: inline-block; + font-weight: bold; + height: 33px; + padding: 17px 15px 0; + text-decoration: none; + } + + a:hover, a.active { + background: url(../img/navigation-hover.png) repeat-x left top; + color: #3B3B3B; + } + + li {display: inline;} +} +#recaptcha_widget_div {margin-top: -20px;} #redbox { position: absolute; top: 0px; @@ -171,41 +167,45 @@ height: 280px; width: 280px; border-radius: 0px 10px 10px 0px; + z-index: 100; } - - - #usernav { +#usernav { position: absolute; top: 0; right: 0; text-align: right; background: black; - background: linear-gradient(to bottom, #45484d 0%,#000000 100%); + background: linear-gradient(to bottom, #45484d 0%, #000000 100%); border-radius: 0 0 0 10px; font-size: 14pt; color: #FFF; padding: 10px 10px; z-index: 50; - box-shadow: -1px -1px 5px 1px rgba(0,0,0,0.75); + box-shadow: -1px -1px 5px 1px rgba(0, 0, 0, 0.75); + a {color: #FFF;} +} - a { - color: #FFF; - } - } - #usernav img { - vertical-align: middle; - } - - .clearfix { - zoom: 1px; - } - - .clearfix:after { - clear: both; - } - - .gallery { +.comment_picture { + display: table-cell; + padding: 0px 10px; + width: 60px; + vertical-align: top; +} +.comment_header { + display: table-cell; + padding: 0px 10px; + width: 140px; + vertical-align: top; +} +.comment_header h3 {margin: 0} +.comment_text { + display: table-cell; + padding: 0px 10px; + width: auto; + max-width: 700px; +} +.gallery { display: inline; float: left; height: 200px; @@ -213,222 +213,57 @@ overflow: hidden; text-align: center; width: 300px; - } - - .gallery .thumbnail { +} +.gallery .thumbnail { display: block; float: none; margin: 5px auto; - } +} - div.thumbnail a.delete_image { +.game img { + float:right; + margin: 0.5em 0 0.5em 1em; + width: 300px; + height: auto; + box-shadow: 1px 1px 5px 1px #444; +} + +.game:nth-child(2n+1) img { + float:left; + margin-right:1em; + margin: 0.5em 2em 0.5em 0; + } + +.officer { + float:left; + width: 140px; + margin:5px; + box-sizing: border-box; + text-align: center; + + img { + border: 0; + border-radius: 50%; + box-shadow: 1px 1px 5px 1px #444; + width:130px; + height:130px; + } + + .function {font-size:small;margin-top: 0.25em} +} + +img.posting_image, img.partner { + float: left; + width: 200px; + height: 120px; + padding: 2px; + margin: 0 1em 1em 0; + border: 1px solid #babdb6; +} + + +.thumbnail a.delete_image { position: absolute; right: 4px; bottom: 0px; - } - - #body { - background-color: #ffffff; - background-image: url('../img/kranich.png'), url('../img/header_bg.jpg'); - background-repeat: no-repeat, no-repeat; - background-position: center bottom, center top; - background-attachment: scroll, fixed; } -body { - font: 12pt Philosopher, Georgia, serif; - line-height: 1; - position: relative; - margin: 0; - height: 100%; - min-width: 960px; - padding: 0; - vertical-align: baseline; - } - - fieldset { - border: none; - color: #2e3436; - border-radius: 10px; - margin: 10px 0 0 0; - padding: 0 10px 0 160px; - background-color: #f2f5f6; - background: linear-gradient(135deg, #f2f5f6 0%, #e3eaed 37%, #c8d7dc 100%); - vertical-align: top; - } - - fieldset legend { - margin-top: -.1em; - margin-left: -150px; - color: #a40000; - font-family: 'Amerika Sans', sans-serif; - font-variant: small-caps; - font-weight: 400; - font-size: 16pt; - text-shadow: 2px 2px 2px #888; - } - - fieldset div { - margin: 5px 0px; - } - - fieldset .required { - font-weight: bold; - } - - fieldset .buttonbar { - border-radius: 0px 0px 10px 10px; - margin: 0 -10px 0 -160px; - } - - fieldset .help_text { - font-size: small; - } - - fieldset .field_name { - text-align: right; - width: 140px; - margin: 0 20px 0 -160px; - padding-top: 3px; - display: inline-block; - clear: left; - vertical-align: top; - } - - fieldset input, fieldset textarea { - border: 1px solid #999999; - border-radius: 5px; - padding: 2px; - margin: 0; - } - - fieldset input[maxlength="255"], fieldset textarea { - box-sizing: border-box; - width: 100%; - max-width: 760px; - } - - fieldset ul { - display: inline-block; - padding: 0; - } - - fieldset ul li { - list-style: none; - display: inline; - } - - fieldset table { - display: inline-table; - max-width: 760px; - } - - img.partner, img.partner_right, img.posting_image { - border: 1px solid #babdb6; - float: left; - height: 120px; - margin: 0 20px 0 0; - padding: 4px; - width: 200px; - } - - img.partner:nth-of-type(odd) { - float: right; - } - - - - .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; - width: 60px; - height: 60px; - } - - .comment_picture { - display: table-cell; - padding: 0px 10px; - width: 60px; - vertical-align: top; - } - - .comment_header { - display: table-cell; - padding: 0px 10px; - width: 140px; - vertical-align: top; - } - - .comment_header h3 { - margin: 0 - } - - .comment_text { - display: table-cell; - padding: 0px 10px; - width: auto; - max-width: 700px; - } - - .userinfo { - width: 140px; - } - - .grid_6 { - width: 460px; - } - - .grid_7 { - width: 540px; - } - - .grid_8 { - width: 620px; - } - - .grid_9 { - width: 700px; - } - - .grid_10 { - width: 780px; - } - - .grid_11 { - width: 860px; - } - - .grid_12 { - width: 940px; - } - - #recaptcha_widget_div { - margin-top: -20px; - } - - #google_maps { - position: relative; - top: 0px; - left: 0px; - height: 280px; - padding: 10px; - border-radius: 0px 10px 10px 0px; - } -} \ No newline at end of file diff --git a/src/kasu/static/less/font-aweseome/animated.less b/src/kasu/static/less/font-awesome/animated.less similarity index 100% rename from src/kasu/static/less/font-aweseome/animated.less rename to src/kasu/static/less/font-awesome/animated.less diff --git a/src/kasu/static/less/font-aweseome/bordered-pulled.less b/src/kasu/static/less/font-awesome/bordered-pulled.less similarity index 100% rename from src/kasu/static/less/font-aweseome/bordered-pulled.less rename to src/kasu/static/less/font-awesome/bordered-pulled.less diff --git a/src/kasu/static/less/font-aweseome/core.less b/src/kasu/static/less/font-awesome/core.less similarity index 100% rename from src/kasu/static/less/font-aweseome/core.less rename to src/kasu/static/less/font-awesome/core.less diff --git a/src/kasu/static/less/font-aweseome/fixed-width.less b/src/kasu/static/less/font-awesome/fixed-width.less similarity index 100% rename from src/kasu/static/less/font-aweseome/fixed-width.less rename to src/kasu/static/less/font-awesome/fixed-width.less diff --git a/src/kasu/static/less/font-aweseome/font-awesome.less b/src/kasu/static/less/font-awesome/font-awesome.less similarity index 100% rename from src/kasu/static/less/font-aweseome/font-awesome.less rename to src/kasu/static/less/font-awesome/font-awesome.less diff --git a/src/kasu/static/less/font-aweseome/icons.less b/src/kasu/static/less/font-awesome/icons.less similarity index 100% rename from src/kasu/static/less/font-aweseome/icons.less rename to src/kasu/static/less/font-awesome/icons.less diff --git a/src/kasu/static/less/font-aweseome/larger.less b/src/kasu/static/less/font-awesome/larger.less similarity index 100% rename from src/kasu/static/less/font-aweseome/larger.less rename to src/kasu/static/less/font-awesome/larger.less diff --git a/src/kasu/static/less/font-aweseome/list.less b/src/kasu/static/less/font-awesome/list.less similarity index 100% rename from src/kasu/static/less/font-aweseome/list.less rename to src/kasu/static/less/font-awesome/list.less diff --git a/src/kasu/static/less/font-aweseome/mixins.less b/src/kasu/static/less/font-awesome/mixins.less similarity index 100% rename from src/kasu/static/less/font-aweseome/mixins.less rename to src/kasu/static/less/font-awesome/mixins.less diff --git a/src/kasu/static/less/font-aweseome/path.less b/src/kasu/static/less/font-awesome/path.less similarity index 100% rename from src/kasu/static/less/font-aweseome/path.less rename to src/kasu/static/less/font-awesome/path.less diff --git a/src/kasu/static/less/font-aweseome/rotated-flipped.less b/src/kasu/static/less/font-awesome/rotated-flipped.less similarity index 100% rename from src/kasu/static/less/font-aweseome/rotated-flipped.less rename to src/kasu/static/less/font-awesome/rotated-flipped.less diff --git a/src/kasu/static/less/font-aweseome/stacked.less b/src/kasu/static/less/font-awesome/stacked.less similarity index 100% rename from src/kasu/static/less/font-aweseome/stacked.less rename to src/kasu/static/less/font-awesome/stacked.less diff --git a/src/kasu/static/less/font-aweseome/variables.less b/src/kasu/static/less/font-awesome/variables.less similarity index 100% rename from src/kasu/static/less/font-aweseome/variables.less rename to src/kasu/static/less/font-awesome/variables.less diff --git a/src/kasu/static/less/header_nav.less b/src/kasu/static/less/header_nav.less index 54014e1..ee836b4 100644 --- a/src/kasu/static/less/header_nav.less +++ b/src/kasu/static/less/header_nav.less @@ -1,6 +1,6 @@ @media screen and (min-width: @min-desktop-width) { #siteheader { - min-height: 100px; + min-height: 110px; width: 960px; margin: 0 auto; padding: 0; @@ -33,17 +33,17 @@ #mainnav { left: 233px; position: absolute; - top: 65px; + bottom: 0px; #toggle, .toggle {display: none;} - > ul.main_menu { - padding: 0px; - position:relativ; + ul.main_menu { + list-style:none; + margin:0; + padding: 0; > li { - display: block; - float: left; + display: inline-block; min-width: 50px; padding: 8px; font: normal small-caps 18px 'Amerika Sans', sans-serif; @@ -70,17 +70,16 @@ display: none; background:rgba(255, 255, 255, 0.8); border-radius: 10px; - padding: 10px 0; + padding: 0.25em; min-width: 10em; position: absolute; - top: 30px; - margin-left: -10px; + top: 100%; + margin: 0 0 0 -15px; box-shadow: -1px 1px 5px 0px rgba(0,0,0,0.75); transition: all 0.25s linear; li { display:block; - padding:2px; float: none; font: normal small-caps 14pt 'Amerika Sans', sans-serif; text-align: left; @@ -89,14 +88,14 @@ li a { display:block; color: black; - padding: 0.5em 0.25em; + padding: 5px; transition: all 0.25s linear; border-radius: 0.5em; } li a:hover { color: white; background: #bc0a19; - background: linear-gradient(135deg, #6d0019 0%, #8f0222 44%, #a90329 100%); + background: linear-gradient(135deg, #a90329 0%,#8f0222 44%,#6d0019 100%); transition: all 0.25s linear; } } diff --git a/src/kasu/static/less/kasu.less b/src/kasu/static/less/kasu.less index 0faf55c..717566c 100644 --- a/src/kasu/static/less/kasu.less +++ b/src/kasu/static/less/kasu.less @@ -1,9 +1,31 @@ @min-desktop-width: 700px; @max-mobile-with: 699px; +@import "font-awesome/path.less"; +@import "font-awesome/variables.less"; + @import "common"; -@import "desktop"; -@import "mobile"; + @import "header_nav"; -@import "print"; -@import "font-aweseome/font-awesome"; + +@media screen and (max-width: @max-mobile-with) { + @import "mobile"; +} + +@media screen and (min-width: @min-desktop-width) { + @import "desktop"; +} + +@page { + margin: 1cm 1cm 1cm 2cm; + size: A4 portrait; +} + +@media print { + @import "print"; +} +@import "font-awesome/core.less"; +@import "font-awesome/larger.less"; +@import "font-awesome/fixed-width.less"; +@import "font-awesome/list.less"; +@import "font-awesome/icons.less"; \ No newline at end of file diff --git a/src/kasu/static/less/mobile.less b/src/kasu/static/less/mobile.less index 808855a..165cdfa 100644 --- a/src/kasu/static/less/mobile.less +++ b/src/kasu/static/less/mobile.less @@ -1,199 +1,216 @@ -@media screen and (max-width: @max-mobile-with) { - - body { - margin: 5px 10px; - background: url('../img/background_mobile.png') no-repeat top center; - background-attachment: fixed; - font: 12pt "Philosopher", Georgia, serif; - } - - - #display .grid_10 { - margin: 0; - position: relative; - z-index: 1 - } - - #display .next, #display .previous { +body { + margin: 5px 10px; + background: url('../img/background_mobile.png') no-repeat top center; + background-attachment: fixed; + font: 12pt "Philosopher", Georgia, serif; +} +img { + max-width: 100%; + height: auto; +} +img.thumbnail { display: block; - position: absolute; - top: 0px; - width: 45px; - height: 100%; - margin: 0px; - padding: 0; - text-indent: 9999px; - overflow: hidden; - opacity: .5; - } - - #display .next:hover, #display .previous:hover { - opacity: .9; - transition: all 0.2s ease-out; - } - - #display .next { - background: transparent url(../img/right-arrow.png) no-repeat center center; - right: 0px; - z-index: 3; - } - - #display .previous { - background: transparent url(../img/left-arrow.png) no-repeat center center; - left: 0px; - z-index: 2 - } - - #footer { - border-top: 1px solid black; - text-align: center; - } - - img.posting_image, img.partner { float: left; - width: 99px; - height: 59px; - padding: 2px; - margin: 1em 0.5em 0 0; - border: 1px solid #babdb6; + height: 70px; + width: 70px; + margin: 5px; + box-shadow: 2px 2px 5px #888; } - - img { - max-width: 100%; - height: auto; - } - - #topnav a { - display: inline-block; - color: #000; - font: 400 small-caps 24pt 'Amerika Sans', sans-serif; - min-width: 80px; - text-align: center; - text-decoration: none; - text-shadow: 2px 2px 2px #2e3436; - padding: 5px; - } - - #topnav a.active { - color: #bc0a19; - } - - #topnav a:hover { - color: #FFF; - } - - #sitelogo { - background: url('../img/logo_mobile.png') no-repeat; - width: 114px; - height: 54px; - left: 5px; +img.posting_image, img.partner { + float: left; + width: 99px; + height: 59px; + padding: 2px; + margin: 1em 0.5em 0 0; + border: 1px solid #babdb6; +} +ul.main_dropdown { + list-style-type: none; margin: 0; padding: 0; - text-indent: -9999px; - top: 5px; - z-index: 20; + + li {padding: 0;} + + a {padding-left: 2em; font-size: 12pt;} +} + +#display .grid_10 { + margin: 0; + position: relative; + z-index: 1 +} +#display .next, #display .previous { + display: block; + position: absolute; + top: 0px; + width: 45px; + height: 100%; + margin: 0px; + padding: 0; + text-indent: 9999px; + overflow: hidden; + opacity: .5; +} +#display .next:hover, #display .previous:hover { + opacity: .9; + transition: all 0.2s ease-out; +} +#display .next { + background: transparent url(../img/right-arrow.png) no-repeat center center; + right: 0px; + z-index: 3; +} +#display .previous { + background: transparent url(../img/left-arrow.png) no-repeat center center; + left: 0px; + z-index: 2 +} +#footer { + border-top: 1px solid black; + text-align: center; +} +#jumbotron {background: none !important;} +#maincontent {width: 100%;} +#mainnav {display: block; float: right} +#navigation { + margin: 10px 0; + padding: 0; + background: #45484d url("../img/navigation-mobile.png") top left repeat-x; + background-size: contain; +} +#navigation a { + font: bold 12px Arial; + color: #FFF; + text-decoration: none; +} +#navigation li { + display: inline-block; + padding: 0.5em 0.3em 0.5em 0.5em; + text-align: center; + border-left: 1px solid #ffffff; + margin: 0; +} +#navigation li:first-of-type {border: none;} +#sitelogo { + background: url('../img/logo_mobile.png') no-repeat; + width: 114px; + height: 54px; + left: 5px; + margin: 0; + padding: 0; + text-indent: -9999px; + top: 5px; + z-index: 20; + float: left; +} +#sitelogo a { + display: block; + width: 114px; + height: 54px; +} +#siteheader:after { + content: ""; + clear: both; + display: block; + visibility: hidden; + height: 0px; +} +#redbox { + margin-top: 1em; + display: block; + } +#teaser { + background: none; + margin-bottom: 1em; +} +#teaser_text {background: rgba(255, 255, 255, 0.5);} +#topnav a { + display: inline-block; + color: #000; + font: 400 small-caps 24pt 'Amerika Sans', sans-serif; + min-width: 80px; + text-align: center; + text-decoration: none; + text-shadow: 2px 2px 2px #2e3436; + padding: 5px; +} +#topnav a.active {color: #bc0a19;} +#topnav a:hover {color: #FFF;} +#toggle, .toggle {display: none;} +#toggle:checked ~ .main_menu { + display: block; + opacity: 1; +} +#toggle:checked ~ .toggle, .toggle:hover {background: #45ABD6} + +.comment_picture { + display: table-cell; + padding: 0px 10px; + width: 60px; + vertical-align: top; +} +.comment_header { + display: table-cell; + padding: 0px 10px; + width: 140px; + vertical-align: top; +} +.comment_header h3 {margin: 0} +.comment {display: block} +.comment_picture { + display: block; + float: left; + vertical-align: top; + width: 60px; +} +.comment_header { + display: block; + float: left; + padding: 0px 10px; + vertical-align: top; + width: 140px; +} +.comment_header h3 {margin: 0} +.comment_text { + border-top: 1px solid #45484d; + display: block; + margin: 0px 10px; + padding-top: 0.5em; + clear: both; +} +.gallery { float: left; + width: 150px; + height: 150px; + margin: 10px; } - - #teaser { - background: none; - margin-bottom: 1em; - } - - #teaser_text { - background: rgba(255, 255, 255, 0.5); - } - - #navigation { - margin: 10px 0; - padding: 0; - background: #45484d url("../img/navigation-mobile.png") top left repeat-x; - background-size: contain; - } - - #navigation li { - display: inline-block; - padding: 0.5em 0.3em 0.5em 0.5em; - text-align: center; - border-left: 1px solid #ffffff; - margin: 0; - } - - #navigation li:first-of-type { - border: none; - } - - #navigation a { - font: bold 12px Arial; - color: #FFF; - text-decoration: none; - } - - #sitelogo a { - display: block; - width: 114px; - height: 54px; - } - - #siteheader:after { - content: "."; - clear: both; - display: block; - visibility: hidden; - height: 0px; - } - - #maincontent { - width: 100%; - } - - /* Dynamische Menü */ - #mainnav { - display: block; - float: right - } - - #toggle, .toggle { - display: none; - } - - #toggle:checked ~ .main_menu { - display: block; - opacity: 1; - } - - #toggle:checked ~ .toggle, .toggle:hover { - background: #45ABD6; - } - - .comment_picture { - display: table-cell; - padding: 0px 10px; - width: 60px; - vertical-align: top; - } - - .comment_header { - display: table-cell; - padding: 0px 10px; +.gallery h3 {font-size: 12pt;} +.game img { + float:right; + margin: 0.5em 0 0.5em 1em; width: 140px; - vertical-align: top; - } - - .comment_header h3 { - margin: 0 - } - - .toggle { - z-index: 2; - display: block; - position: relative; - cursor: pointer; - -webkit-touch-callout: none; - -webkit-user-select: none; - user-select: none; - } - + height: auto; + box-shadow: 1px 1px 5px 1px #444; +} +.game:nth-child(2n+1) img { + float:left; + margin-right:1em; + margin: 0.5em 2em 0.5em 0; + } +.grid_2 { + min-width: 140px; + width:31%; + margin: 1% 0 1% 0; +} +.grid_3 { + width:48%; + margin: 1% 0 1% 0; +} +.grid_4, .grid_5, .grid_6, .grid_7, .grid_8, .grid_9, .grid_10, .grid_11, .grid_12 { + clear: both; + margin:0; + width: 100%; +} .main_menu { position: absolute; display: none; @@ -231,59 +248,37 @@ border-left: 3px solid #a40000; } } -ul.main_dropdown { - list-type: none; - margin: 0; - padding: 0; - - li {padding: 0;} - - a {padding-left: 2em; font-size: 12pt;} - -} - - - #jumbotron { - background: none !important; - } - - #redbox { - margin-top: 1em; - display: block; - } - - - .grid_6, .grid_7, .grid_8, .grid_9, .grid_10, .grid_11, .grid_12 { - width: 100%; - clear: both; - } - - .player { - display: inline; - float: left; - margin-left: 10px; - margin-right: 10px; - position: relative; +.officer { + float:left; + width: 25%; + padding:5px; box-sizing: border-box; - -moz-box-sizing: border-box; - min-width: 60px; - } - - .toggle { - display: block; - width: 150px; - margin: 8px 0; - padding: 10px; - background: #a40000; text-align: center; - color: #FFFFFF; - content: 'Main Menu'; - border-radius: 2px; - box-sizing: border-box; - transition: all 0.5s linear; - } - .thumbnail { + img { + border: 0; + border-radius: 50%; + box-shadow: 1px 1px 5px 1px #444; + width:100%; + height:100%; + } + + .function {font-size:small;margin-top: 0.25em} +} +.toggle { + background: #a40000; + border-radius: 5px; + color: #FFFFFF; + cursor: pointer; + display: block; + margin: 8px 0; + padding: 10px; + position: relative; + transition: all 0.5s linear; + /*user-select: none;*/ + z-index: 2; +} +.thumbnail { display: block; position: relative; float: left; @@ -292,152 +287,9 @@ ul.main_dropdown { width: 70px; margin: 5px; box-shadow: 2px 2px 5px #888; - } - .thumbnail img { - height: 70px; - width: 70px; - } - - .thumbnail a.delete_image { - display: none - } - - img.thumbnail { - display: block; - float: left; - height: 70px; - width: 70px; - margin: 5px; - box-shadow: 2px 2px 5px #888; - } - - fieldset { - border: none; - color: #2e3436; - border-radius: 10px; - margin: 10px 0 0 0; - padding: 0 10px 0 160px; - background: #f2f5f6; /* Old browsers */ - background: linear-gradient(135deg, #f2f5f6 0%, #e3eaed 37%, #c8d7dc 100%); - background: -moz-linear-gradient(-45deg, #f2f5f6 0%, #e3eaed 37%, #c8d7dc 100%); - background: -webkit-gradient(linear, left top, right bottom, color-stop(0%, #f2f5f6), - color-stop(37%, #e3eaed), color-stop(100%, #c8d7dc)); - background: -webkit-linear-gradient(-45deg, #f2f5f6 0%, #e3eaed 37%, #c8d7dc 100%); - background: -o-linear-gradient(-45deg, #f2f5f6 0%, #e3eaed 37%, #c8d7dc 100%); - background: -ms-linear-gradient(-45deg, #f2f5f6 0%, #e3eaed 37%, #c8d7dc 100%); - - } - - fieldset legend { - margin-top: -.1em; - margin-left: -150px; - color: #a40000; - font-family: 'Amerika Sans', sans-serif; - font-variant: small-caps; - font-weight: 400; - font-size: 16pt; - text-shadow: 2px 2px 2px #888; - } - - fieldset .required { - font-weight: bold; - } - - fieldset .error { - color: #a40000 - } - - fieldset .buttonbar { - border-radius: 0px 0px 10px 10px; - margin: 0 -10px 0 -160px; - } - - fieldset .help_text { - font-size: small; - } - - fieldset .field_name { - text-align: right; - width: 140px; - margin: 0 20px 0 -160px; - padding-top: 3px; - display: inline-block; - clear: left; - } - - fieldset input, fieldset textarea { - border: 1px solid #999999; - border-radius: 5px; - padding: 2px; - margin: 0; - font-size: 12pt; - } - - fieldset input[maxlength="255"], fieldset textarea { - width: 99%; - box-sizing: border-box; - } - - fieldset ul { - display: inline-block; - padding: 0; - } - - fieldset ul li { - list-style: none; - display: inline; - } - - .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; - } - - .comment { - display: block - } - - .comment_picture { - display: block; - float: left; - vertical-align: top; - width: 60px; - } - - .comment_header { - display: block; - float: left; - padding: 0px 10px; - vertical-align: top; - width: 140px; - } - - .comment_header h3 { - margin: 0 - } - - .comment_text { - border-top: 1px solid #45484d; - display: block; - margin-left: 0px 10px; - padding-top: 0.5em; - clear: both; - } + img { + height: 70px; + width: 70px; + } } \ No newline at end of file diff --git a/src/kasu/static/less/print.less b/src/kasu/static/less/print.less index 5a15ef0..eb6898c 100644 --- a/src/kasu/static/less/print.less +++ b/src/kasu/static/less/print.less @@ -1,91 +1,45 @@ -@media print { +a:link, a:visited {color: black; font-weight: bold;} +body, article { + width: 100%; + margin: 0; + padding: 0; + color: #000; + background: #fff; +} +h1 {font-size: 32pt;} +h2, h3, h4, h5, h6 { + text-shadow: none; + page-break-after: avoid; +} +img { + max-width: 100% !important; + page-break-inside: avoid; +} +nav, aside {display: none;} +ul {page-break-inside: avoid;} +#footer { + width: 100%; + padding-top: 0.5em; + border-top: 1px solid black; + text-align: center; +} +#jumbotron {background: none !important;} +#maincontent nav {display: none} +#maincontent aside {display: none} +#sitelogo { + background: url(../img/logo.png) top right no-repeat; + background-size: contain; + left: 0; + margin: 0; + padding: 0; + line-height: 1cm; + font-family: 'Amerika Sans', Helvetica; + font-size: 8pt; + top: 5px; + z-index: 99; +} +#comment_form, #comments, #footer, #navigation, #mainnav, +#usernav, #bottom_buttonbar, #footer > form {display: none;} - @page { - size: portrait; - margin: 0.5cm 0.5cm 0.5cm 1cm; - orphans: 3; - widows: 3; - } - - nav, aside, #comment_form, #navigation, #mainnav, #usernav, #bottom_buttonbar, #footer > form { - display: none !important; - } - - #footer { - width: 100%; - padding-top: 0.5em; - border-top: 1px solid black; - text-align: center; - } - - * { - -webkit-print-color-adjust: exact; - print-color-adjust: exact; - } - - a:link, a:visited { - color: black; - font-weight: bold; - } - - #sitelogo { - background: url(../img/logo.png) top right no-repeat; - background-size: contain; - left: 0; - margin: 0; - padding: 0; - line-height: 1cm; - font-family: 'Amerika Sans', Helvetica; - font-size: 8pt; - top: 5px; - z-index: 99; - } - - #jumbotron { - background: none !important; - } - - body, article { - width: 100%; - margin: 0; - padding: 0; - color: #000; - background: #fff; - } - - h1 { - font-size: 32pt; - } - h2, h3, h4, h5, h6 { - text-shadow: none; - page-break-after: avoid; - } - - img { - max-width: 100% !important; - } - - ul, img { - page-break-inside: avoid; - } - - #comment_form, #comments, #footer, #navigation, #mainnav, #usernav, #bottom_buttonbar, #footer > form { - display: none; - } - - .more_link { - display: none - } - - #maincontent nav { - display: none - } - - #maincontent aside { - display: none - } - - .grid_6, grid_7, .grid_8, grid_9, .grid_10, .grid_11, .grid_12 { - width: 100% - } -} \ No newline at end of file +.grid_6, grid_7, .grid_8, grid_9, .grid_10, .grid_11, .grid_12 {width: 100%} +.more_link {display: none} diff --git a/src/kasu/templates/base.html b/src/kasu/templates/base.html index c3d7012..3e781e6 100644 --- a/src/kasu/templates/base.html +++ b/src/kasu/templates/base.html @@ -15,15 +15,28 @@ document.createElement('aside'); document.createElement('footer'); document.createElement('hgroup'); - + - + + + + + + + + + + + + + + {% block opengraph %} @@ -37,7 +50,7 @@ diff --git a/src/kasu/urls.py b/src/kasu/urls.py index e428497..98cc863 100644 --- a/src/kasu/urls.py +++ b/src/kasu/urls.py @@ -17,39 +17,43 @@ urlpatterns = patterns( url(r'^404/$', TemplateView.as_view(template_name='404.html')), url(r'^admin/doc/', include('django.contrib.admindocs.urls')), url(r'^admin/', include(admin.site.urls)), - url(r'^ckeditor/', include('ckeditor.urls')), + url(r'^ckeditor/', include('ckeditor_uploader.urls')), url(r'^comments/', include('django_comments.urls')), url(r'^content/', include('content.urls')), url(r'^events/', include('events.urls')), url(r'^events.ics$', EventListIcal.as_view(), name='events-ical'), url(r'^feeds/latest/$', LatestNews(), name='feed-latest-news'), url(r'^feeds/comments/$', LatestComments(), name='feed-latest-comments'), - url(r'^gallery/', include('gallery.urls')), - url(r'^google25dabc1a49a9ef03.html$', TemplateView.as_view(template_name='google25dabc1a49a9ef03.html')), + url(r'^gallery/', include('events.gallery_urls')), + url(r'^google25dabc1a49a9ef03.html$', TemplateView.as_view( + template_name='google25dabc1a49a9ef03.html')), url(r'^grappelli/', include('grappelli.urls')), url(r'^i18n/', include('django.conf.urls.i18n'), name='start-page'), url(r'^index.html$', StartPage.as_view()), - url(r'^markdown/', include('django_markdown.urls')), url(r'^membership/', include('membership.urls')), url(r'^news/', include('content.news_urls')), url(r'^ranking/', include('mahjong_ranking.urls')), url(r'^ranking/', include('maistar_ranking.urls')), url(r'^robots.txt$', TemplateView.as_view(template_name='robots.txt')), url(r'^users/$', MembershipDetail.as_view(), name='membership-details'), - url(r'^users/(?P[\-\.\d\w]+)/$', MembershipDetail.as_view(), name='membership-details'), + url(r'^users/(?P[\-\.\d\w]+)/$', + MembershipDetail.as_view(), name='membership-details'), ) if settings.DEBUG: urlpatterns += patterns('', - url(r'^media/(?P.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}), - ) + url(r'^media/(?P.*)$', 'django.views.static.serve', + {'document_root': settings.MEDIA_ROOT}), + ) if 'rosetta' in settings.INSTALLED_APPS: - urlpatterns += patterns('', url(r'^rosetta/', include('rosetta.urls')),) + urlpatterns += patterns('', url(r'^rosetta/', + include('rosetta.urls')),) if 'debug_toolbar' in settings.INSTALLED_APPS: import debug_toolbar - urlpatterns += patterns('', url(r'^__debug__/', include(debug_toolbar.urls)),) + urlpatterns += patterns('', url(r'^__debug__/', + include(debug_toolbar.urls)),) urlpatterns += patterns('', url(r'^add_page/(?P[\+\.\-\d\w\/]+)/$', @@ -58,5 +62,6 @@ urlpatterns += patterns('', PageEditForm.as_view(), name='edit-page'), url(r'^(?P[\-\d\w\/]+)\.html$', PageHtml.as_view(), name='view-page'), - url(r'^(?P[\-\d\w\/]+)\.pdf$', PagePdf.as_view()), -) + url(r'^(?P[\-\d\w\/]+)\.pdf$', + PagePdf.as_view()), + ) diff --git a/src/kasu/utils.py b/src/kasu/utils.py index fdc91b4..598f09a 100644 --- a/src/kasu/utils.py +++ b/src/kasu/utils.py @@ -273,13 +273,16 @@ STATUS_CHOICES = ( ) USER_MODEL = get_user_model() + class OverwriteStorage(FileSystemStorage): + def get_available_name(self, name): """ Returns a filename that's free on the target storage system, and available for new content to be written to. """ - # If the filename already exists, remove it as if it was a true file system + # If the filename already exists, remove it as if it was a true file + # system if self.exists(name): os.remove(os.path.join(settings.MEDIA_ROOT, name)) return name @@ -294,7 +297,7 @@ class MassMailer(object): def __init__(self, subject=None, template=None, context=None): """ - + """ self.mail_queue = set() self.recipients = set() @@ -344,7 +347,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.first_name, recipient.last_name, recipient.email) message = mail.EmailMultiAlternatives( subject=self.subject, body=self.txt_template.render(mail_context), diff --git a/src/kasu/wsgi.py b/src/kasu/wsgi.py index df57f08..cf23333 100644 --- a/src/kasu/wsgi.py +++ b/src/kasu/wsgi.py @@ -14,5 +14,3 @@ os.environ['DJANGO_SETTINGS_MODULE'] = 'kasu.settings.production' import django.core.handlers.wsgi application = django.core.handlers.wsgi.WSGIHandler() - - diff --git a/src/mahjong_ranking/admin.py b/src/mahjong_ranking/admin.py index f1057ed..db8d573 100644 --- a/src/mahjong_ranking/admin.py +++ b/src/mahjong_ranking/admin.py @@ -24,7 +24,8 @@ def recalculate(modeladmin, request, queryset): set_dirty(user=kyu_dan_ranking.user_id) elif isinstance(modeladmin, SeasonRankingAdmin): for ladder_ranking in queryset: - set_dirty(user=ladder_ranking.user_id, season=ladder_ranking.season) + set_dirty(user=ladder_ranking.user_id, + season=ladder_ranking.season) recalculate.short_description = _("Recalculate") @@ -41,6 +42,7 @@ def unconfirm(modeladmin, request, queryset): hanchan.save() unconfirm.short_description = _('Set unconfirmed') + class EventRankingAdmin(admin.ModelAdmin): list_filter = ['event'] list_display = ('placement', 'user', 'event', 'avg_placement', 'avg_score', @@ -61,7 +63,7 @@ class HanchanAdmin(admin.ModelAdmin): ('Hanchan', { 'fields': ( ('event', 'season'), - 'start', + 'start', ('player1', 'player1_input_score', 'player1_comment'), ('player2', 'player2_input_score', 'player2_comment'), ('player3', 'player3_input_score', 'player3_comment'), @@ -84,7 +86,7 @@ class KyuDanAdmin(admin.ModelAdmin): 'classes': ('grp-collapse grp-open',), }), ('Frühere Aufzeichnungen', { - 'fields': ('legacy_date', ('legacy_kyu_points', 'legacy_dan_points')), + 'fields': ('legacy_date', 'legacy_hanchan_count', ('legacy_kyu_points', 'legacy_dan_points')), 'classes': ('grp-collapse grp-closed',), }), ) diff --git a/src/mahjong_ranking/forms.py b/src/mahjong_ranking/forms.py index 475764b..b6c70e9 100644 --- a/src/mahjong_ranking/forms.py +++ b/src/mahjong_ranking/forms.py @@ -23,45 +23,33 @@ class HanchanForm(forms.ModelForm): class Meta(object): model = models.Hanchan - fields = ( 'start', - 'player1', 'player1_input_score', - 'player2', 'player2_input_score', - 'player3', 'player3_input_score', - 'player4', 'player4_input_score', + fields = ('start', + 'player1', 'player1_input_score', # 'player1_comment', + 'player2', 'player2_input_score', # 'player2_comment', + 'player3', 'player3_input_score', # 'player3_comment', + 'player4', 'player4_input_score', # 'player4_comment', 'comment') widgets = {'event': forms.HiddenInput(), 'comment': forms.widgets.Textarea(attrs={'rows': 4, 'cols': 40}) - } + } def __init__(self, *args, **kwargs): super(HanchanForm, self).__init__(*args, **kwargs) # self.fields['event'].widget.attrs['disabled'] = True for i in xrange(1, 4): - self.fields['player%d_input_score' % i].widget.attrs['size'] = 6 - self.fields['player%d_input_score' % i].widget.attrs['type'] = 'number' + player_input_score = 'player%d_input_score' % i + self.fields[player_input_score].widget.attrs['size'] = 6 + self.fields[player_input_score].widget.attrs['type'] = 'number' + - ''' - def clean_start(self): - u"""Das Datum darf nicht in der Zukunft liegen und es muss innerhalb - der Dauer des Events liegen.""" - start = self.cleaned_data['start'] - event = self.cleaned_data['event'] - if start > timezone.now(): - raise django.forms.ValidationError( - _("It's not allowed to enter future games.")) - if not event.start <= start <= event.end: - raise django.forms.ValidationError( - _("Only games running during this event are allowed.")) - return start - ''' class HanchanAdminForm(HanchanForm): + class Meta(object): model = models.Hanchan fields = HanchanForm.Meta.fields + ('confirmed',) - class SeasonSelectForm(django.forms.Form): season = django.forms.ChoiceField(label='', choices=('a', 'b', 'c')) diff --git a/src/mahjong_ranking/locale/de/LC_MESSAGES/django.mo b/src/mahjong_ranking/locale/de/LC_MESSAGES/django.mo index 7950a077808f4deb426f8cbb688e3bf38b697f0e..8ac751060f9a5f2f0c6c223afc3d52478f8b4203 100644 GIT binary patch delta 1712 zcmY+^OGs2v9LMpaIirnQmRdgYnWp(_)G{5leDyF*C?t#u8*&h#F)&M7-C7hvAks** z5Fu!xP(iqfTvenb7VgYlu7!jaNiCx9?|KS4%>RDQIgaP?KWFYj{@23s6enbiS&HF7 zx#~5h41*ZJG0ek{7?117$87S5#oxB=rJ;7BH5GldvoH~hQB%~Q{=Xh~U`woV+%KM^ zBY}>~xD5x;gJDd;o46fEQ44v5hj0q_<7dpm?--3qaa#*YMtv_6`IrtK1=wx96XzIH zM8^~zyKx>PVG_o%id~qF+CeF*KY;pP2kyj+s2vX=|7r$NnH|A=e1@8L z!J*PbWdoG~KeKA*jYyYiMGbfsmAWga0sE|B>usxp%Frm13^RdR$fWImiW>g~YCLC_ ziY9nt|FDQhXfNBgm(i4&1Wd;q)PQxUfsWaBo3-8cccG558{bA5(}R_?Ge}c2b|RT@ z%ovqqI$ohx^Z}KLCDaa9QJGjn#xU#1+RP>@_0gPz7Ltt0OggGRAJecJwSi95!h4Z_ zHGP=H{AP%X1|COEJc(^Mg9q^&7GN4_(vBKY3v0%5Y)4Hzf;!W?)@dxHy^LDW58R8f zG_-*nOk#dhq6*gYa94_2VJ|8(VcdiFkdJx7Lpzy4W#%Ow!`G+qVp8swgX^Zf)xg zs{8Y|wAqR>q4~{8qLMg6oF-JXyQ74k(52B&ua;;ebQkJ~5<+RzE!SHxmr&tU+}C$C zDtiZrXrh%+(b=o)BQBicp_I1}x-1=pN+F>$)x{|zRJg$IF{qwN=uN5npriZ0%r3_C zcsy0rfvVa_kuN*3uH0Xf>-PsLt4sY=k@LQi#ERf>Q-3%(aJ}N%jd0|FuPC82IM{zH d*dGey_JtzPeU~CB@n1Zlfx+RS*^T%f&mXwShWG#g delta 1300 zcmXZbUr3Wt7{~EbYi4t6v$Ry0+R{XG=HIL(=0$-Q-XuhlG#X`Al!h%R3Em_!rJG_w z5M9}SFG?6gDhe;kiVC{Oi*->^WIn^7HfqdM4w0gPiVoi3-UknZ|vn{Dr7|P6-cM zV3mEb7MqA$Y&?M4nWI>UXHW^R+V|r&p0K9u_+8XdPT_-j#yrG2;*(Ud5mU%cIOaPK zOBpDjeN|MA+KC2k>Yxqzu4zYg(1|*lDC$V|p*k8wnl>lw_$U?-Uqm&KL=`@bI;v+F zWPOw7L5Y7LmvQlS69%vnw_+JOsE)3n3cG<}oIox726d*NtbTqV<;2aXLU-VD>_as$ zta1JS&e;iLn5h)WVWv?#^9q;YXH=rUs7?Z$n|3B2Td@RHKoo0m7+WxfTJRfcr+%Xv zc9G1{f_@&qO1pi7I3-YUd83j&RUAghj+7HolA-h{sWd%m(-= z(2V)YKr#M8B`)GWrbHpsmRF(*3)}H#)RwpACZkzhZg*W%q_#0}FQ?eIE?i$-XpQzo=EBLp2WB}?TYmsNRIfr-2VVv C_I@Y; diff --git a/src/mahjong_ranking/locale/de/LC_MESSAGES/django.po b/src/mahjong_ranking/locale/de/LC_MESSAGES/django.po index eedbefe..e82451a 100644 --- a/src/mahjong_ranking/locale/de/LC_MESSAGES/django.po +++ b/src/mahjong_ranking/locale/de/LC_MESSAGES/django.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: kasu.mahjong_ranking\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-08-22 23:28+0200\n" -"PO-Revision-Date: 2015-08-22 15:09+0100\n" -"Last-Translator: Christian Berg \n" +"PO-Revision-Date: 2015-09-06 00:13+0200\n" +"Last-Translator: Christian Berg \n" "Language-Team: Kasu \n" "Language: de\n" "MIME-Version: 1.0\n" @@ -375,7 +375,7 @@ msgstr "%s wurde erfolgreich aktualisiert." #: views.py:96 #, python-format msgid "%s has been added successfully. You can now add a new one." -msgstr "%s wurde erfolgreich hinzugefügt. Du kannst eine neue anlagen." +msgstr "%s wurde erfolgreich hinzugefügt. Du kannst eine neue eintragen." #: views.py:113 views.py:129 msgid "Event does not exist" diff --git a/src/mahjong_ranking/management/commands/export-ranking.py b/src/mahjong_ranking/management/commands/export-ranking.py new file mode 100644 index 0000000..01528e2 --- /dev/null +++ b/src/mahjong_ranking/management/commands/export-ranking.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +""" +Export Mahjong Rankings... +""" + +from operator import itemgetter +from django.core.management.base import BaseCommand +from mahjong_ranking.models import SeasonRanking +from openpyxl import Workbook + + +class Command(BaseCommand): + help = "Exports the SeasonRankings" + + def geneate_seasonexcel(self, json_data): + + wb = Workbook() + worksheet = wb.active + + worksheet.append([ + 'Rang', 'Spitzname', + '⌀ Platz', '⌀ Punkte', + 'Hanchans', 'Gut', 'Gewonnen' + ]) + + json_data = json_data.values() + json_data = sorted(json_data, key=itemgetter('placement')) + for row in json_data: + print row + worksheet.append([ + row['placement'], row['username'], + row['avg_placement'], row['avg_score'], + row['hanchan_count'], + row['good_hanchans'], row['won_hanchans'] + ]) + wb.save("sample.xlsx") + + def handle(self, *args, **options): + season_json = SeasonRanking.objects.json_data() + self.geneate_seasonexcel(season_json) diff --git a/src/mahjong_ranking/management/commands/random-ranking.py b/src/mahjong_ranking/management/commands/random-ranking.py index 1bdac6f..294fc07 100644 --- a/src/mahjong_ranking/management/commands/random-ranking.py +++ b/src/mahjong_ranking/management/commands/random-ranking.py @@ -85,7 +85,6 @@ class Command(BaseCommand): self.add_players(hanchan) hanchan.save() - def handle(self, *args, **options): num_hanchans = int(options.get('hanchans', 4)) self.user_list = list(auth.get_user_model().objects.all()) @@ -93,5 +92,3 @@ class Command(BaseCommand): for event in Event.objects.all(): for i in range(random.randrange(2, 8)): self.create_hanchan(event) - - diff --git a/src/mahjong_ranking/managers.py b/src/mahjong_ranking/managers.py index 56985f7..ab95f07 100644 --- a/src/mahjong_ranking/managers.py +++ b/src/mahjong_ranking/managers.py @@ -1,11 +1,11 @@ __author__ = 'christian' from datetime import date - from django.db import models class HanchanManager(models.Manager): + use_for_related_fields = True def confirmed_hanchans(self, user=None, **kwargs): @@ -77,3 +77,25 @@ class SeasonRankingManager(models.Manager): def season_list(self): values_list = self.model.objects.values_list('season', flat=True) return values_list.order_by('season').distinct() + + def json_data(self, season=None): + season = season or date.today().year + json_data = {} + values = self.filter(season=season, placement__isnull=False) + values = values.values('placement', 'user_id', 'user__username', + 'user__first_name', 'user__last_name', 'avg_placement', 'avg_score', + 'hanchan_count', 'good_hanchans', 'won_hanchans') + for user in values: + json_data[user['user_id']] = { + 'placement': user['placement'], + 'user_id': user['user_id'], + 'username': user['user__username'], + 'first_name': user['user__first_name'], + 'last_name': user['user__last_name'], + 'avg_placement': user['avg_placement'], + 'avg_score': user['avg_score'], + 'hanchan_count': user['hanchan_count'], + 'good_hanchans': user['good_hanchans'], + 'won_hanchans': user['won_hanchans'] + } + return json_data diff --git a/src/mahjong_ranking/middleware.py b/src/mahjong_ranking/middleware.py index 8167eca..7c87179 100644 --- a/src/mahjong_ranking/middleware.py +++ b/src/mahjong_ranking/middleware.py @@ -36,38 +36,41 @@ class DenormalizationUpdateMiddleware(object): cache.set('ladder_ranking_queue', set(), 360) for event_id in self.event_queue: - self.update_event_placement() + self.update_event_placements() for season in self.season_queue: self.update_season_placements() - - return response def recalculate_event_rankings(self, queue): # recalculate tournament (event) rankings: for event_id, user_id in queue: - 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] + 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() self.event_queue.add(event_id) return queue def recalulate_kyu_dan_ranking(self, queue): for user_id in queue: - ranking = models.KyuDanRanking.objects.get_or_create(user_id=user_id)[0] + ranking = models.KyuDanRanking.objects.get_or_create(user_id=user_id)[ + 0] ranking.recalculate() return queue def recalculate_ladder_ranking(self, queue): for season, user_id in queue: - ladder = models.SeasonRanking.objects.get_or_create(user_id=user_id, season=season)[0] + ladder = models.SeasonRanking.objects.get_or_create( + user_id=user_id, season=season)[0] ladder.recalculate() self.season_queue.add(season) def update_event_placements(self): for event_id in self.event_queue: - eventranking_set = models.EventRanking.objects.filter(event_id=event_id).order_by('avg_placement', '-avg_score') + eventranking_set = models.EventRanking.objects.filter( + event_id=event_id).order_by('avg_placement', '-avg_score') placement = 1 for ranking in eventranking_set: ranking.placement = placement diff --git a/src/mahjong_ranking/migrations/0001_initial.py b/src/mahjong_ranking/migrations/0001_initial.py new file mode 100644 index 0000000..e2bacf4 --- /dev/null +++ b/src/mahjong_ranking/migrations/0001_initial.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('events', '0005_auto_20150907_2021'), + ] + + operations = [ + migrations.CreateModel( + name='EventRanking', + fields=[ + ('id', models.AutoField(verbose_name='ID', + serialize=False, auto_created=True, primary_key=True)), + ('placement', models.PositiveIntegerField(null=True, blank=True)), + ('avg_placement', models.FloatField(default=4)), + ('avg_score', models.FloatField(default=0)), + ('hanchan_count', models.PositiveIntegerField(default=0)), + ('good_hanchans', models.PositiveIntegerField(default=0)), + ('won_hanchans', models.PositiveIntegerField(default=0)), + ('event', models.ForeignKey(to='events.Event')), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ('placement', 'avg_placement', '-avg_score'), + }, + ), + migrations.CreateModel( + name='Hanchan', + fields=[ + ('id', models.AutoField(verbose_name='ID', + serialize=False, auto_created=True, primary_key=True)), + ('start', models.DateTimeField( + help_text='Wichtig damit die richtigen Hanchans in die Wertung kommen.', verbose_name='Beginn')), + ('player1_input_score', models.IntegerField(verbose_name='Punkte')), + ('player1_game_score', models.PositiveIntegerField( + default=0, verbose_name='Punkte', editable=False)), + ('player1_placement', models.PositiveSmallIntegerField( + default=0, editable=False)), + ('player1_kyu_points', models.SmallIntegerField( + null=True, editable=False, blank=True)), + ('player1_dan_points', models.SmallIntegerField( + null=True, editable=False, blank=True)), + ('player1_bonus_points', models.SmallIntegerField( + null=True, editable=False, blank=True)), + ('player1_comment', models.CharField(verbose_name='Anmerkung', + max_length=255, editable=False, blank=True)), + ('player2_input_score', models.IntegerField(verbose_name='Punkte')), + ('player2_game_score', models.PositiveIntegerField( + default=0, verbose_name='Punkte', editable=False)), + ('player2_placement', models.PositiveSmallIntegerField( + default=0, editable=False)), + ('player2_kyu_points', models.SmallIntegerField( + null=True, editable=False, blank=True)), + ('player2_dan_points', models.SmallIntegerField( + null=True, editable=False, blank=True)), + ('player2_bonus_points', models.SmallIntegerField( + null=True, editable=False, blank=True)), + ('player2_comment', models.CharField(verbose_name='Anmerkung', + max_length=255, editable=False, blank=True)), + ('player3_input_score', models.IntegerField(verbose_name='Punkte')), + ('player3_game_score', models.PositiveIntegerField( + default=0, verbose_name='Punkte', editable=False)), + ('player3_placement', models.PositiveSmallIntegerField( + default=0, editable=False)), + ('player3_kyu_points', models.SmallIntegerField( + null=True, editable=False, blank=True)), + ('player3_dan_points', models.SmallIntegerField( + null=True, editable=False, blank=True)), + ('player3_bonus_points', models.SmallIntegerField( + null=True, editable=False, blank=True)), + ('player3_comment', models.CharField(verbose_name='Anmerkung', + max_length=255, editable=False, blank=True)), + ('player4_input_score', models.IntegerField(verbose_name='Punkte')), + ('player4_game_score', models.PositiveIntegerField( + default=0, verbose_name='Punkte', editable=False)), + ('player4_placement', models.PositiveSmallIntegerField( + default=0, editable=False)), + ('player4_kyu_points', models.SmallIntegerField( + null=True, editable=False, blank=True)), + ('player4_dan_points', models.SmallIntegerField( + null=True, editable=False, blank=True)), + ('player4_bonus_points', models.SmallIntegerField( + null=True, editable=False, blank=True)), + ('player4_comment', models.CharField(verbose_name='Anmerkung', + max_length=255, editable=False, blank=True)), + ('comment', models.TextField(verbose_name='Anmerkung', blank=True)), + ('confirmed', models.BooleanField( + default=True, help_text='Nur g\xfcltige und best\xe4tigte Hanchans kommen in die Wertung.', verbose_name='Wurde best\xe4tigt')), + ('player_names', models.CharField(max_length=255, editable=False)), + ('season', models.PositiveSmallIntegerField( + verbose_name='Saison', editable=False, db_index=True)), + ('event', models.ForeignKey(to='events.Event')), + ('player1', models.ForeignKey(related_name='user_hanchan+', + verbose_name='Spieler 1', to=settings.AUTH_USER_MODEL)), + ('player2', models.ForeignKey(related_name='user_hanchan+', + verbose_name='Spieler 2', to=settings.AUTH_USER_MODEL)), + ('player3', models.ForeignKey(related_name='user_hanchan+', + verbose_name='Spieler 3', to=settings.AUTH_USER_MODEL)), + ('player4', models.ForeignKey(related_name='user_hanchan+', + verbose_name='Spieler 4', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ('-start',), + 'verbose_name': 'Hanchan', + 'verbose_name_plural': 'Hanchans', + }, + ), + migrations.CreateModel( + name='KyuDanRanking', + fields=[ + ('id', models.AutoField(verbose_name='ID', + serialize=False, auto_created=True, primary_key=True)), + ('dan', models.PositiveSmallIntegerField(null=True, blank=True)), + ('dan_points', models.PositiveIntegerField(default=0)), + ('kyu', models.PositiveSmallIntegerField( + default=10, null=True, blank=True)), + ('kyu_points', models.PositiveIntegerField(default=0)), + ('won_hanchans', models.PositiveIntegerField(default=0)), + ('good_hanchans', models.PositiveIntegerField(default=0)), + ('hanchan_count', models.PositiveIntegerField(default=0)), + ('legacy_date', models.DateField(null=True, blank=True)), + ('legacy_dan_points', models.PositiveIntegerField(default=0)), + ('legacy_kyu_points', models.PositiveIntegerField(default=0)), + ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ('-dan', '-dan_points', '-kyu_points'), + 'verbose_name': 'Ky\u016b/Dan Wertung', + 'verbose_name_plural': 'Ky\u016b/Dan Wertungen', + }, + ), + migrations.CreateModel( + name='SeasonRanking', + fields=[ + ('id', models.AutoField(verbose_name='ID', + serialize=False, auto_created=True, primary_key=True)), + ('season', models.PositiveSmallIntegerField(verbose_name='Saison')), + ('placement', models.PositiveIntegerField(null=True, blank=True)), + ('avg_placement', models.FloatField(null=True, blank=True)), + ('avg_score', models.FloatField(null=True, blank=True)), + ('hanchan_count', models.PositiveIntegerField(default=0)), + ('good_hanchans', models.PositiveIntegerField(default=0)), + ('won_hanchans', models.PositiveIntegerField(default=0)), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ('placement', 'avg_placement', '-avg_score'), + }, + ), + ] diff --git a/src/mahjong_ranking/migrations/0002_kyudanranking_legacy_hanchan_count.py b/src/mahjong_ranking/migrations/0002_kyudanranking_legacy_hanchan_count.py new file mode 100644 index 0000000..57dba04 --- /dev/null +++ b/src/mahjong_ranking/migrations/0002_kyudanranking_legacy_hanchan_count.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('mahjong_ranking', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='kyudanranking', + name='legacy_hanchan_count', + field=models.PositiveIntegerField(default=0), + ), + ] diff --git a/src/aggregator/migrations/__init__.py b/src/mahjong_ranking/migrations/__init__.py similarity index 100% rename from src/aggregator/migrations/__init__.py rename to src/mahjong_ranking/migrations/__init__.py diff --git a/src/mahjong_ranking/models.py b/src/mahjong_ranking/models.py index 8479314..64b4304 100644 --- a/src/mahjong_ranking/models.py +++ b/src/mahjong_ranking/models.py @@ -1,7 +1,9 @@ # -*- encoding: utf-8 -*- -# TODO: Rankings archiv Flag erstellen, womit sie nicht mehr neuberechnet werden dürfen. +# TODO: Rankings archiv Flag erstellen, womit sie nicht mehr neuberechnet +# werden dürfen. +from __future__ import division from datetime import date from django.conf import settings @@ -9,11 +11,10 @@ from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse from django.db import models from django.utils import timezone - from django.utils.translation import ugettext as _ from events.models import Event -from . import KYU_RANKS, DAN_RANKS, DAN_RANKS_DICT, logger, set_dirty, MIN_HANCHANS_FOR_LADDER +from . import KYU_RANKS, DAN_RANKS, DAN_RANKS_DICT, logger, set_dirty from . import managers kyu_dan_rankings = set() @@ -40,7 +41,7 @@ class EventRanking(models.Model): ordering = ('placement', 'avg_placement', '-avg_score',) def get_absolute_url(self): - return reverse('event-ranking', args=[self.tourney_id]) + return reverse('event-ranking', args=[self.event_id]) def recalculate(self): """ @@ -50,23 +51,33 @@ class EventRanking(models.Model): können zwar sehr leicht errechnet werden, es macht trotzdem Sinn sie zwischen zu speichern. """ - logger.info(u'Recalculate EventRanking for Player %s in %s', self.user, - self.event.name) # @IgnorePep8 - event_hanchans = Player.objects.confirmed_hanchans(user=self.user_id, - event=self.event_id) # @IgnorePep8 - aggregator = event_hanchans.aggregate( - models.Avg('placement'), - models.Avg('game_score'), - models.Count('pk')) - self.avg_placement = aggregator['placement__avg'] - self.avg_score = aggregator['score__avg'] - self.hanchan_count = aggregator['pk__count'] - self.good_hanchans = event_hanchans.filter(placement__lt=3).count() - self.won_hanchans = event_hanchans.filter(placement=1).count() + logger.info( + u'Recalculate EventRanking for Player %s in %s', + self.user, self.event.name + ) + sum_placement = 0.0 + sum_score = 0.0 + event_hanchans = Hanchan.objects.confirmed_hanchans( + user=self.user_id, + event=self.event_id + ) + self.hanchan_count = event_hanchans.count() + self.won_hanchans = 0 + self.good_hanchans = 0 + if self.hanchan_count <= 0: self.delete() else: - self.save() + for hanchan in event_hanchans: + hanchan.get_playerdata(user=self.user) + sum_placement += hanchan.placement + sum_score += hanchan.game_score + self.won_hanchans += 1 if hanchan.placement == 1 else 0 + self.good_hanchans += 1 if hanchan.placement == 2 else 0 + self.avg_placement = sum_placement / self.hanchan_count + self.avg_score = sum_score / self.hanchan_count + self.save(force_update=True) + class Hanchan(models.Model): @@ -76,52 +87,88 @@ class Hanchan(models.Model): Außerdem gehört jede Hanchan zu einer Veranstaltung. """ event = models.ForeignKey(Event) - start = models.DateTimeField(_('Start'), - help_text=_('This is crucial to get the right Hanchans that scores') - ) + start = models.DateTimeField( + _('Start'), + help_text=_('This is crucial to get the right Hanchans that scores') + ) - player1 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_hanchan+', verbose_name=_('Player 1')) + player1 = models.ForeignKey( + settings.AUTH_USER_MODEL, related_name='user_hanchan+', + verbose_name=_('Player 1')) player1_input_score = models.IntegerField(_('Score')) - player1_game_score = models.PositiveIntegerField(_('Score'), default=0, editable=False) - player1_placement = models.PositiveSmallIntegerField(default=0, editable=False) - player1_kyu_points = models.SmallIntegerField(blank=True, null=True, editable=False) - player1_dan_points = models.SmallIntegerField(blank=True, null=True, editable=False) - player1_bonus_points = models.SmallIntegerField(blank=True, null=True, editable=False) - player1_comment = models.CharField(_('Comment'), blank=True, max_length=255, editable=False) + player1_game_score = models.PositiveIntegerField( + _('Score'), default=0, editable=False) + player1_placement = models.PositiveSmallIntegerField( + default=0, editable=False) + player1_kyu_points = models.SmallIntegerField( + blank=True, null=True, editable=False) + player1_dan_points = models.SmallIntegerField( + blank=True, null=True, editable=False) + player1_bonus_points = models.SmallIntegerField( + blank=True, null=True, editable=False) + player1_comment = models.CharField( + _('Comment'), blank=True, max_length=255, editable=False) - player2 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_hanchan+', verbose_name=_('Player 2')) + player2 = models.ForeignKey( + settings.AUTH_USER_MODEL, related_name='user_hanchan+', + verbose_name=_('Player 2')) player2_input_score = models.IntegerField(_('Score')) - player2_game_score = models.PositiveIntegerField(_('Score'), default=0, editable=False) - player2_placement = models.PositiveSmallIntegerField(default=0, editable=False) - player2_kyu_points = models.SmallIntegerField(blank=True, null=True, editable=False) - player2_dan_points = models.SmallIntegerField(blank=True, null=True, editable=False) - player2_bonus_points = models.SmallIntegerField(blank=True, null=True, editable=False) - player2_comment = models.CharField(_('Comment'), blank=True, max_length=255, editable=False) + player2_game_score = models.PositiveIntegerField( + _('Score'), default=0, editable=False) + player2_placement = models.PositiveSmallIntegerField( + default=0, editable=False) + player2_kyu_points = models.SmallIntegerField( + blank=True, null=True, editable=False) + player2_dan_points = models.SmallIntegerField( + blank=True, null=True, editable=False) + player2_bonus_points = models.SmallIntegerField( + blank=True, null=True, editable=False) + player2_comment = models.CharField( + _('Comment'), blank=True, max_length=255, editable=False) - player3 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_hanchan+', verbose_name=_('Player 3')) + player3 = models.ForeignKey( + settings.AUTH_USER_MODEL, related_name='user_hanchan+', + verbose_name=_('Player 3')) player3_input_score = models.IntegerField(_('Score')) - player3_game_score = models.PositiveIntegerField(_('Score'), default=0, editable=False) - player3_placement = models.PositiveSmallIntegerField(default=0, editable=False) - player3_kyu_points = models.SmallIntegerField(blank=True, null=True, editable=False) - player3_dan_points = models.SmallIntegerField(blank=True, null=True, editable=False) - player3_bonus_points = models.SmallIntegerField(blank=True, null=True, editable=False) - player3_comment = models.CharField(_('Comment'), blank=True, max_length=255, editable=False) + player3_game_score = models.PositiveIntegerField( + _('Score'), default=0, editable=False) + player3_placement = models.PositiveSmallIntegerField( + default=0, editable=False) + player3_kyu_points = models.SmallIntegerField( + blank=True, null=True, editable=False) + player3_dan_points = models.SmallIntegerField( + blank=True, null=True, editable=False) + player3_bonus_points = models.SmallIntegerField( + blank=True, null=True, editable=False) + player3_comment = models.CharField( + _('Comment'), blank=True, max_length=255, editable=False) - player4 = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='user_hanchan+', verbose_name=_('Player 4')) + player4 = models.ForeignKey( + settings.AUTH_USER_MODEL, related_name='user_hanchan+', + verbose_name=_('Player 4')) player4_input_score = models.IntegerField(_('Score')) - player4_game_score = models.PositiveIntegerField(_('Score'), default=0, editable=False) - player4_placement = models.PositiveSmallIntegerField(default=0, editable=False) - player4_kyu_points = models.SmallIntegerField(blank=True, null=True, editable=False) - player4_dan_points = models.SmallIntegerField(blank=True, null=True, editable=False) - player4_bonus_points = models.SmallIntegerField(blank=True, null=True, editable=False) - player4_comment = models.CharField(_('Comment'), blank=True, max_length=255, editable=False) + player4_game_score = models.PositiveIntegerField( + _('Score'), default=0, editable=False) + player4_placement = models.PositiveSmallIntegerField( + default=0, editable=False) + player4_kyu_points = models.SmallIntegerField( + blank=True, null=True, editable=False) + player4_dan_points = models.SmallIntegerField( + blank=True, null=True, editable=False) + player4_bonus_points = models.SmallIntegerField( + blank=True, null=True, editable=False) + player4_comment = models.CharField( + _('Comment'), blank=True, max_length=255, editable=False) 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.') + help_text=_( + 'Only valid and confirmed Hanchans ' + 'will be counted in the rating.') ) player_names = models.CharField(max_length=255, editable=False) - season = models.PositiveSmallIntegerField(_('Season'), editable=False, db_index=True) + season = models.PositiveSmallIntegerField( + _('Season'), editable=False, db_index=True) objects = managers.HanchanManager() class Meta(object): @@ -137,7 +184,8 @@ class Hanchan(models.Model): def clean(self): """ Check if 4 different Players are attending the Game, - if the Game between the opening hours and the Hanchan has a total of 100.000 Points. + if the Game between the opening hours and the Hanchan has a total of + 100.000 Points. """ errors = {} score_sum = 0 @@ -155,18 +203,21 @@ class Hanchan(models.Model): score_sum += input_score game_score = input_score if input_score > 0 else 0 if player in player_set: - errors['player%d' % i] = _("%s can't attend the same game multiple times") % player + errors['player%d' % i] = _( + "%s can't attend the same game multiple times") % player else: player_set.add(player) setattr(self, 'player%d_game_score' % i, game_score) # Check if the game was played during the event if self.start > timezone.now(): - errors['start'] = _("Games in the future may not be added, Dr. Brown") + errors['start'] = _( + "Games in the future may not be added, Dr. Brown") elif not (self.event.start <= self.start <= self.event.end): errors['start'] = _("Only games during the event are allowed") elif score_sum < 100000: - errors['player4_input_score'] = _('Gamescore is lower then 100.000 Pt.') + errors['player4_input_score'] = _( + 'Gamescore is lower then 100.000 Pt.') elif score_sum > 100000: errors['player4_input_score'] = _('Gamescore is over 100.000 Pt.') @@ -190,12 +241,18 @@ class Hanchan(models.Model): else: player['placement'] = placement setattr(self, "player%d" % player_nr, player['user']) - setattr(self, "player%d_input_score" % player_nr, player['input_score']) - setattr(self, "player%d_game_score" % player_nr, player['game_score']) - setattr(self, "player%d_placement" % player_nr, player['placement']) - setattr(self, "player%d_kyu_points" % player_nr, player['kyu_points']) - setattr(self, "player%d_dan_points" % player_nr, player['dan_points']) - setattr(self, "player%d_bonus_points" % player_nr, player['bonus_points']) + setattr(self, "player%d_input_score" % + player_nr, player['input_score']) + setattr(self, "player%d_game_score" % + player_nr, player['game_score']) + setattr(self, "player%d_placement" % + player_nr, player['placement']) + setattr(self, "player%d_kyu_points" % + player_nr, player['kyu_points']) + setattr(self, "player%d_dan_points" % + player_nr, player['dan_points']) + setattr(self, "player%d_bonus_points" % + player_nr, player['bonus_points']) setattr(self, "player%d_comment" % player_nr, player['comment']) other_player_placement = player['placement'] other_player_game_score = player['game_score'] @@ -206,13 +263,13 @@ class Hanchan(models.Model): def get_absolute_url(self): return "{url:s}#{id:d}".format( - url=reverse('event-hanchan-list', kwargs={'event':self.event_id}), - id= self.pk + url=reverse('event-hanchan-list', kwargs={'event': self.event_id}), + id=self.pk ) def get_playerdata(self, user): - """i small workaround to access score, placement and points of a - specific user prominent from a in the user templates""" + """small workaround to access score, placement and points of a + specific user prominent in the user templates""" for player in ('player1', 'player2', 'player3', 'player4'): if getattr(self, player) == user: self.user = user @@ -252,7 +309,8 @@ class Hanchan(models.Model): 'comment': getattr(self, 'player%d_comment' % i), }) # sort player by Score: - return sorted(player_list, key=lambda player: player['input_score'], reverse=True) + return sorted(player_list, key=lambda player: player['input_score'], + reverse=True) def save(self, **kwargs): self.season = self.event.mahjong_season or date.today().year @@ -276,12 +334,13 @@ class KyuDanRanking(models.Model): good_hanchans = models.PositiveIntegerField(default=0) hanchan_count = models.PositiveIntegerField(default=0) legacy_date = models.DateField(blank=True, null=True) + legacy_hanchan_count = models.PositiveIntegerField(default=0) legacy_dan_points = models.PositiveIntegerField(default=0) legacy_kyu_points = models.PositiveIntegerField(default=0) wins_in_a_row = 0 class Meta(object): - ordering = ('-dan_points', '-kyu_points',) + ordering = ('-dan', '-dan_points', '-kyu_points',) verbose_name = _(u'Kyū/Dan Ranking') verbose_name_plural = _(u'Kyū/Dan Rankings') @@ -316,15 +375,18 @@ class KyuDanRanking(models.Model): hanchan.dan_points += bonus_points hanchan.bonus_points += bonus_points - hanchan.player_comment += '3 Siege in Folge: +%d Punkte auf den %d. Dan. ' % (bonus_points, new_dan_rank) + hanchan.player_comment += '3 Siege in Folge: +%d Punkte auf den ' \ + '%d. Dan. ' % ( + bonus_points, new_dan_rank) self.dan_points += bonus_points + self.wins_in_a_row = 0 # TODO: Komplett Überabreiten! def append_tournament_bonuspoints(self, hanchan): """ Prüft ob es die letzte Hanchan in einem Turnier war. Wenn ja werden - bei Bedarf Bonuspunkte vergeben, falls der Spieler das Turnier gewonnen - hat. + bei Bedarf Bonuspunkte vergeben, falls der Spieler das Turnier + gewonnen hat. :param hanchan: Ein Player Objekt """ bonus_points = 0 @@ -333,24 +395,27 @@ class KyuDanRanking(models.Model): ).order_by('-start') last_hanchan_this_event = hanchans_this_event[0] if hanchan != last_hanchan_this_event: - return # Das braucht nur am Ende eines Turnieres gemacht werden. - event_ranking = EventRanking.objects.get( - user=self.user, - event=hanchan.event) - if event_ranking.placement == 1: - bonus_points += 4 - hanchan.comment += '+4 Punkte für den Sieg des Turnieres. ' - if event_ranking.avg_placement == 1: - bonus_points += 8 - hanchan.comment += '+8 Pkt: alle Spiele des Turnieres gewonnen. ' + return False # Das braucht nur am Ende eines Turnieres gemacht werden. + else: + event_ranking = EventRanking.objects.get( + user=self.user, + event=hanchan.event + ) + if event_ranking.placement == 1: + bonus_points += 4 + hanchan.player_comment += u'+4 Punkte Turnier gewonnen. ' + if event_ranking.avg_placement == 1: + bonus_points += 8 + hanchan.player_comment += u'+8 Pkt: alle Spiele des Turnieres gewonnen. ' - if bonus_points and self.dan: - hanchan.dan_points += bonus_points - self.dan_points += bonus_points - elif bonus_points: - hanchan.kyu_points += bonus_points - self.kyu_points += bonus_points - hanchan.bonus_points += bonus_points + if bonus_points and self.dan: + hanchan.dan_points += bonus_points + self.dan_points += bonus_points + elif bonus_points: + hanchan.kyu_points += bonus_points + self.kyu_points += bonus_points + hanchan.bonus_points += bonus_points + return True def get_absolute_url(self): if self.dan or self.dan_points > 0: @@ -363,32 +428,33 @@ class KyuDanRanking(models.Model): Fetches all valid Hanchans from this Player and recalculates his Kyu/Dan Ranking. """ - self.kyu_points = self.legacy_kyu_points or 0 - self.dan_points = self.legacy_dan_points or 0 self.dan = None + self.dan_points = self.legacy_dan_points or 0 self.kyu = None - self.won_hanchans = 0 - self.good_hanchans = 0 + self.kyu_points = self.legacy_kyu_points or 0 + self.hanchan_count = self.legacy_hanchan_count or 0 - logger.info("recalculating Kyu/Dan punkte for %s...", self.user) + self.good_hanchans = 0 + self.won_hanchans = 0 + + logger.info("recalculating Kyu/Dan points for %s...", self.user) self.update_rank() valid_hanchans = Hanchan.objects.confirmed_hanchans(user=self.user) valid_hanchans = valid_hanchans.order_by('start') if self.legacy_date: valid_hanchans = valid_hanchans.filter(start__gt=self.legacy_date) - - self.hanchan_count = valid_hanchans.count() + self.hanchan_count += valid_hanchans.count() for hanchan in valid_hanchans: hanchan.get_playerdata(self.user) - hanchan.bonus_points=0 - hanchan.player_comment="" + hanchan.bonus_points = 0 + hanchan.player_comment = u"" self.update_hanchan_points(hanchan) if hanchan.event.mahjong_tournament: self.append_tournament_bonuspoints(hanchan) self.update_rank() self.append_3_in_a_row_bonuspoints(hanchan) self.update_rank() - self.won_hanchans += 1 if hanchan.placement == 1 else 0 + self.won_hanchans += 1 if hanchan.placement == 1 else 0 self.good_hanchans += 1 if hanchan.placement == 2 else 0 hanchan.update_playerdata(self.user) hanchan.save(force_update=True) @@ -447,7 +513,8 @@ class KyuDanRanking(models.Model): hanchan.kyu_points -= (self.kyu_points + hanchan.kyu_points) self.kyu_points += hanchan.kyu_points - # TODO: Merkwürdige Methode die zwar funktioniert aber nicht sehr aussagekräfig ist. Überarbeiten? + # TODO: Merkwürdige Methode die zwar funktioniert aber nicht sehr + # aussagekräfig ist. Überarbeiten? def update_rank(self): if self.dan and self.dan_points < 0: self.dan_points = 0 @@ -493,7 +560,8 @@ class SeasonRanking(models.Model): return reverse('player-ladder-score', args=[self.user.username]) def recalculate(self): - season_hanchans = Hanchan.objects.season_hanchans(user=self.user, season=self.season) + season_hanchans = Hanchan.objects.season_hanchans( + user=self.user, season=self.season) sum_placement = 0 sum_score = 0 self.placement = None @@ -501,7 +569,9 @@ class SeasonRanking(models.Model): self.good_hanchans = 0 self.won_hanchans = 0 - logger.info(u'Recalculate LadderRanking for Player %s in Season %s', self.user, self.season) + logger.info( + u'Recalculate LadderRanking for Player %s in Season %s', + self.user, self.season) for hanchan in season_hanchans: sum_placement += hanchan.placement sum_score += hanchan.game_score @@ -517,16 +587,21 @@ class SeasonRanking(models.Model): def update_ranking(sender, instance, **kwargs): - for user in (instance.player1, instance.player2, instance.player3, instance.player4): - logger.debug("Marking %s's kyu/dan for recalculation", user) + for user in ( + instance.player1, instance.player2, instance.player3, instance.player4): + logger.debug("marking %s's kyu/dan for recalculation", user) set_dirty(user=user.id) + logger.debug( + "marking event %s for recalculation.", instance.event) + set_dirty(event=instance.event_id, user=user.id) + if instance.season: - logger.debug("Marking %s's ladder %i season for recalculation.", user, instance.season) + logger.debug( + "marking %s's ladder %i season for recalculation.", user, + instance.season) set_dirty(user=user.id, season=instance.season) - if instance.event.mahjong_tournament: - logger.debug("Marking tournament %s for recalculation.", instance.event) - set_dirty(event=instance.event_id, user=user.id) set_dirty(season=instance.season) + models.signals.pre_delete.connect(update_ranking, sender=Hanchan) models.signals.post_save.connect(update_ranking, sender=Hanchan) diff --git a/src/mahjong_ranking/templates/mahjong_ranking/eventhanchan_list.html b/src/mahjong_ranking/templates/mahjong_ranking/eventhanchan_list.html index 2da2137..941c5e3 100644 --- a/src/mahjong_ranking/templates/mahjong_ranking/eventhanchan_list.html +++ b/src/mahjong_ranking/templates/mahjong_ranking/eventhanchan_list.html @@ -1,5 +1,5 @@ {% extends "events/event_detail.html" %} -{% load i18n humanize %} +{% load i18n humanize thumbnail %} {% block title %}Hanchans: {{ event.name }}{% endblock %} @@ -7,38 +7,45 @@

    {% trans 'Played Hanchans' %}

     

    {% for hanchan in hanchan_list %} -

    {{hanchan.start|time:'H:i'}}: {{ hanchan.player_names }}

    -{% for player in hanchan.player_list %} - {{ player.user }} -
    -

    {{ player.user }}

    -

    {{ player.placement|ordinal }} {% trans 'Place' %}
    - {% trans 'Score' %}: {{ player.game_score|intcomma }}
    - {% if player.dan_points != None %} - {% trans 'Dan Points' %}: {{ player.dan_points }} - {% else %} - {% trans 'Kyu Points' %}: {{ player.kyu_points|default:0 }} - {% endif %} -

    -
    -{% endfor %} -{% if not hanchan.confirmed %} -

    Diese Hanchan wurde nicht anerkannt und wird daher nicht gezählt.

    -{% elif hanchan.comment %} -

    {{ hanchan.comment }}

    -{% endif %} -

    {{hanchan.start|time:'H:i'}}: {{ hanchan.player_names }}

    + {% for player in hanchan.player_list %} +
    + + {{ player.user }} +
      +
    • {{ player.placement|ordinal }} {% trans 'Place' %}
    • +
    • {% trans 'Score' %}: {{ player.game_score|intcomma }}
    • + {% if player.dan_points != None %} +
    • {% trans 'Dan Points' %}: {{ player.dan_points }}
    • + {% else %} +
    • {% trans 'Kyu Points' %}: {{ player.kyu_points|default:0 }}
    • + {% endif %} +
    +
    + {% endfor %} + {% if not hanchan.confirmed %} +

    Diese Hanchan wurde nicht anerkannt und wird daher nicht gezählt.

    + {% elif hanchan.comment %} +

    {{ hanchan.comment }}

    {% endif %} - {% if perms.mahjong_ranking.change_hanchan %} - {% trans 'Edit Hanchan' %} - {% endif %} -

    + {% empty %} -

    {% trans 'No Hanchan has been added to this event yet.'%}

    +

    {% trans 'No Hanchan has been added to this event yet.'%}

    {% endfor %} {% endblock %} diff --git a/src/mahjong_ranking/templates/mahjong_ranking/eventranking_list.html b/src/mahjong_ranking/templates/mahjong_ranking/eventranking_list.html index ecd8769..5c0cfb7 100644 --- a/src/mahjong_ranking/templates/mahjong_ranking/eventranking_list.html +++ b/src/mahjong_ranking/templates/mahjong_ranking/eventranking_list.html @@ -1,6 +1,5 @@ {% extends "events/event_detail.html" %} - -{% load i18n comments%} +{% load i18n comments humanize thumbnail %} {% block title %}{% trans "Tournament Ranking" %}: {{ event.name }}{% endblock %} {% block teaser %}

    {% trans "Tournament Ranking" %}: {{ event.name }}

    {% endblock %} @@ -28,17 +27,14 @@ {% for player in eventranking_list %} {{player.placement}}. - - {% if player.user.avatar %} - - {% else %} - - {% endif %} - - {{player.user}} - {% if user.is_authenticated %}{{player.user.last_name}} {{player.user.first_name}}{% else %}---{% endif %} - {{player.avg_placement|floatformat:2 }} - {{player.avg_score|floatformat:0 }} + + {{player.user}} + {% if user.is_authenticated %}{{player.user.last_name}} + {{player.user.first_name}}{% else %}---{% endif %} + {{player.avg_placement|floatformat:2}} + {{player.avg_score|floatformat:0|intcomma }} {{player.hanchan_count}} {{player.good_hanchans}} {{player.won_hanchans}} diff --git a/src/mahjong_ranking/templates/mahjong_ranking/hanchan_confirm_delete.html b/src/mahjong_ranking/templates/mahjong_ranking/hanchan_confirm_delete.html index 86a3192..f144c7f 100644 --- a/src/mahjong_ranking/templates/mahjong_ranking/hanchan_confirm_delete.html +++ b/src/mahjong_ranking/templates/mahjong_ranking/hanchan_confirm_delete.html @@ -1,5 +1,5 @@ {% extends "events/event_detail.html" %} -{% load i18n humanize %} +{% load i18n humanize thumbnail %} {% block meta_title %}{% trans 'Delete Hanchan' %}{% endblock %} @@ -7,7 +7,8 @@

    {{hanchan.start|time:'H:i'}}: {{ hanchan.player_names }}

    {% for player in hanchan.player_list %} {{ player.user }}

    {{ player.user }}

    diff --git a/src/mahjong_ranking/templates/mahjong_ranking/hanchan_form.html b/src/mahjong_ranking/templates/mahjong_ranking/hanchan_form.html index 55ee14c..f4b2d5f 100644 --- a/src/mahjong_ranking/templates/mahjong_ranking/hanchan_form.html +++ b/src/mahjong_ranking/templates/mahjong_ranking/hanchan_form.html @@ -40,25 +40,42 @@ $('input[name$="_input_score"]').change(function() {recalculate_values(this);}); - + + {% with event_formset as form %}{% include "form.html" %} {% endwith %} - + + - + + - + + - + + diff --git a/src/mahjong_ranking/templates/mahjong_ranking/kyudanranking_list.html b/src/mahjong_ranking/templates/mahjong_ranking/kyudanranking_list.html index d6f97cf..072eed9 100644 --- a/src/mahjong_ranking/templates/mahjong_ranking/kyudanranking_list.html +++ b/src/mahjong_ranking/templates/mahjong_ranking/kyudanranking_list.html @@ -1,9 +1,11 @@ {% extends "mahjong_ranking/page.html" %} -{% load i18n %} +{% load i18n thumbnail %} {% block title %}{% trans 'Player List' %}{% endblock %} {% block teaser %}

    {% trans 'Player List' %}

    {% endblock %} +{% block redbox %}{% include 'mahjong_ranking/ladder_redbox.html' %}{% endblock %} + {% block content %}
    {% trans 'Player' %}{% trans 'Score' %}{% trans 'Score' %}{% trans 'Comment' %}
    {{ form.player1 }} {% if form.player1.errors %}{{ form.player1.errors }}{% endif %}{{ form.player1_input_score}} {% if form.player1_input_score.errors %}{{ form.player1_input_score.errors }}{% endif %}{{ form.player1_input_score}} + {{ form.instance.player1_comment}} + {% if form.player1_input_score.errors %}{{ form.player1_input_score.errors }}{% endif %} +
    {{ form.player2 }} {% if form.player2.errors %}{{ form.player2.errors }}{% endif %}{{ form.player2_input_score}} {% if form.player2_input_score.errors %}{{ form.player2_input_score.errors }}{% endif %}{{ form.player2_input_score}} + {{ form.instance.player2_comment }} + {% if form.player2_input_score.errors %}{{ form.player2_input_score.errors }}{% endif %} +
    {{ form.player3 }} {% if form.player3.errors %}{{ form.player3.errors }}{% endif %}{{ form.player3_input_score}} {% if form.player3_input_score.errors %}{{ form.player3_input_score.errors }}{% endif %}{{ form.player3_input_score}} + {{ form.instance.player3_comment }} + {% if form.player3_input_score.errors %}{{ form.player3_input_score.errors }}{% endif %} +
    {{ form.player4 }} {% if form.player4.errors %}{{ form.player4.errors }}{% endif %}{{ form.player4_input_score}} {% if form.player4_input_score.errors %}{{ form.player4_input_score.errors }}{% endif %}{{ form.player4_input_score}} + {{ form.instance.player4_comment }} + {% if form.player4_input_score.errors %}{{ form.player4_input_score.errors }}{% endif %} +
    @@ -40,11 +42,9 @@ {% for ranking in kyudanranking_list %} diff --git a/src/mahjong_ranking/templates/mahjong_ranking/ladder_redbox.html b/src/mahjong_ranking/templates/mahjong_ranking/ladder_redbox.html new file mode 100644 index 0000000..92ca77c --- /dev/null +++ b/src/mahjong_ranking/templates/mahjong_ranking/ladder_redbox.html @@ -0,0 +1,36 @@ +{% load i18n %} + +

    {% trans 'Latest Hanchans' %}

    + +

    {% trans 'Latest Events' %}

    + + +{% if season_list %} +

    {% trans 'Ladder Archive' %}

    + + + + +{% endif %} diff --git a/src/mahjong_ranking/templates/mahjong_ranking/player_dan_score.html b/src/mahjong_ranking/templates/mahjong_ranking/player_dan_score.html index e8c7dd8..5a0aa33 100644 --- a/src/mahjong_ranking/templates/mahjong_ranking/player_dan_score.html +++ b/src/mahjong_ranking/templates/mahjong_ranking/player_dan_score.html @@ -5,8 +5,7 @@ {% block teaser %}

    {% trans 'Dan Score for' %} {{membership.username}}

    {% endblock %} {% block score_list %} -
    -

    {% trans 'Hanchans that apply to the Dan Score' %}

    +

    {% trans 'Hanchans that apply to the Dan Score' %}

    - {% if ranking.user.avatar %} - - {% else %} - - {% endif %} + {{ ranking.user }} {% if user.is_authenticated %}{{ranking.user.last_name}} {{ranking.user.first_name}}{% else %}---{% endif %}
    @@ -39,7 +38,7 @@ {% endfor %} - + {% endfor %}
    {{hanchan.dan_points}}{{ hanchan.comment }}{{ hanchan.player_comment }} {% if perms.mahjong_ranking.delete_hanchan %} @@ -51,5 +50,4 @@
    -
    {% endblock %} \ No newline at end of file diff --git a/src/mahjong_ranking/templates/mahjong_ranking/player_invalid_score.html b/src/mahjong_ranking/templates/mahjong_ranking/player_invalid_score.html index 8b00680..7da226c 100644 --- a/src/mahjong_ranking/templates/mahjong_ranking/player_invalid_score.html +++ b/src/mahjong_ranking/templates/mahjong_ranking/player_invalid_score.html @@ -6,7 +6,7 @@ {% block teaser %}

    {% trans 'Unconfirmed Hanchans from' %} {{membership.username}}

    {% endblock %} {% block score_list %} -

    {% trans 'Invalid hanchans with' %} {{membership.username}}

    +

    {% trans 'Invalid hanchans with' %} {{membership.username}}

    diff --git a/src/mahjong_ranking/templates/mahjong_ranking/player_kyu_score.html b/src/mahjong_ranking/templates/mahjong_ranking/player_kyu_score.html index 3ed4f47..2444be7 100644 --- a/src/mahjong_ranking/templates/mahjong_ranking/player_kyu_score.html +++ b/src/mahjong_ranking/templates/mahjong_ranking/player_kyu_score.html @@ -6,7 +6,7 @@ {% block teaser %}

    {% trans 'Kyu Score for' %} {{membership.username}}

    {% endblock %} {% block score_list %} -

    {% trans 'Hanchans that apply to the Kyu Score' %}

    +

    {% trans 'Hanchans that apply to the Kyu Score' %}

    {% trans 'Event' %}
    diff --git a/src/mahjong_ranking/templates/mahjong_ranking/player_ladder_score.html b/src/mahjong_ranking/templates/mahjong_ranking/player_ladder_score.html index e13af5e..6185c38 100644 --- a/src/mahjong_ranking/templates/mahjong_ranking/player_ladder_score.html +++ b/src/mahjong_ranking/templates/mahjong_ranking/player_ladder_score.html @@ -5,7 +5,7 @@ {% block teaser %}

    {% trans 'Ladder Score for' %} {{membership.username}} / {{ season }}

    {% endblock %} {% block score_list %} -

    {% trans 'Hanchans that apply to the Ladder Score' %} - {{ season }}

    +

    {% trans 'Hanchans that apply to the Ladder Score' %} - {{ season }}

    diff --git a/src/mahjong_ranking/templates/mahjong_ranking/seasonranking_list.html b/src/mahjong_ranking/templates/mahjong_ranking/seasonranking_list.html index 91a559f..3ea58c4 100644 --- a/src/mahjong_ranking/templates/mahjong_ranking/seasonranking_list.html +++ b/src/mahjong_ranking/templates/mahjong_ranking/seasonranking_list.html @@ -1,11 +1,12 @@ {% extends "mahjong_ranking/page.html" %} -{% load i18n comments%} +{% load i18n comments humanize thumbnail %} +{% block title %}Mahjong Ladder - {{ season }}{% endblock %} {% block teaser %}

    Mahjong Ladder - {{ season }}

    -
      +
      • {% trans 'Start' %}: {{ season_start|date:'SHORT_DATE_FORMAT' }}
      • {% trans 'End' %}: {{ season_end|date:'SHORT_DATE_FORMAT' }}
      • {% trans 'Participants' %}: {{ seasonranking_list.count }}
      • @@ -13,6 +14,8 @@
    {% endblock %} +{% block redbox %}{% include 'mahjong_ranking/ladder_redbox.html' %}{% endblock %} + {% block content %}
    @@ -35,11 +38,13 @@ {% for player in seasonranking_list %} - + - - + + @@ -52,31 +57,4 @@ {% endfor %}
    {{player.placement}}. {{player.user}} {% if user.is_authenticated %}{{player.user.last_name}} {{player.user.first_name}}{% else %}---{% endif %}{{player.avg_placement|floatformat:2 }}{{player.avg_score|floatformat:0 }}{{player.avg_placement|ordinal }}{{player.avg_score|floatformat:0|intcomma }} {{player.hanchan_count}} {{player.good_hanchans}} {{player.won_hanchans}}
    -{% endblock %} - -{% block redbox %} -

    {% trans 'Latest Hanchans' %}

    -
      - {% for hanchan in latest_hanchan_list %} -
    • {{hanchan.event.name}} - {{hanchan.start|time:'H:i'}}: - {{hanchan.player_names}} -
    • - {% endfor %} -
    -

    {% trans 'Latest Events' %}

    -
      - {% for event in latest_event_list %} -
    • {{event.name}}
    • - {% endfor %} -
    -

    {% trans 'Ladder Archive' %}

    -
    - - -
    -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/mahjong_ranking/tests.py b/src/mahjong_ranking/tests.py index 501deb7..75f2f59 100644 --- a/src/mahjong_ranking/tests.py +++ b/src/mahjong_ranking/tests.py @@ -9,6 +9,7 @@ from django.test import TestCase class SimpleTest(TestCase): + def test_basic_addition(self): """ Tests that 1 + 1 always equals 2. diff --git a/src/mahjong_ranking/urls.py b/src/mahjong_ranking/urls.py index ca74e39..85e6e1f 100644 --- a/src/mahjong_ranking/urls.py +++ b/src/mahjong_ranking/urls.py @@ -13,19 +13,33 @@ import views urlpatterns = patterns( '', - # url('^$', RedirectView.as_view(pattern_name='mahjong-ladder', permanent=True)), - url('^$',views.SeasonRankingList.as_view()), - url(r'^event/(?P[\d]+)/mahjong/$', views.EventHanchanList.as_view(), name="event-hanchan-list"), - url(r'^event/(?P[\d]+)/add-hanchan/$', views.HanchanForm.as_view(), name="add-hanchan-form"), - url(r'^event/(?P[\d]+)/mahjong-ranking/$', views.EventRankingList.as_view(), name="event-ranking"), - url(r'^hanchan/(?P[\d]+)/edit/$', views.HanchanForm.as_view(), name="edit-hanchan"), - url(r'^hanchan/(?P[\d]+)/delete/$', views.DeleteHanchan.as_view(), name="delete-hanchan"), - url(r'^mahjong-ladder/$', views.SeasonRankingList.as_view(), name="mahjong-ladder"), - url(r'^mahjong-ladder/(?P[\d]+)/$', views.SeasonRankingList.as_view(), name="mahjong-ladder"), - url(r'^player/(?P[\-\.\d\w]+)/dan/$', views.PlayerDanScore.as_view(), name="player-dan-score"), - url(r'^player/(?P[\-\.\d\w]+)/invalid/$', views.PlayerInvalidScore.as_view(), name="player-invalid-score"), - url(r'^player/(?P[\-\.\d\w]+)/kyu/$', views.PlayerKyuScore.as_view(), name="player-kyu-score"), - url(r'^player/(?P[\-\.\d\w]+)/ladder/$', views.PlayerLadderScore.as_view(), name="player-ladder-score"), - url(r'^mahjong/$', views.KyuDanRankingList.as_view(), name="kyudanranking-list"), - url(r'^mahjong/(?P[\+\-\w]+)/$', views.KyuDanRankingList.as_view(), name="kyudanranking-list"), + url(r'^$', RedirectView.as_view( + url='/ranking/mahjong-ladder/', permanent=True) + ), + url(r'^event/(?P[\d]+)/mahjong/$', + views.EventHanchanList.as_view(), name="event-hanchan-list"), + url(r'^event/(?P[\d]+)/add-hanchan/$', + views.HanchanForm.as_view(), name="add-hanchan-form"), + url(r'^event/(?P[\d]+)/mahjong-ranking/$', + views.EventRankingList.as_view(), name="event-ranking"), + url(r'^hanchan/(?P[\d]+)/edit/$', + views.HanchanForm.as_view(), name="edit-hanchan"), + url(r'^hanchan/(?P[\d]+)/delete/$', + views.DeleteHanchan.as_view(), name="delete-hanchan"), + url(r'^mahjong-ladder/$', views.SeasonRankingList.as_view(), + name="mahjong-ladder"), + url(r'^mahjong-ladder/(?P[\d]+)/$', + views.SeasonRankingList.as_view(), name="mahjong-ladder"), + url(r'^player/(?P[\-\.\d\w]+)/dan/$', + views.PlayerDanScore.as_view(), name="player-dan-score"), + url(r'^player/(?P[\-\.\d\w]+)/invalid/$', + views.PlayerInvalidScore.as_view(), name="player-invalid-score"), + url(r'^player/(?P[\-\.\d\w]+)/kyu/$', + views.PlayerKyuScore.as_view(), name="player-kyu-score"), + url(r'^player/(?P[\-\.\d\w]+)/ladder/$', + views.PlayerLadderScore.as_view(), name="player-ladder-score"), + url(r'^mahjong/$', views.KyuDanRankingList.as_view(), + name="kyudanranking-list"), + url(r'^mahjong/(?P[\+\-\w]+)/$', + views.KyuDanRankingList.as_view(), name="kyudanranking-list"), ) diff --git a/src/mahjong_ranking/views.py b/src/mahjong_ranking/views.py index b21cf69..c665a31 100644 --- a/src/mahjong_ranking/views.py +++ b/src/mahjong_ranking/views.py @@ -6,7 +6,7 @@ from django.core.urlresolvers import reverse import django.forms import django.http from django.contrib.messages.views import SuccessMessageMixin -from django.utils.translation import gettext as _ +from django.utils.translation import ugettext as _ from django.views import generic from events.models import Event @@ -21,14 +21,15 @@ kyu_dan_order = { '-hanchan_count': ('-hanchan_count',), '+rank': ('-kyu', 'dan'), '-rank': ('-dan', 'kyu'), - '+score': ('dan_points', 'kyu_points'), - '-score': ('-dan_points', '-kyu_points'), + '+score': ('dan_points', '-dan', 'kyu_points'), + '-score': ('-dan_points', 'dan', '-kyu_points'), '+username': ('user__username',), '-username': ('-user__username',) } -class DeleteHanchan(EventDetailMixin, 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. @@ -43,7 +44,8 @@ class DeleteHanchan(EventDetailMixin, PermissionRequiredMixin, generic.DeleteVie kwargs={'event': self.object.event.pk}) -class HanchanForm(SuccessMessageMixin, EventDetailMixin, PermissionRequiredMixin, generic.UpdateView): +class HanchanForm(SuccessMessageMixin, EventDetailMixin, + PermissionRequiredMixin, generic.UpdateView): """ Ein Formular um neue Hanchans anzulegen, bzw. eine bestehende zu bearbeitsen @@ -68,7 +70,8 @@ class HanchanForm(SuccessMessageMixin, EventDetailMixin, PermissionRequiredMixin return forms.HanchanForm def get_object(self, queryset=None): - if self.kwargs.get('hanchan') and self.request.user.has_perm('mahjong_ranking.change_hanchan'): + if self.kwargs.get('hanchan') and self.request.user.has_perm( + 'mahjong_ranking.change_hanchan'): hanchan = models.Hanchan.objects.get(id=self.kwargs['hanchan']) self.event = hanchan.event elif self.kwargs.get('event'): @@ -93,7 +96,9 @@ class HanchanForm(SuccessMessageMixin, EventDetailMixin, PermissionRequiredMixin if self.kwargs.get('hanchan'): return _('%s has been updated successfully.') % self.object else: - return _('%s has been added successfully. You can now add a new one.') % self.object + return _( + '%s has been added successfully. You can now add a new ' + 'one.') % self.object class EventHanchanList(EventDetailMixin, generic.ListView): @@ -129,7 +134,26 @@ class EventRankingList(EventDetailMixin, generic.ListView): raise django.http.Http404(_('Event does not exist')) -class KyuDanRankingList(generic.ListView): +class MahjongMixin(object): + + def get_context_data(self, **kwargs): + context = super(MahjongMixin, self).get_context_data(**kwargs) + try: + context['season'] = self.season + context['season_start'] = date(year=self.season, month=1, day=1) + context['season_end'] = date(year=self.season, month=12, day=31) + context['season_list'] = models.SeasonRanking.objects.season_list + except AttributeError: + pass + context[ + 'latest_hanchan_list'] = \ + models.Hanchan.objects.confirmed_hanchans()[ + :3] + context['latest_event_list'] = Event.objects.latest_events(num=3) + return context + + +class KyuDanRankingList(MahjongMixin, generic.ListView): """ Anzeige aller Spiele mit ihrem Kyu bzw Dan Grad. """ @@ -145,30 +169,22 @@ class KyuDanRankingList(generic.ListView): def get_queryset(self): queryset = models.KyuDanRanking.objects.all().order_by(*self.order_by) - return queryset.select_related('user__membership') + return queryset.select_related() -class SeasonRankingList(generic.ListView): +class SeasonRankingList(MahjongMixin, generic.ListView): model = models.SeasonRanking paginate_by = 25 season = None def get_queryset(self): self.season = int(self.kwargs.get('season', date.today().year)) - queryset = self.model.objects.filter(season=self.season, - placement__isnull=False) + queryset = self.model.objects.filter( + season=self.season, + placement__isnull=False + ) return queryset.select_related() - def get_context_data(self, **kwargs): - context = super(SeasonRankingList, self).get_context_data(**kwargs) - context['season'] = self.season - context['season_start'] = date(year=self.season, month=1, day=1) - context['season_end'] = date(year=self.season, month=12, day=31) - context['season_list'] = models.SeasonRanking.objects.season_list - context['latest_hanchan_list'] = models.Hanchan.objects.confirmed_hanchans()[:3] - context['latest_event_list'] = Event.objects.archive()[:3] - return context - class PlayerScore(LoginRequiredMixin, generic.ListView): paginate_by = 25 @@ -176,9 +192,12 @@ class PlayerScore(LoginRequiredMixin, generic.ListView): def get(self, request, *args, **kwargs): user_model = auth.get_user_model() try: - self.user = user_model.objects.get(username=self.kwargs.get('username')) + self.user = user_model.objects.get( + username=self.kwargs.get('username')) except user_model.DoesNotExist: - raise django.http.Http404(_("No user found matching the name {}").format(self.kwargs.get('username'))) + raise django.http.Http404( + _("No user found matching the name {}").format( + self.kwargs.get('username'))) return super(PlayerScore, self).get(request, *args, **kwargs) def get_context_data(self, **kwargs): @@ -235,6 +254,7 @@ class PlayerLadderScore(PlayerScore): self.season = int(self.request.GET.get('season')) except: self.season = date.today().year - hanchan_list = models.Hanchan.objects.season_hanchans(user=self.user, season=self.season) + hanchan_list = models.Hanchan.objects.season_hanchans( + user=self.user, season=self.season) print hanchan_list return hanchan_list diff --git a/src/maistar_ranking/forms.py b/src/maistar_ranking/forms.py index 7c131e7..1e7a1c6 100644 --- a/src/maistar_ranking/forms.py +++ b/src/maistar_ranking/forms.py @@ -27,7 +27,7 @@ class GameForm(forms.ModelForm): cleaned_data = super(GameForm, self).clean() players_in_game = set() for player_nr in ( - 'player1', 'player2', 'player3', 'player4', 'player5', 'player6'): + 'player1', 'player2', 'player3', 'player4', 'player5', 'player6'): current_player = cleaned_data.get(player_nr) if current_player and current_player in players_in_game: msg = _("%s may only participate once." % current_player) diff --git a/src/maistar_ranking/migrations/0001_initial.py b/src/maistar_ranking/migrations/0001_initial.py index 7b375c1..79b5f59 100644 --- a/src/maistar_ranking/migrations/0001_initial.py +++ b/src/maistar_ranking/migrations/0001_initial.py @@ -16,7 +16,8 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Game', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('id', models.AutoField(verbose_name='ID', + serialize=False, auto_created=True, primary_key=True)), ('comment', models.TextField(verbose_name='Anmerkung', blank=True)), ('player1_score', models.SmallIntegerField(verbose_name='Punkte')), ('player1_placement', models.PositiveSmallIntegerField(editable=False)), @@ -30,25 +31,36 @@ class Migration(migrations.Migration): ('player5_placement', models.PositiveSmallIntegerField(editable=False)), ('player6_score', models.SmallIntegerField(verbose_name='Punkte')), ('player6_placement', models.PositiveSmallIntegerField(editable=False)), - ('confirmed', models.BooleanField(default=True, help_text='das Spiel z\xe4hlt nur wenn es best\xe4tigt wurde', verbose_name='Wurde best\xe4tigt')), + ('confirmed', models.BooleanField( + default=True, help_text='das Spiel z\xe4hlt nur wenn es best\xe4tigt wurde', verbose_name='Wurde best\xe4tigt')), ('player_names', models.CharField(max_length=255, editable=False)), - ('season', models.PositiveSmallIntegerField(verbose_name='Saison', editable=False, db_index=True)), - ('event', models.ForeignKey(related_name='maistargame_set', to='events.Event')), - ('player1', models.ForeignKey(related_name='+', verbose_name='Spieler 1', to=settings.AUTH_USER_MODEL)), - ('player2', models.ForeignKey(related_name='+', verbose_name='Spieler 2', to=settings.AUTH_USER_MODEL)), - ('player3', models.ForeignKey(related_name='+', verbose_name='Spieler 3', to=settings.AUTH_USER_MODEL)), - ('player4', models.ForeignKey(related_name='+', verbose_name='Spieler 4', to=settings.AUTH_USER_MODEL)), - ('player5', models.ForeignKey(related_name='+', verbose_name='Spieler 5', to=settings.AUTH_USER_MODEL)), - ('player6', models.ForeignKey(related_name='+', verbose_name='Spieler 6', to=settings.AUTH_USER_MODEL)), + ('season', models.PositiveSmallIntegerField( + verbose_name='Saison', editable=False, db_index=True)), + ('event', models.ForeignKey( + related_name='maistargame_set', to='events.Event')), + ('player1', models.ForeignKey(related_name='+', + verbose_name='Spieler 1', to=settings.AUTH_USER_MODEL)), + ('player2', models.ForeignKey(related_name='+', + verbose_name='Spieler 2', to=settings.AUTH_USER_MODEL)), + ('player3', models.ForeignKey(related_name='+', + verbose_name='Spieler 3', to=settings.AUTH_USER_MODEL)), + ('player4', models.ForeignKey(related_name='+', + verbose_name='Spieler 4', to=settings.AUTH_USER_MODEL)), + ('player5', models.ForeignKey(related_name='+', + verbose_name='Spieler 5', to=settings.AUTH_USER_MODEL)), + ('player6', models.ForeignKey(related_name='+', + verbose_name='Spieler 6', to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( name='Ranking', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('id', models.AutoField(verbose_name='ID', + serialize=False, auto_created=True, primary_key=True)), ('season', models.PositiveSmallIntegerField(verbose_name='Saison')), ('placement', models.PositiveIntegerField(null=True, blank=True)), - ('avg_placement', models.PositiveSmallIntegerField(null=True, blank=True)), + ('avg_placement', models.PositiveSmallIntegerField( + null=True, blank=True)), ('avg_score', models.SmallIntegerField(null=True, blank=True)), ('games_count', models.PositiveSmallIntegerField(default=0)), ('games_good', models.PositiveSmallIntegerField(default=0)), diff --git a/src/maistar_ranking/migrations/0002_auto_20150823_2232.py b/src/maistar_ranking/migrations/0002_auto_20150823_2232.py new file mode 100644 index 0000000..ab3e729 --- /dev/null +++ b/src/maistar_ranking/migrations/0002_auto_20150823_2232.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('maistar_ranking', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='game', + name='comment', + field=models.TextField(verbose_name='Kommentar', blank=True), + ), + ] diff --git a/src/maistar_ranking/migrations/0003_auto_20150901_2204.py b/src/maistar_ranking/migrations/0003_auto_20150901_2204.py new file mode 100644 index 0000000..5266a05 --- /dev/null +++ b/src/maistar_ranking/migrations/0003_auto_20150901_2204.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('maistar_ranking', '0002_auto_20150823_2232'), + ] + + operations = [ + migrations.AlterField( + model_name='game', + name='comment', + field=models.TextField(verbose_name='Anmerkung', blank=True), + ), + ] diff --git a/src/maistar_ranking/models.py b/src/maistar_ranking/models.py index 3659a96..2503133 100644 --- a/src/maistar_ranking/models.py +++ b/src/maistar_ranking/models.py @@ -87,9 +87,6 @@ class Game(models.Model): for nr in range(1, 7): self._player_list.append({ 'user': getattr(self, 'player%d' % nr), - 'membership': Membership.objects.get( - user=getattr(self, 'player%d' % nr) - ), 'score': getattr(self, 'player%d_score' % nr), 'placement': getattr(self, 'player%d_placement' % nr), }) diff --git a/src/maistar_ranking/settings.py b/src/maistar_ranking/settings.py index 16966b1..e974f99 100644 --- a/src/maistar_ranking/settings.py +++ b/src/maistar_ranking/settings.py @@ -20,5 +20,3 @@ MAISTAR_GAMES_FOR_LADDERRANKING = getattr( MAISTAR_SEASON_START = getattr( settings, 'MAISTAR_SEASON_START', date(year=1, month=8, day=1) ) - - diff --git a/src/maistar_ranking/templates/maistar_ranking/game_list.html b/src/maistar_ranking/templates/maistar_ranking/game_list.html index 70f0aba..57274f3 100644 --- a/src/maistar_ranking/templates/maistar_ranking/game_list.html +++ b/src/maistar_ranking/templates/maistar_ranking/game_list.html @@ -1,25 +1,25 @@ {% extends "events/event_detail.html" %} -{% load i18n humanize %} +{% load i18n humanize thumbnail %} {% block title %}{% trans 'Mai-Star Games' %} - {{ event.name }}{% endblock %} {% block maincontent %}

    {% trans 'Played Mai-Star Games' %}

    -

     

    + {% for game in game_list %} {% empty %}

    {% trans 'No Mai-Star games have been added to this event yet.'%}

    diff --git a/src/maistar_ranking/templates/maistar_ranking/player_game_list.html b/src/maistar_ranking/templates/maistar_ranking/player_game_list.html index 6b9e3ba..0498b16 100644 --- a/src/maistar_ranking/templates/maistar_ranking/player_game_list.html +++ b/src/maistar_ranking/templates/maistar_ranking/player_game_list.html @@ -6,8 +6,7 @@ {% block teaser %}

    {% trans 'Mai-Star Games' %}: {{membership.username}}
    {% trans 'Season' %} {{season}}

    {% endblock %} {% block score_list %} -
    -

    {% trans 'Mai-Star Games with' %} {{ membership.username }} - {{season}}

    +

    {% trans 'Mai-Star Games with' %} {{ membership.username }} - {{season}}

    @@ -50,6 +49,4 @@ {% endfor %}
    - -
    {% endblock %} \ No newline at end of file diff --git a/src/maistar_ranking/templates/maistar_ranking/ranking_list.html b/src/maistar_ranking/templates/maistar_ranking/ranking_list.html index 4107649..3f4f1c8 100644 --- a/src/maistar_ranking/templates/maistar_ranking/ranking_list.html +++ b/src/maistar_ranking/templates/maistar_ranking/ranking_list.html @@ -1,10 +1,9 @@ {% extends "base.html" %} -{% load i18n comments humanize %} +{% load i18n comments humanize thumbnail %} {% block teaser %}

    {% trans 'Mai-Star Ranking' %} - {% trans 'Season' %} {{season}}

    {% endblock %} -{% block maincontent %} -
    +{% block content %} @@ -27,7 +26,9 @@ {% with player.user.get_profile as profile %} - + @@ -45,7 +46,6 @@ {% endfor %}
    {{ player.placement|ordinal }}{{ player.user }}{{ player.user }} {{player.user}} {% if user.is_authenticated %}{{profile.last_name}} {{profile.first_name}}{% else %} ---{% endif %} {{player.avg_placement|floatformat:0 }}
    -
    {% endblock %} {% block redbox %} diff --git a/src/maistar_ranking/urls.py b/src/maistar_ranking/urls.py index 451577e..efe061e 100644 --- a/src/maistar_ranking/urls.py +++ b/src/maistar_ranking/urls.py @@ -13,12 +13,19 @@ from .views import DeleteGame, ListGames, ListPlayerGames, \ urlpatterns = patterns( 'maistar_ranking.views', url('^$', ListRankings.as_view()), - url(r'^event/(?P[\d]+)/maistar/$', ListGames.as_view(), name="maistar-game-list"), - url(r'^event/(?P[\d]+)/maistar/add/$', GameForm.as_view(), name="maistar-add-game"), - url(r'^maistar/(?P[\d]+)/edit/$', GameForm.as_view(), name="maistar-edit-game"), - url(r'^maistar/(?P[\d]+)/delete/$', DeleteGame.as_view(), name="maistar-delete-game"), + url(r'^event/(?P[\d]+)/maistar/$', + ListGames.as_view(), name="maistar-game-list"), + url(r'^event/(?P[\d]+)/maistar/add/$', + GameForm.as_view(), name="maistar-add-game"), + url(r'^maistar/(?P[\d]+)/edit/$', + GameForm.as_view(), name="maistar-edit-game"), + url(r'^maistar/(?P[\d]+)/delete/$', + DeleteGame.as_view(), name="maistar-delete-game"), url(r'^maistar/$', ListRankings.as_view(), name="maistar-ranking"), - url(r'^maistar/(?P[\d]+)/$', ListRankings.as_view(), name="maistar-ranking"), - url(r'^player/(?P[\-\.\d\w]+)/maistar/$', ListPlayerGames.as_view(), name="maistar-player-games"), - url(r'^player/(?P[\-\.\d\w]+)/maistar/(?P[\d]+)/$', ListPlayerGames.as_view(), name="maistar-player-games"), + url(r'^maistar/(?P[\d]+)/$', + ListRankings.as_view(), name="maistar-ranking"), + url(r'^player/(?P[\-\.\d\w]+)/maistar/$', + ListPlayerGames.as_view(), name="maistar-player-games"), + url(r'^player/(?P[\-\.\d\w]+)/maistar/(?P[\d]+)/$', + ListPlayerGames.as_view(), name="maistar-player-games"), ) diff --git a/src/maistar_ranking/views.py b/src/maistar_ranking/views.py index 549182f..71ba7c3 100644 --- a/src/maistar_ranking/views.py +++ b/src/maistar_ranking/views.py @@ -84,7 +84,8 @@ class ListPlayerGames(PlayerScore): def get_context_data(self, **kwargs): context = super(ListPlayerGames, self).get_context_data() context['membership'] = self.user - context['season_list'] = models.Ranking.objects.filter(user=self.user).values_list('season', flat=True) + context['season_list'] = models.Ranking.objects.filter( + user=self.user).values_list('season', flat=True) context['season'] = self.season return context @@ -116,6 +117,7 @@ class ListRankings(generic.ListView): context['is_archive'] = self.is_archive context['season'] = self.season context['season_list'] = models.Ranking.objects.get_seasons() - context['latest_games'] = models.Game.objects.filter(confirmed=True)[:3] - context['latest_events'] = Event.objects.archive()[:3] + context['latest_games'] = models.Game.objects.filter(confirmed=True)[ + :3] + context['latest_events'] = Event.objects.latest_events(num=3) return context diff --git a/src/manage.py b/src/manage.py index 41be209..1d37998 100755 --- a/src/manage.py +++ b/src/manage.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +# -*- encoding: utf-8 -*- + import os import sys @@ -8,4 +10,4 @@ if __name__ == "__main__": from django.core.management import execute_from_command_line - execute_from_command_line(sys.argv) \ No newline at end of file + execute_from_command_line(sys.argv) diff --git a/src/membership/admin.py b/src/membership/admin.py index 19b0d3b..14eb963 100644 --- a/src/membership/admin.py +++ b/src/membership/admin.py @@ -7,9 +7,8 @@ Created on 19.09.2011 from django.contrib import admin from django.contrib.auth.models import Group from django.contrib.auth.admin import UserAdmin, GroupAdmin -from django.contrib.auth.forms import ReadOnlyPasswordHashField +# from django.contrib.auth.forms import ReadOnlyPasswordHashField from django.utils.translation import ugettext as _ -from imagekit.admin import AdminThumbnail from membership.models import Membership, ActivationRequest @@ -30,10 +29,12 @@ def cleanup_activation(modeladmin, request, queryset): activation.user.delete() -cleanup_activation.short_description = _("Cleanup selected Activation Requests") +cleanup_activation.short_description = _( + "Cleanup selected Activation Requests") class ProxyGroup(Group): + class Meta: proxy = True verbose_name = _('Group') @@ -41,10 +42,10 @@ class ProxyGroup(Group): class MembershipAdmin(UserAdmin): - admin_thumbnail = AdminThumbnail(image_field='thumbnail') + # admin_thumbnail = AdminThumbnail(image_field='thumbnail') list_filter = ('membership', 'confirmed') list_display = ( - 'admin_thumbnail', + 'avatar', 'username', 'first_name', 'last_name', @@ -55,11 +56,11 @@ class MembershipAdmin(UserAdmin): list_editable = ('confirmed', 'paid_until',) list_display_links = ('username',) fieldsets = ( - (None, {'fields': (('username', 'password'), 'gender', + (None, {'fields': (('username', 'password'), 'gender', 'avatar', ('first_name', 'last_name'), ('email', 'website'))}), (_('Membership'), {'classes': ('collapse',), 'fields': (('membership', 'confirmed'), 'birthday', 'telephone', - 'street_name', ('post_code', 'city'))}), + 'street_name', ('post_code', 'city'))}), (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}), (_('Important dates'), {'fields': ('last_login', 'date_joined')}), diff --git a/src/membership/forms.py b/src/membership/forms.py index 1365223..3d18f26 100644 --- a/src/membership/forms.py +++ b/src/membership/forms.py @@ -23,33 +23,32 @@ class MembershipForm(forms.ModelForm): fields = ( 'username', 'gender', 'first_name', 'last_name', 'email', 'avatar', 'website', 'membership', 'birthday', 'telephone', 'street_name', - 'post_code', 'city', 'comment' + 'post_code', 'city' ) 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']: + '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']: + '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']: + 'post_code']: raise forms.ValidationError(_('For your membership, we need this. \ Please fill out this field yet.')) return self.cleaned_data['post_code'] @@ -145,6 +144,7 @@ class RegistrationForm(forms.ModelForm): user.is_active = False if commit: user.save() - activation = models.ActivationRequest.objects.create_pending_registration(user) # @IgnorePep8 + activation = models.ActivationRequest.objects.create_pending_registration( + user) # @IgnorePep8 self.send_email(activation) return user diff --git a/src/membership/locale/de/LC_MESSAGES/django.mo b/src/membership/locale/de/LC_MESSAGES/django.mo index dc378a7b0102d4abc740fc37a8e66bbecb2d6e63..5987ce821a1c4bbd96eddea4c8d27ab1e8579909 100644 GIT binary patch delta 683 zcmXZZPbkA-9LMqJXBhKm^P9i(CuOokksXk1CFHUTg`?Pk94t$T`W=*ua@j#SxriIu zNx4WQM>}$Gm5W4k^#1t$YVGwr-#y>&^L@T6#P8#muO-fnF{aTrrU45Wz;~?1Zw#Ro zHl`14ti%NB+zbYB4t0LTi)XNo{jTRBma{*>3OvJUybK%XfALG8hCs63Eu2C>`$hEO z5~@Jji?3rX`z(@b_E7mpScRvk#;#BgxJ6z3?8RSD5BaEf+{7OOD(GVjO0cmMyKoF+ zsKRNCVFvZ$V^pJOn8XWIqo3aIMbxB98{P9kRHJRE26`P9IuJ)CCQt>Zup3jz$7K1s z#siGvWRo$ixP}wBkNS(AP@^rPCYNY->r9{;UPVn}3#ZU=Sj@6`z;TSUxEC&>8rwh> z-a#dvd%s^HcbG?S|Av}S5p|I-;=VM5%Im;(>_H24mIvnN%kBb!k^X_9&cWf)p}~AK U^kwB*>;)^3+qReUZ}z7D4{D@Cp8x;= delta 699 zcmXZZPbfrD6vy#9=CAR09>%{$vlumrH2IsRNffJ5qQ+uYEM_?}3$pNPvY{-*M%gMW zsfn_{LN+!E$-;uAjfIVDsU`*MrxT|RQ?gxU!X(jlJpDFWq zfd`ntxmII3aSbPNAN7e|P*>}3b8aq+sxymfU=?)}n>dB`4i9rYJYfct?aqM()Th`$ z72ZN6p1bd_k+aOR8-Jp1$lu`{6hTeeh05#27!IKawU@>!IsZ*KJldBUj;B&%10$70 eoFYP_&kO3Mvm)DfkBf>qX=M diff --git a/src/membership/locale/de/LC_MESSAGES/django.po b/src/membership/locale/de/LC_MESSAGES/django.po index 121d3ce..9984462 100644 --- a/src/membership/locale/de/LC_MESSAGES/django.po +++ b/src/membership/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: kasu.membership\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-08-16 11:34+0200\n" -"PO-Revision-Date: 2015-08-16 11:37+0200\n" +"PO-Revision-Date: 2015-09-03 18:31+0200\n" "Last-Translator: Christian Berg \n" "Language-Team: Kasu \n" "Language: de\n" @@ -356,10 +356,10 @@ msgid "" " confirm your email address, otherwise your can't login.\n" " " msgstr "" -"

    Nach dem du deine Daten eingegeben hast, wirst du eine E-Mail zur " -"Bestätigung bekommen.

    \n" -"

    Bitte klicke auf den Link in dieser E-Mail zur Verifizierung, erst dann " -"ist die Anmeldung möglich.

    " +"Nach dem du deine Daten eingegeben hast, wirst du eine E-Mail zur " +"Bestätigung bekommen.\n" +"Bitte klicke auf den Link in dieser E-Mail zur Verifizierung, erst dann ist " +"die Anmeldung möglich." #: templates/membership/register_form.html:30 msgid "reset" diff --git a/src/membership/management/commands/cleanup-registration.py b/src/membership/management/commands/cleanup-registration.py index 48c7848..fd24633 100644 --- a/src/membership/management/commands/cleanup-registration.py +++ b/src/membership/management/commands/cleanup-registration.py @@ -11,4 +11,3 @@ class Command(BaseCommand): def handle(self, *args, **options): for activation in ActivationRequest.objects.expired(): activation.user.delete() - diff --git a/src/membership/migrations/0001_initial.py b/src/membership/migrations/0001_initial.py index 73201e8..816a3ff 100644 --- a/src/membership/migrations/0001_initial.py +++ b/src/membership/migrations/0001_initial.py @@ -20,33 +20,57 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Membership', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(null=True, verbose_name='last login', blank=True)), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, max_length=30, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')], help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, verbose_name='username')), - ('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)), - ('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)), - ('email', models.EmailField(max_length=254, verbose_name='email address', blank=True)), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('gender', models.CharField(max_length=1, verbose_name='Geschlecht', choices=[(b'm', 'M\xe4nnlich'), (b'f', 'Weiblich')])), + ('id', models.AutoField(verbose_name='ID', + serialize=False, auto_created=True, primary_key=True)), + ('password', models.CharField( + max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField( + null=True, verbose_name='last login', blank=True)), + ('is_superuser', models.BooleanField(default=False, + help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, max_length=30, validators=[django.core.validators.RegexValidator( + '^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')], help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, verbose_name='username')), + ('first_name', models.CharField(max_length=30, + verbose_name='first name', blank=True)), + ('last_name', models.CharField(max_length=30, + verbose_name='last name', blank=True)), + ('email', models.EmailField(max_length=254, + verbose_name='email address', blank=True)), + ('is_staff', models.BooleanField(default=False, + help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField( + default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField( + default=django.utils.timezone.now, verbose_name='date joined')), + ('gender', models.CharField(max_length=1, verbose_name='Geschlecht', choices=[ + (b'm', 'M\xe4nnlich'), (b'f', 'Weiblich')])), ('website', models.URLField(blank=True)), - ('avatar', models.ImageField(storage=utils.OverwriteStorage(), null=True, upload_to=membership.models.get_upload_path, blank=True)), - ('membership', models.BooleanField(default=False, help_text='Ja, ich bin mit den Statuen einverstanden und m\xf6chte Mitglied werden.', verbose_name='Mitgliedschaft')), - ('birthday', models.DateField(null=True, verbose_name='Geburtstag', blank=True)), - ('telephone', models.CharField(max_length=30, null=True, verbose_name='Telefon', blank=True)), - ('street_name', models.CharField(max_length=75, null=True, verbose_name='Adresse', blank=True)), - ('post_code', models.PositiveSmallIntegerField(null=True, verbose_name='Postleitzahl', blank=True)), - ('city', models.CharField(max_length=75, null=True, verbose_name='Ort', blank=True)), - ('deposit', models.PositiveSmallIntegerField(default=0, editable=False)), + ('avatar', models.ImageField(storage=utils.OverwriteStorage( + ), null=True, upload_to=membership.models.get_upload_path, blank=True)), + ('membership', models.BooleanField(default=False, + help_text='Ja, ich bin mit den Statuen einverstanden und m\xf6chte Mitglied werden.', verbose_name='Mitgliedschaft')), + ('birthday', models.DateField(null=True, + verbose_name='Geburtstag', blank=True)), + ('telephone', models.CharField(max_length=30, + null=True, verbose_name='Telefon', blank=True)), + ('street_name', models.CharField(max_length=75, + null=True, verbose_name='Adresse', blank=True)), + ('post_code', models.PositiveSmallIntegerField( + null=True, verbose_name='Postleitzahl', blank=True)), + ('city', models.CharField(max_length=75, + null=True, verbose_name='Ort', blank=True)), + ('deposit', models.PositiveSmallIntegerField( + default=0, editable=False)), ('registration_date', models.DateField(auto_now_add=True)), - ('paid_until', models.DateField(null=True, verbose_name='Bezahlt bis', blank=True)), - ('confirmed', models.BooleanField(default=False, help_text='Diese Person hat ihre Mitgliedschaft bezahlt', verbose_name='Best\xe4tigt')), + ('paid_until', models.DateField(null=True, + verbose_name='Bezahlt bis', blank=True)), + ('confirmed', models.BooleanField(default=False, + help_text='Diese Person hat ihre Mitgliedschaft bezahlt', verbose_name='Best\xe4tigt')), ('comment', models.TextField(blank=True)), - ('groups', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups')), - ('user_permissions', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Permission', blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions')), + ('groups', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, + help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Permission', + blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions')), ], options={ 'ordering': ('last_name', 'first_name'), @@ -61,9 +85,12 @@ class Migration(migrations.Migration): migrations.CreateModel( name='ActivationRequest', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('activation_key', models.CharField(max_length=40, verbose_name='Aktivierungsschl\xfcssel')), - ('user', models.OneToOneField(verbose_name='Benutzer', to=settings.AUTH_USER_MODEL)), + ('id', models.AutoField(verbose_name='ID', + serialize=False, auto_created=True, primary_key=True)), + ('activation_key', models.CharField( + max_length=40, verbose_name='Aktivierungsschl\xfcssel')), + ('user', models.OneToOneField( + verbose_name='Benutzer', to=settings.AUTH_USER_MODEL)), ], options={ 'verbose_name': 'Ausstehende Aktivierung', diff --git a/src/membership/migrations/0003_auto_20150820_2122.py b/src/membership/migrations/0003_auto_20150820_2122.py index d6eddaf..8caf4c2 100644 --- a/src/membership/migrations/0003_auto_20150820_2122.py +++ b/src/membership/migrations/0003_auto_20150820_2122.py @@ -13,6 +13,7 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( name='membership', - options={'ordering': ('username',), 'verbose_name': 'Mitgliedschaft', 'verbose_name_plural': 'Mitgliedschaften'}, + options={'ordering': ('username',), 'verbose_name': 'Mitgliedschaft', + 'verbose_name_plural': 'Mitgliedschaften'}, ), ] diff --git a/src/membership/migrations/0004_remove_membership_comment.py b/src/membership/migrations/0004_remove_membership_comment.py new file mode 100644 index 0000000..a821526 --- /dev/null +++ b/src/membership/migrations/0004_remove_membership_comment.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('membership', '0003_auto_20150820_2122'), + ] + + operations = [ + migrations.RemoveField( + model_name='membership', + name='comment', + ), + ] diff --git a/src/membership/migrations/0005_auto_20150901_2204.py b/src/membership/migrations/0005_auto_20150901_2204.py new file mode 100644 index 0000000..3ac89aa --- /dev/null +++ b/src/membership/migrations/0005_auto_20150901_2204.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import membership.models +import utils +import easy_thumbnails.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('membership', '0004_remove_membership_comment'), + ] + + operations = [ + migrations.AlterField( + model_name='membership', + name='avatar', + field=easy_thumbnails.fields.ThumbnailerImageField(storage=utils.OverwriteStorage( + ), null=True, upload_to=membership.models.get_upload_path, blank=True), + ), + ] diff --git a/src/membership/models.py b/src/membership/models.py index d5a4e67..e39c348 100644 --- a/src/membership/models.py +++ b/src/membership/models.py @@ -11,10 +11,8 @@ from django.contrib.auth.models import AbstractUser from django.core.urlresolvers import reverse from django.db import models from django.utils.translation import ugettext as _ -from imagekit.models import ImageSpecField -from imagekit.processors import SmartResize - from . import PAID_MEMBERSHIP_GROUP +from easy_thumbnails.fields import ThumbnailerImageField from utils import OverwriteStorage @@ -152,7 +150,7 @@ class Membership(AbstractUser): # last_name = models.CharField(_("Last Name"), max_length=30) # email = models.EmailField(_('Email'), unique=True) website = models.URLField(blank=True) - avatar = models.ImageField( + avatar = ThumbnailerImageField( upload_to=get_upload_path, storage=OverwriteStorage(), blank=True, @@ -201,21 +199,9 @@ class Membership(AbstractUser): default=False, help_text=_('This person has paid the membership fee.') ) - comment = models.TextField(blank=True) + # comment = models.TextField(blank=True) # objects = MembershipManager() - thumbnail = ImageSpecField( - format='JPEG', - source='avatar', - processors=[SmartResize(width=60, height=60)], - ) - - profile = ImageSpecField( - processors=[SmartResize(width=140, height=140)], - format='PNG', - source='avatar', - ) - class Meta(object): ordering = ('username', ) swappable = 'AUTH_USER_MODEL' diff --git a/src/membership/specs.py b/src/membership/specs.py index 1cb2223..86ce5ee 100644 --- a/src/membership/specs.py +++ b/src/membership/specs.py @@ -21,4 +21,4 @@ class Thumbnail(ImageSpec): class Profile(ImageSpec): pre_cache = False - processors = (ResizeProfile,) \ No newline at end of file + processors = (ResizeProfile,) diff --git a/src/membership/templates/membership/activation_error.html b/src/membership/templates/membership/activation_error.html index 2b9aa45..bd4055a 100644 --- a/src/membership/templates/membership/activation_error.html +++ b/src/membership/templates/membership/activation_error.html @@ -7,7 +7,7 @@ {% block maincontent %}

    Aktivierung fehlgeschlagen

    -

    Leider war die Aktivierung des Schlüssels “{{ activation_key }}” war nicht erfolgreich, dies könnte mehrere Gründe haben:

    +

    Leider war die Aktivierung des Schlüssels “{{ activation_key }}” nicht erfolgreich, dies könnte mehrere Gründe haben:

    • Der Aktivierungs Link ist nicht korrekt. Bitte kopiere die ganze Zeile aus der E-Mail in die Adressleiste.
    • diff --git a/src/membership/templates/membership/membership_detail.html b/src/membership/templates/membership/membership_detail.html index db3c32a..d04e6ba 100644 --- a/src/membership/templates/membership/membership_detail.html +++ b/src/membership/templates/membership/membership_detail.html @@ -1,5 +1,5 @@ {% extends "base.html" %} -{% load i18n comments %} +{% load i18n comments thumbnail %} {% block title %}{{ membership.first_name }} {{membership.last_name}}{% endblock %} @@ -16,8 +16,8 @@ {% endblock %} {% block maincontent %} -{% if membership.profile %} - {% trans 'Profile Image' %} +{% if membership.avatar %} + {% trans 'Profile Image' %} {% else %}
      Noch kein Foto hoch geladen
      {% endif %} diff --git a/src/membership/urls.py b/src/membership/urls.py index 2a078d6..060fbd4 100644 --- a/src/membership/urls.py +++ b/src/membership/urls.py @@ -21,6 +21,8 @@ urlpatterns = patterns( url(r'^activation_sent/$', views.ActivationSent.as_view(), name="membership-registration-complete"), - url(r'^(?P[\-\.\d\w]+)/$', views.MembershipDetail.as_view(), name='membership-details'), - url(r'^(?P[\-\.\d\w]+)/edit/$', views.EditMembership.as_view(), name="membership-edit") -) \ No newline at end of file + url(r'^(?P[\-\.\d\w]+)/$', + views.MembershipDetail.as_view(), name='membership-details'), + url(r'^(?P[\-\.\d\w]+)/edit/$', + views.EditMembership.as_view(), name="membership-edit") +) diff --git a/src/membership/views.py b/src/membership/views.py index 259d87a..29f1f86 100644 --- a/src/membership/views.py +++ b/src/membership/views.py @@ -49,7 +49,7 @@ class ActivateRegistration(generic.DetailView): def login(self, user): backend = auth.get_backends()[0] user.backend = "%s.%s" % ( - backend.__module__, backend.__class__.__name__) # @IgnorePep8 + 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.')) diff --git a/src/utils/forms.py b/src/utils/forms.py index dd276dc..dea314c 100644 --- a/src/utils/forms.py +++ b/src/utils/forms.py @@ -24,6 +24,7 @@ class NumberInput(forms.widgets.Input): class TimeInput(forms.widgets.Select): + def __init__(self, attrs=None, ): timeset = datetime.datetime(2000, 1, 1, 0, 0) choices = [('', '-------')] diff --git a/src/utils/html5/base.py b/src/utils/html5/base.py index 6377811..773080f 100644 --- a/src/utils/html5/base.py +++ b/src/utils/html5/base.py @@ -13,6 +13,7 @@ except ImportError: class LookupBase(object): + @classmethod def name(cls): app_name = cls.__module__.split('.')[-2].lower() @@ -72,6 +73,7 @@ class LookupInvalid(Exception): class LookupRegistry(object): + def __init__(self): self._registry = {} @@ -84,7 +86,7 @@ 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' \ + raise LookupAlreadyRegistered(u'The name %s is already registered' % name) self._registry[name] = lookup diff --git a/src/utils/html5/forms.py b/src/utils/html5/forms.py index e5d3946..972c451 100644 --- a/src/utils/html5/forms.py +++ b/src/utils/html5/forms.py @@ -16,6 +16,7 @@ from . import widgets class Html5Mixin(object): + def widget_attrs(self, widget): """ Overwrites the standard Widget Attributes to add some HTML5 Stuff @@ -193,6 +194,7 @@ class PhoneField(Html5Mixin, django.forms.CharField): class ReCaptchaField(django.forms.fields.CharField): + def __init__(self, *args, **kwargs): self.widget = widgets.ReCaptchaInput self.required = True diff --git a/src/utils/html5/models.py b/src/utils/html5/models.py index 9a29122..f1ee133 100644 --- a/src/utils/html5/models.py +++ b/src/utils/html5/models.py @@ -11,6 +11,7 @@ from . import forms, widgets class BooleanField(models.BooleanField): + def formfield(self, **kwargs): defaults = {'form_class': forms.BooleanField} defaults.update(kwargs) @@ -18,6 +19,7 @@ class BooleanField(models.BooleanField): class CharField(models.CharField): + def formfield(self, **kwargs): defaults = {'form_class': forms.CharField} defaults.update(kwargs) @@ -25,6 +27,7 @@ class CharField(models.CharField): class DateField(models.DateField): + def formfield(self, **kwargs): defaults = { 'form_class': forms.DateField} @@ -33,6 +36,7 @@ class DateField(models.DateField): class DateTimeField(models.DateTimeField): + def formfield(self, **kwargs): defaults = {'form_class': forms.DateTimeField} defaults.update(kwargs) @@ -40,6 +44,7 @@ class DateTimeField(models.DateTimeField): class EmailField(models.EmailField): + def formfield(self, **kwargs): defaults = {'form_class': forms.EmailField} defaults.update(kwargs) @@ -47,6 +52,7 @@ class EmailField(models.EmailField): class FileField(models.FileField): + def formfield(self, **kwargs): defaults = {'form_class': forms.FileField} defaults.update(kwargs) @@ -54,6 +60,7 @@ 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): @@ -76,6 +83,7 @@ class ImageField(models.ImageField): class IntegerField(models.IntegerField): + def formfield(self, **kwargs): defaults = {'form_class': forms.IntegerField} defaults.update(kwargs) @@ -83,6 +91,7 @@ class IntegerField(models.IntegerField): class NullBooleanField(models.NullBooleanField): + def formfield(self, **kwargs): defaults = {'form_class': forms.BooleanField} defaults.update(kwargs) @@ -90,6 +99,7 @@ class NullBooleanField(models.NullBooleanField): class PositiveSmallIntegerField(models.PositiveSmallIntegerField): + def formfield(self, **kwargs): defaults = { 'form_class': forms.IntegerField, @@ -101,6 +111,7 @@ class PositiveSmallIntegerField(models.PositiveSmallIntegerField): class PositiveIntegerField(models.IntegerField): + def formfield(self, **kwargs): defaults = { 'form_class': forms.IntegerField, @@ -111,6 +122,7 @@ class PositiveIntegerField(models.IntegerField): class SlugField(models.SlugField): + def formfield(self, **kwargs): defaults = {'form_class': forms.SlugField} defaults.update(kwargs) @@ -118,6 +130,7 @@ class SlugField(models.SlugField): class TextField(models.TextField): + def formfield(self, **kwargs): defaults = { 'form_class': forms.CharField, @@ -128,6 +141,7 @@ class TextField(models.TextField): class URLField(models.URLField): + def formfield(self, **kwargs): defaults = {'form_class': forms.URLField} defaults.update(kwargs) diff --git a/src/utils/html5/widgets.py b/src/utils/html5/widgets.py index 6f6eeae..e8ac6c0 100644 --- a/src/utils/html5/widgets.py +++ b/src/utils/html5/widgets.py @@ -14,6 +14,7 @@ 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) @@ -40,6 +41,7 @@ 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 @@ -94,7 +96,7 @@ class CheckboxInput(widgets.CheckboxInput): if result: final_attrs['checked'] = 'checked' if not ( - value is True or value is False or value is None or value == ''): # @IgnorePep8 + 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) @@ -139,7 +141,7 @@ class ReCaptchaInput(widgets.Widget): type="hidden" name="recaptcha_response_field" value="manual_challenge"> """ return mark_safe(javascript % {'public_key': - settings.RECAPTCHA_PUBLIC_KEY, + settings.RECAPTCHA_PUBLIC_KEY, 'theme': 'red'}) def value_from_datadict(self, data, files, name): @@ -148,11 +150,13 @@ 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'} @@ -195,6 +199,7 @@ 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) @@ -230,6 +235,7 @@ class LookupMultipleHiddenInput(widgets.MultipleHiddenInput): class AutoCompleteSelectMultipleWidget(SelectableMultiWidget): + def __init__(self, lookup_class, *args, **kwargs): self.lookup_class = lookup_class widgets = [ @@ -252,6 +258,7 @@ class AutoCompleteSelectMultipleWidget(SelectableMultiWidget): class AutoComboboxSelectMultipleWidget(SelectableMultiWidget): + def __init__(self, lookup_class, *args, **kwargs): self.lookup_class = lookup_class widgets = [ @@ -274,6 +281,7 @@ class AutoComboboxSelectMultipleWidget(SelectableMultiWidget): class AutoCompleteSelectWidget(SelectableMultiWidget): + def __init__(self, lookup_class, *args, **kwargs): self.lookup_class = lookup_class self.allow_new = kwargs.pop('allow_new', False) @@ -299,6 +307,7 @@ 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) diff --git a/src/utils/management/commands/compresscss.py b/src/utils/management/commands/compresscss.py index e5debc4..ea3e6a9 100644 --- a/src/utils/management/commands/compresscss.py +++ b/src/utils/management/commands/compresscss.py @@ -103,19 +103,16 @@ class Command(BaseCommand): return css - def remove_unnecessary_semicolons(self, css): """Remove unnecessary semicolons.""" return re.sub(r";+\}", "}", css) - def remove_empty_rules(self, css): """Remove empty rules.""" return re.sub(r"[^\}\{]+\{\}", "", css) - def normalize_rgb_colors_to_hex(self, css): """Convert `rgb(51,102,153)` to `#336699`.""" @@ -133,7 +130,6 @@ class Command(BaseCommand): return re.sub(r"([\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", r"\1\2", css) - def condense_multidimensional_zeros(self, css): """Replace `:0 0 0 0;`, `:0 0 0;` etc. with `:0;`.""" @@ -141,18 +137,17 @@ class Command(BaseCommand): css = css.replace(":0 0 0;", ":0;") css = css.replace(":0 0;", ":0;") - # Revert `background-position:0;` to the valid `background-position:0 0;`. + # Revert `background-position:0;` to the valid `background-position:0 + # 0;`. css = css.replace("background-position:0;", "background-position:0 0;") return css - def condense_floating_points(self, css): """Replace `0.6` with `.6` where possible.""" return re.sub(r"(:|\s)0+\.(\d+)", r"\1.\2", css) - def condense_hex_colors(self, css): """Shorten colors from #AABBCC to #ABC where possible.""" @@ -170,7 +165,6 @@ class Command(BaseCommand): match = regex.search(css, match.end()) return css - def condense_whitespace(self, css): """Condense multiple adjacent whitespace characters into one.""" @@ -196,7 +190,6 @@ class Command(BaseCommand): lines.append(css[line_start:]) return '\n'.join(lines) - def compress(self, css, wrap=None): css = self.remove_comments(css) css = self.condense_whitespace(css) @@ -223,7 +216,8 @@ class Command(BaseCommand): 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_output = '%s/%s.css' % (CSS_ROOT, site.domain.replace('.', '_')) + css_output = '%s/%s.css' % (CSS_ROOT, + site.domain.replace('.', '_')) print _("Compressing CSS for %s") % site.name print "Input Dir: %s" % css_input diff --git a/src/utils/management/commands/scss-compiler.py b/src/utils/management/commands/scss-compiler.py index 1b219ab..2ab97b1 100644 --- a/src/utils/management/commands/scss-compiler.py +++ b/src/utils/management/commands/scss-compiler.py @@ -23,7 +23,8 @@ class Command(BaseCommand): 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_output = '%s/%s.css' % (CSS_ROOT, site.domain.replace('.', '_')) + css_output = '%s/%s.css' % (CSS_ROOT, + site.domain.replace('.', '_')) print _("Compressing CSS for %s") % site.name print "Input Dir: %s" % css_input @@ -44,4 +45,3 @@ class Command(BaseCommand): with open(css_output, 'w') as css_file: css_file.write(self.compress(css)) # file.write(css) - diff --git a/src/utils/massmailer.py b/src/utils/massmailer.py index bded007..d1d977d 100644 --- a/src/utils/massmailer.py +++ b/src/utils/massmailer.py @@ -6,6 +6,7 @@ from django.conf import settings from django.template import loader, Context from django.contrib.auth import get_user_model + class MassMailer(object): """ This Class will send E-Mails via an SMTP Connection to multiple recipients. diff --git a/src/utils/templatetags/__init__.py b/src/utils/templatetags/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/utils/templatetags/markup.py b/src/utils/templatetags/markup.py deleted file mode 100644 index c49a377..0000000 --- a/src/utils/templatetags/markup.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- encoding: utf-8 -*- -""" -Created on 25.11.2013 - -@author: christian -""" -from django import template -from django.conf import settings -from django.utils.safestring import mark_safe -from django.utils.encoding import force_text -import markdown - -register = template.Library() - - -@register.filter(name='markdown', is_safe=True) -def markdown_filter(value, arg=''): - """ - Runs Markdown over a given value, optionally using various - extensions python-markdown supports. - - Syntax:: - - {{ value|markdown:"extension1_name,extension2_name..." }} - - To enable safe mode, which strips raw HTML and only returns HTML - generated by actual Markdown syntax, pass "safe" as the first - extension in the list. - - If the version of Markdown in use does not support extensions, - they will be silently ignored. - - """ - markdown_vers = getattr(markdown, "version_info", 0) - if markdown_vers < (2, 1): - if settings.DEBUG: - raise template.TemplateSyntaxError( - "Error in 'markdown' filter: Django does not support versions of the Python markdown library < 2.1.") - return force_text(value) - else: - extensions = [e for e in arg.split(",") if e] - if extensions and extensions[0] == "safe": - extensions = extensions[1:] - return mark_safe(markdown.markdown( - force_text(value), extensions, safe_mode=True, - enable_attributes=False)) - else: - return mark_safe(markdown.markdown( - force_text(value), extensions, safe_mode=False)) diff --git a/sync.sh b/sync.sh index 731666d..37407a8 100755 --- a/sync.sh +++ b/sync.sh @@ -8,7 +8,7 @@ EXCLUDE_FILES="*.pyc" # ./cleanup.sh grunt -echo "Syncing projectfiles ..." +echo "Syncing project assets ..." rsync -r --copy-links --delete $SYNC_ASSESTS $SSH_LOGIN:~/ echo "Installing dependecies" @@ -22,5 +22,4 @@ rsync -r --copy-links --delete $SYNC_SOURCECODE $SSH_LOGIN:~/ echo "Rebuild and reload django..." ssh $SSH_LOGIN "rm src/kasu/settings/development.*" ssh $SSH_LOGIN "virtualenv/bin/python ~/src/manage.py collectstatic -l --noinput -v1" -# ssh $SSH_LOGIN "virtualenv/bin/python ~/src/manage.py collecttemplates --noinput -v1" ssh $SSH_LOGIN "~/init/kasu restart"