diff --git a/requirements/base.txt b/requirements/base.txt index 67e6141..3d9e487 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -8,7 +8,6 @@ django-csp django-extra-views django-markdown easy-thumbnails -flup-py3 icalendar markdown pillow diff --git a/src/content/admin.py b/src/content/admin.py index 30cbc4a..f9cb2a9 100644 --- a/src/content/admin.py +++ b/src/content/admin.py @@ -1,33 +1,33 @@ -""" -Created on 19.09.2011 - -@author: christian -""" -# import stuff we need from django from django.contrib import admin from . import models class PageTabularInline(admin.TabularInline): + """ Displays the sub-pages of an page element. """ fields = ('title_de', 'menu_name_de', "position",) model = models.Page sortable_field_name = "position" class ArticleAdmin(admin.ModelAdmin): + """ Admin interface for the news-articles. """ prepopulated_fields = {"slug": ("headline_de",)} list_display = ('headline', 'category', 'date_created', 'author',) list_editable = ('category', 'author') class CategoryAdmin(admin.ModelAdmin): + """ Admin interface for categories of the news-aricles. """ list_display = ['name', 'slug', 'description'] list_display_links = ('name', 'slug',) prepopulated_fields = {'slug': ('name_de',)} class PageAdmin(admin.ModelAdmin): + """ + Admin interface for static pages that can contain HTML or PDF Content. + """ prepopulated_fields = {"slug": ('menu_name_de',)} inlines = [PageTabularInline, ] list_display = ('position', 'menu_name', 'title', 'parent', 'path',) @@ -54,6 +54,7 @@ class PageAdmin(admin.ModelAdmin): }), ) + admin.site.register(models.Article, ArticleAdmin) admin.site.register(models.Page, PageAdmin) admin.site.register(models.Category, CategoryAdmin) diff --git a/src/content/forms.py b/src/content/forms.py index 626f670..7540422 100644 --- a/src/content/forms.py +++ b/src/content/forms.py @@ -3,11 +3,10 @@ Created on 04.10.2011 @author: christian """ -import django.forms +from django import forms from django.template.defaultfilters import slugify from django.utils.translation import ugettext as _ -from utils.html5 import forms from . import models @@ -35,9 +34,9 @@ class ArticleForm(forms.ModelForm): class PageForm(forms.ModelForm): error_css_class = 'error' required_css_class = 'required' - content_type = django.forms.ChoiceField( + content_type = forms.ChoiceField( choices=models.CONTENT_CHOICES, - widget=django.forms.RadioSelect + widget=forms.RadioSelect ) class Meta(object): @@ -47,7 +46,7 @@ class PageForm(forms.ModelForm): def clean(self): cleaned_data = super(PageForm, self).clean() content_type = cleaned_data.get("content_type") - pdf_de = cleaned_data.get("pdf_de") + cleaned_data.get("pdf_de") if content_type == "2" and not cleaned_data.get("pdf_de"): msg = _('Please upload a PDF-File to this PDF-Page.') self._errors["content_type"] = self.error_class([msg]) diff --git a/src/content/locale/de/LC_MESSAGES/django.po b/src/content/locale/de/LC_MESSAGES/django.po index bd3a534..0a91ae6 100644 --- a/src/content/locale/de/LC_MESSAGES/django.po +++ b/src/content/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: kasu.content\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-09-28 00:25+0200\n" +"POT-Creation-Date: 2017-05-10 23:16+0200\n" "PO-Revision-Date: 2016-09-28 00:24+0200\n" "Last-Translator: Christian Berg \n" "Language-Team: Deutsch <>\n" @@ -20,235 +20,265 @@ msgstr "" "X-Translated-Using: django-rosetta 0.7.2\n" "X-Generator: Poedit 1.8.9\n" -#: src/content/feeds.py:14 +#: content/feeds.py:14 msgid "Current news from Kasu" msgstr "Aktuelle Nachrichten von Kasu" -#: src/content/feeds.py:42 +#: content/feeds.py:42 msgid "Latest comments on kasu.at" msgstr "Neueste Kommentare auf Kasu.at " -#: src/content/feeds.py:43 +#: content/feeds.py:43 msgid "Kasu - latest comments" msgstr "Kasu - neue Kommentare" -#: src/content/forms.py:52 src/content/models.py:204 +#: content/forms.py:52 content/models.py:249 msgid "Please upload a PDF-File to this PDF-Page." msgstr "Bitte eine PDF Datei für diese PDF Seite hochladen." -#: src/content/models.py:51 +#: content/models.py:51 msgid "Headline" msgstr "Schlagzeile" -#: src/content/models.py:53 +#: content/models.py:53 msgid "Content" msgstr "Inhalt" -#: src/content/models.py:55 src/content/models.py:234 -#: src/content/templates/content/article_detail.html:25 +#: content/models.py:55 content/models.py:279 +#: content/templates/content/article_detail.html:25 msgid "Category" msgstr "Kategorie" -#: src/content/models.py:56 src/content/models.py:228 +#: content/models.py:56 content/models.py:273 msgid "Image" msgstr "Bild" -#: src/content/models.py:58 src/content/models.py:230 +#: content/models.py:58 content/models.py:275 msgid "Slug" msgstr "Slug" -#: src/content/models.py:60 -#: src/content/templates/content/article_detail.html:23 +#: content/models.py:60 content/templates/content/article_detail.html:23 msgid "Author" msgstr "Autor" -#: src/content/models.py:61 +#: content/models.py:61 msgid "Status" msgstr "Status" -#: src/content/models.py:63 +#: content/models.py:63 msgid "Created" msgstr "Erstellt" -#: src/content/models.py:64 +#: content/models.py:64 msgid "Modified" msgstr "Bearbeitet" -#: src/content/models.py:68 +#: content/models.py:68 msgid "Article" msgstr "Artikel" -#: src/content/models.py:69 +#: content/models.py:69 msgid "Articles" msgstr "Artikel" -#: src/content/models.py:126 src/content/models.py:132 +#: content/models.py:126 content/models.py:132 msgid "The short name for the menu-entry of this page" msgstr "Ein kurzer Name für den Menüeintrag" -#: src/content/models.py:136 src/content/models.py:139 -msgid "This title appears in the HTML header" -msgstr "Der Titel erscheint im HTML Header" +#: content/models.py:137 content/models.py:142 +msgid "The page title as you'd like it to be seen by the public" +msgstr "" -#: src/content/models.py:140 +#: content/models.py:144 msgid "slug" msgstr "Slug" -#: src/content/models.py:141 +#: content/models.py:146 +msgid "" +"The name of the page as it will appear in URLs e.g http://domain.com/blog/" +"[my-slug]/" +msgstr "" + +#: content/models.py:153 msgid "Path" msgstr "Pfad" -#: src/content/models.py:147 +#: content/models.py:165 msgid "Position" msgstr "Position" -#: src/content/models.py:149 +#: content/models.py:170 msgid "status" msgstr "Status" -#: src/content/models.py:155 +#: content/models.py:172 content/models.py:173 +#, fuzzy +#| msgid "Description" +msgid "search description" +msgstr "Beschreibung" + +#: content/models.py:176 +#, fuzzy +#| msgid "Content" +msgid "content type" +msgstr "Inhalt" + +#: content/models.py:181 msgid "enable comments" msgstr "Kommentare möglich" -#: src/content/models.py:156 +#: content/models.py:186 msgid "Template" msgstr "Vorlage" -#: src/content/models.py:219 +#: content/models.py:194 +#, fuzzy +#| msgid "created on" +msgid "first created at" +msgstr "erstellt am" + +#: content/models.py:199 +msgid "latest updated at" +msgstr "" + +#: content/models.py:264 msgid "Page" msgstr "Seite" -#: src/content/models.py:220 +#: content/models.py:265 msgid "Pages" msgstr "Seiten" -#: src/content/models.py:224 src/content/models.py:225 +#: content/models.py:269 content/models.py:270 msgid "Name" msgstr "Name" -#: src/content/models.py:226 src/content/models.py:227 +#: content/models.py:271 content/models.py:272 msgid "Description" msgstr "Beschreibung" -#: src/content/models.py:235 +#: content/models.py:280 msgid "Categories" msgstr "Kategorien" -#: src/content/templates/content/article_archive.html:5 -#: src/content/templates/content/article_archive.html:20 +#: content/templates/content/article_archive.html:5 +#: content/templates/content/article_archive.html:20 msgid "Article Archive" msgstr "Nachrichtenarchiv" -#: src/content/templates/content/article_archive.html:35 -#: src/content/templates/content/article_archive_month.html:5 -#: src/content/templates/content/article_archive_year.html:7 +#: content/templates/content/article_archive.html:35 +#: content/templates/content/article_archive_month.html:5 +#: content/templates/content/article_archive_year.html:7 msgid "Archive" msgstr "Archiv" -#: src/content/templates/content/article_archive.html:52 +#: content/templates/content/article_archive.html:52 msgid "All Categories" msgstr "Alle Kategorien" -#: src/content/templates/content/article_archive.html:67 +#: content/templates/content/article_archive.html:67 msgid "created on" msgstr "erstellt am" -#: src/content/templates/content/article_archive.html:68 +#: content/templates/content/article_archive.html:68 msgid "by" msgstr "von" -#: src/content/templates/content/article_archive.html:69 +#: content/templates/content/article_archive.html:69 msgid "comments" msgstr "Kommentare" -#: src/content/templates/content/article_archive.html:73 +#: content/templates/content/article_archive.html:73 msgid "Read More" msgstr "Mehr lesen" -#: src/content/templates/content/article_archive.html:76 +#: content/templates/content/article_archive.html:76 msgid "We're sorry. Your search yielded no results." msgstr "Es tut uns leid. Deine Suche ergab keine Treffer." -#: src/content/templates/content/article_archive.html:94 +#: content/templates/content/article_archive.html:94 msgid "Add Article" msgstr "neuer Artikel " -#: src/content/templates/content/article_archive_month.html:7 +#: content/templates/content/article_archive_month.html:7 msgid "back" msgstr "Zurück" -#: src/content/templates/content/article_detail.html:24 +#: content/templates/content/article_detail.html:24 msgid "Created on" msgstr "Erstellt am" -#: src/content/templates/content/article_detail.html:36 +#: content/templates/content/article_detail.html:36 msgid "share on" msgstr "Teile auf" -#: src/content/templates/content/article_detail.html:51 -#: src/content/templates/content/article_form.html:17 src/content/views.py:88 +#: content/templates/content/article_detail.html:51 +#: content/templates/content/article_form.html:24 content/views.py:88 msgid "Edit Article" msgstr "Artikel bearbeiten" -#: src/content/templates/content/article_form.html:17 src/content/views.py:90 +#: content/templates/content/article_form.html:24 content/views.py:90 msgid "Create Article" msgstr "Artikel erstellen" -#: src/content/templates/content/article_form.html:22 -#: src/content/templates/content/page_form.html:39 -#: src/content/templates/content/page_form.html:46 +#: content/templates/content/article_form.html:29 +#: content/templates/content/page_form.html:49 +#: content/templates/content/page_form.html:56 msgid "German" msgstr "Deutsch" -#: src/content/templates/content/article_form.html:23 -#: src/content/templates/content/page_form.html:40 -#: src/content/templates/content/page_form.html:54 +#: content/templates/content/article_form.html:30 +#: content/templates/content/page_form.html:50 +#: content/templates/content/page_form.html:64 msgid "English" msgstr "Englisch" -#: src/content/templates/content/article_form.html:42 -#: src/content/templates/content/page_form.html:63 +#: content/templates/content/article_form.html:49 +#: content/templates/content/page_form.html:73 msgid "reset" msgstr "Zurücksetzen" -#: src/content/templates/content/article_form.html:43 -#: src/content/templates/content/page_form.html:64 +#: content/templates/content/article_form.html:50 +#: content/templates/content/page_form.html:74 msgid "save" msgstr "Speichern" -#: src/content/templates/content/page_form.html:5 -#: src/content/templates/content/page_form.html:32 +#: content/templates/content/page_form.html:5 +#: content/templates/content/page_form.html:42 msgid "Edit Page" msgstr "Seite bearbeiten" -#: src/content/templates/content/page_form.html:5 -#: src/content/templates/content/page_form.html:18 -#: src/content/templates/content/page_form.html:32 +#: content/templates/content/page_form.html:5 +#: content/templates/content/page_form.html:28 +#: content/templates/content/page_form.html:42 msgid "Add Page" msgstr "Seite hinzufügen" -#: src/content/templates/content/page_form.html:17 +#: content/templates/content/page_form.html:27 msgid "Edit" msgstr "Bearbeiten" -#: src/content/templates/content/page_form.html:34 +#: content/templates/content/page_form.html:44 msgid "HTML Specific" msgstr "HTML spezifisch" -#: src/content/views.py:23 +#: content/views.py:23 msgid "This Category does not exist." msgstr "Diese Kategorie existiert nicht." -#: src/content/views.py:157 +#: content/views.py:157 #, python-format msgid "No Page found matching the Path %s" msgstr "Keine Seite unter dem Pfad %s gefunden" -#: src/content/views.py:172 +#: content/views.py:172 #, python-format msgid "No PDF Document found matching the Path %s" msgstr "Kein PDF Dokument unter dem Pfad %s gefunden." +#~ msgid "This title appears in the HTML header" +#~ msgstr "Der Titel erscheint im HTML Header" + #~ msgid "Share on Google+" #~ msgstr "Auf Google+ teilen" diff --git a/src/content/migrations/0001_initial.py b/src/content/migrations/0001_initial.py index 79532a4..9714409 100644 --- a/src/content/migrations/0001_initial.py +++ b/src/content/migrations/0001_initial.py @@ -148,6 +148,6 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='page', - unique_together=set([('slug', 'parent')]), + unique_together={('slug', 'parent')}, ), ] diff --git a/src/content/migrations/0002_auto_20150823_2232.py b/src/content/migrations/0002_auto_20150823_2232.py index 8625ebc..08bc1a6 100644 --- a/src/content/migrations/0002_auto_20150823_2232.py +++ b/src/content/migrations/0002_auto_20150823_2232.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations +from django.db import models import ckeditor.fields diff --git a/src/content/migrations/0003_auto_20161012_2205.py b/src/content/migrations/0003_auto_20161012_2205.py index d1d027f..ea8a665 100644 --- a/src/content/migrations/0003_auto_20161012_2205.py +++ b/src/content/migrations/0003_auto_20161012_2205.py @@ -15,46 +15,55 @@ class Migration(migrations.Migration): migrations.AddField( model_name='page', name='date_created', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='first created at', editable=False, db_index=True), + field=models.DateTimeField(default=django.utils.timezone.now, + verbose_name='first created at', editable=False, db_index=True), ), migrations.AddField( model_name='page', name='date_modified', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='latest updated at', editable=False), + field=models.DateTimeField( + default=django.utils.timezone.now, verbose_name='latest updated at', editable=False), ), migrations.AlterField( model_name='article', name='date_created', - field=models.DateTimeField(auto_now_add=True, verbose_name='Erstellt'), + field=models.DateTimeField( + auto_now_add=True, verbose_name='Erstellt'), ), migrations.AlterField( model_name='page', name='content_type', - field=models.IntegerField(verbose_name='Inhaltstyp', choices=[(0, 'Django View'), (1, 'HTML'), (2, 'PDF')]), + field=models.IntegerField(verbose_name='Inhaltstyp', choices=[ + (0, 'Django View'), (1, 'HTML'), (2, 'PDF')]), ), migrations.AlterField( model_name='page', name='path', - field=models.CharField(verbose_name='Pfad', unique=True, max_length=255, editable=False, db_index=True), + field=models.CharField( + verbose_name='Pfad', unique=True, max_length=255, editable=False, db_index=True), ), migrations.AlterField( model_name='page', name='slug', - field=models.SlugField(help_text='The name of the page as it will appear in URLs e.g http://domain.com/blog/[my-slug]/', max_length=100, verbose_name='Slug'), + field=models.SlugField( + help_text='The name of the page as it will appear in URLs e.g http://domain.com/blog/[my-slug]/', max_length=100, verbose_name='Slug'), ), migrations.AlterField( model_name='page', name='template', - field=models.CharField(default=b'content/page.html', max_length=255, verbose_name='Vorlage'), + field=models.CharField( + default=b'content/page.html', max_length=255, verbose_name='Vorlage'), ), migrations.AlterField( model_name='page', name='title_de', - field=models.CharField(help_text="The page title as you'd like it to be seen by the public", max_length=255, verbose_name=b'Titel'), + field=models.CharField( + help_text="The page title as you'd like it to be seen by the public", max_length=255, verbose_name=b'Titel'), ), migrations.AlterField( model_name='page', name='title_en', - field=models.CharField(help_text="The page title as you'd like it to be seen by the public", max_length=255, verbose_name=b'Title', blank=True), + field=models.CharField(help_text="The page title as you'd like it to be seen by the public", + max_length=255, verbose_name=b'Title', blank=True), ), ] diff --git a/src/content/migrations/0004_auto_20161012_2206.py b/src/content/migrations/0004_auto_20161012_2206.py index 3849b81..4fba0c8 100644 --- a/src/content/migrations/0004_auto_20161012_2206.py +++ b/src/content/migrations/0004_auto_20161012_2206.py @@ -14,11 +14,13 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='page', name='date_created', - field=models.DateTimeField(auto_now_add=True, verbose_name='first created at', db_index=True), + field=models.DateTimeField( + auto_now_add=True, verbose_name='first created at', db_index=True), ), migrations.AlterField( model_name='page', name='date_modified', - field=models.DateTimeField(auto_now=True, verbose_name='latest updated at'), + field=models.DateTimeField( + auto_now=True, verbose_name='latest updated at'), ), ] diff --git a/src/content/migrations/0005_auto_20161012_2236.py b/src/content/migrations/0005_auto_20161012_2236.py index f52a2c2..7d0cf6e 100644 --- a/src/content/migrations/0005_auto_20161012_2236.py +++ b/src/content/migrations/0005_auto_20161012_2236.py @@ -14,11 +14,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='page', name='description_de', - field=models.TextField(verbose_name='search description', blank=True), + field=models.TextField( + verbose_name='search description', blank=True), ), migrations.AddField( model_name='page', name='description_en', - field=models.TextField(verbose_name='search description', blank=True), + field=models.TextField( + verbose_name='search description', blank=True), ), ] diff --git a/src/content/models.py b/src/content/models.py index caa461d..e63e09c 100644 --- a/src/content/models.py +++ b/src/content/models.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from os import path - from ckeditor.fields import RichTextField from django.conf import settings from django.core.cache import cache @@ -12,8 +10,7 @@ from django.utils import timezone from django.utils.safestring import mark_safe from django.utils.translation import get_language, ugettext as _ -from utils import STATUS_CHOICES, STATUS_WAITING, STATUS_PUBLISHED, \ - cleaner +from utils import STATUS_CHOICES, STATUS_WAITING, STATUS_PUBLISHED, CLEANER CONTENT_CHOICES = ( (0, u'Django View'), @@ -44,7 +41,10 @@ class ArticleManager(models.Manager): 'author', 'category') def published(self): - return self.filter(status=STATUS_PUBLISHED, date_created__lte=timezone.now()) + return self.filter( + status=STATUS_PUBLISHED, + date_created__lte=timezone.now() + ) class Article(models.Model): @@ -74,8 +74,8 @@ class Article(models.Model): self.date_created = timezone.now() if not self.slug: self.slug = slugify(self.headline_de)[:50] - self.content_de = cleaner.clean_html(self.content_de) - self.content_en = cleaner.clean_html(self.content_en) + self.content_de = CLEANER.clean_html(self.content_de) + self.content_en = CLEANER.clean_html(self.content_en) def __str__(self): return self.headline @@ -120,6 +120,7 @@ class Page(models.Model): Ist keine englische Übersetzung vorhanden, wird die deutsche Version angeboten. """ + menu_name_de = models.CharField( max_length=255, verbose_name='Menü Name', @@ -143,7 +144,10 @@ class Page(models.Model): slug = models.SlugField( verbose_name=_('slug'), max_length=100, - help_text=_("The name of the page as it will appear in URLs e.g http://domain.com/blog/[my-slug]/") + help_text=_( + 'The name of the page as it will appear in URLs e.g ' + 'http://domain.com/blog/[my-slug]/' + ) ) path = models.CharField( max_length=255, @@ -169,8 +173,10 @@ class Page(models.Model): default=STATUS_WAITING, verbose_name=_('status') ) - description_de = models.TextField(verbose_name=_('search description'), blank=True) - description_en = models.TextField(verbose_name=_('search description'), blank=True) + description_de = models.TextField( + verbose_name=_('search description'), blank=True) + description_en = models.TextField( + verbose_name=_('search description'), blank=True) content_type = models.IntegerField( choices=CONTENT_CHOICES, verbose_name=_('content type')) @@ -213,26 +219,34 @@ class Page(models.Model): @property def description(self): - return getattr(self, "description_%s" % get_language()) or self.description_de + lang_attr = "description_%s" % get_language() + return getattr(self, lang_attr, self.description_de) @property def menu_name(self): - return getattr(self, - "menu_name_%s" % get_language()) or self.menu_name_de + lang_attr = "menu_name_%s" % get_language() + return getattr(self, lang_attr, self.menu_name_de) @property def pdf_file(self): - return getattr(self, "pdf_%s" % get_language()) or self.pdf_de + lang_attr = "pdf_%s" % get_language() + return getattr(self, lang_attr, self.pdf_de) @property def title(self): - return getattr(self, "title_%s" % get_language()) or self.title_de + """ Return the translated title if available """ + lang_attr = "title_%s" % get_language() + return getattr(self, lang_attr, self.title_de) def clean(self): + """ + + :return: + """ if self.parent is None: self.path = self.slug else: - self.path = path.join(self.parent.path, self.slug) + self.path = '/'.join([self.parent.path, self.slug]) if self.content_type is None: if self.pdf_de: @@ -242,8 +256,8 @@ class Page(models.Model): else: self.content_type = 0 if self.content_type == 1: - self.content_de = cleaner.clean_html(self.content_de) - self.content_en = cleaner.clean_html(self.content_en) + self.content_de = CLEANER.clean_html(self.content_de) + self.content_en = CLEANER.clean_html(self.content_en) elif self.content_type == 2 and not self.pdf_de.name: raise ValidationError( _(u'Please upload a PDF-File to this PDF-Page.')) diff --git a/src/content/sitemaps.py b/src/content/sitemaps.py index c884ddf..aa85e66 100644 --- a/src/content/sitemaps.py +++ b/src/content/sitemaps.py @@ -1,6 +1,7 @@ from django.contrib.sitemaps import Sitemap from .models import Article, Page + class ArticleSitemap(Sitemap): changefreq = "never" priority = 0.6 @@ -17,8 +18,7 @@ class PageSitemap(Sitemap): priority = 0.4 def items(self): - return Page.objects.all() #filter(status__gt=0) + return Page.objects.all() # filter(status__gt=0) def lastmod(self, page): return page.date_modified - diff --git a/src/content/templates/content/article_archive.html b/src/content/templates/content/article_archive.html index a9fa5c2..2a9de09 100755 --- a/src/content/templates/content/article_archive.html +++ b/src/content/templates/content/article_archive.html @@ -49,10 +49,10 @@ {% block navigation %} {% endblock %} diff --git a/src/content/templates/content/article_form.html b/src/content/templates/content/article_form.html index fb03e08..8159236 100755 --- a/src/content/templates/content/article_form.html +++ b/src/content/templates/content/article_form.html @@ -1,5 +1,5 @@ {% extends "base.html" %} -{% load i18n fieldset_extras %} +{% load i18n %} {% block extra_head %} @@ -15,11 +15,7 @@ window.onload = function () { {% endblock %} {% block maincontent %} - {% get_fieldset "category, image" from form as fieldset_common %} - {% get_fieldset "headline_en, content_en" from form as fieldset_en %} - {% get_fieldset "headline_en, content_en" from form as fieldset_en %} - -
+
{% if object.pk %}{% trans "Edit Article" %}{% else %}{% trans "Create Article" %}{% endif %} {% csrf_token %} diff --git a/src/content/templates/content/page_form.html b/src/content/templates/content/page_form.html index a100afb..f13e517 100755 --- a/src/content/templates/content/page_form.html +++ b/src/content/templates/content/page_form.html @@ -50,7 +50,7 @@ window.onload = function () {
  • {% trans "English" %}
  • -
    +
    {% trans "German" %} @@ -67,7 +67,7 @@ window.onload = function () {
    {{form.content_en}}
    -
    +

    diff --git a/src/content/templates/content/page_pdf.html b/src/content/templates/content/page_pdf.html index e54269c..03dabe2 100755 --- a/src/content/templates/content/page_pdf.html +++ b/src/content/templates/content/page_pdf.html @@ -60,7 +60,7 @@ body {

    \ No newline at end of file diff --git a/src/content/urls.py b/src/content/urls.py deleted file mode 100644 index 1fa6239..0000000 --- a/src/content/urls.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- encoding: utf-8 -*- -""" -Created on 03.10.2011 - -@author: christian -""" -from django.conf.urls import url -from .views import ImageList, PageList - - -urlpatterns = [ - url(r'^image_list.js$', ImageList.as_view(), name='content-image-list'), - url(r'^link_list.js$', PageList.as_view(), name='content-page-list'), -] diff --git a/src/content/views.py b/src/content/views.py index 34f359c..34bcb68 100644 --- a/src/content/views.py +++ b/src/content/views.py @@ -1,31 +1,48 @@ -# Create your views here. +import os import django_comments as comments -import os +from csp.decorators import csp_update from django.conf import settings from django.http import HttpResponse, Http404 +from django.utils.decorators import method_decorator from django.utils.translation import ugettext as _ from django.views import generic -from . import models, forms from events.models import Photo from utils.mixins import PermissionRequiredMixin +from . import models, forms class ArticleArchiveMixin(object): + """ + Mixin View to add common context data to Views of the news article archive. + """ def get_category(self, queryset): - try: - 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.")) - except KeyError: + """ + Filter the queryset by category if one has been specified in the URL + + :param queryset: an model.Article.objects Queryset + :return: an model.Article.objects Queryset filterd by category + """ + + category_slug = self.kwargs.get('category') + if not category_slug: self.category = None return self.queryset + try: + self.category = models.Category.objects.get(slug=category_slug) + except models.Category.DoesNotExist: + raise Http404(_("This Category does not exist.")) + return queryset.filter(category=self.category) def get_context_data(self, **kwargs): + """ + Adds the categories and the active category to the template context. + + :return: an django.template.context + """ + context = super(ArticleArchiveMixin, self).get_context_data(**kwargs) context['categories'] = models.Category.objects.all() context['active_category'] = self.category @@ -33,6 +50,10 @@ class ArticleArchiveMixin(object): class ArticleArchiveIndex(ArticleArchiveMixin, generic.ArchiveIndexView): + """ + Displays the latest news and the filters to browse the archives. + """ + queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED) date_field = 'date_created' paginate_by = 5 @@ -40,12 +61,21 @@ class ArticleArchiveIndex(ArticleArchiveMixin, generic.ArchiveIndexView): allow_empty = True def get_queryset(self): - queryset = generic.ArchiveIndexView.get_queryset(self) - queryset = self.get_category(queryset) - return queryset + """ + Filter the Queryset by category. + + :return: models.Article.objects queryset + """ + return self.get_category( + super(ArticleArchiveIndex, self).get_queryset() + ) class ArticleYearArchive(ArticleArchiveMixin, generic.YearArchiveView): + """ + Displays the Articles filterd by a specific year + """ + queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED) date_field = 'date_created' paginate_by = 5 @@ -54,12 +84,21 @@ class ArticleYearArchive(ArticleArchiveMixin, generic.YearArchiveView): allow_empty = True def get_queryset(self): - queryset = generic.YearArchiveView.get_queryset(self) - queryset = self.get_category(queryset) - return queryset + """ + Filter the Queryset by category. + + :return: models.Article.objects queryset + """ + return self.get_category( + super(ArticleYearArchive, self).get_queryset() + ) class ArticleMonthArchive(ArticleArchiveMixin, generic.MonthArchiveView): + """ + Displays the Articles filterd by a specific month + """ + queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED) date_field = 'date_created' month_format = '%m' @@ -68,16 +107,28 @@ class ArticleMonthArchive(ArticleArchiveMixin, generic.MonthArchiveView): allow_empty = True def get_queryset(self): - queryset = generic.MonthArchiveView.get_queryset(self) - queryset = self.get_category(queryset) - return queryset + """ + Filter the Queryset by category. + + :return: models.Article.objects queryset + """ + return self.get_category( + super(ArticleMonthArchive, self).get_queryset() + ) class ArticleDetail(generic.DetailView): + """ + Render the news Article, but only if it got published. + """ + queryset = models.Article.objects.filter(status=models.STATUS_PUBLISHED) class ArticleForm(PermissionRequiredMixin, generic.UpdateView): + """ + View to add or edit an Article + """ model = models.Article form_class = forms.ArticleForm permission_required = 'content.change_article' @@ -97,28 +148,11 @@ class ArticleForm(PermissionRequiredMixin, generic.UpdateView): return models.Article(author=self.request.user) -class ImageList(generic.View): - # noinspection PyMethodMayBeStatic - - def get(self, kwargs): - image_list = [] - response = HttpResponse(content_type='text/javascript') - response.write('var tinyMCEImageList = new Array(') - os.chdir(settings.MEDIA_ROOT) - for dirpath, dirnames, filenames in os.walk( - 'images'): # @UnusedVariable @IgnorePep8 - filenames.sort() - for filename in filenames: - image_list.append('["%(name)s", "%(path)s"]' % { - 'name': os.path.join(dirpath, filename), - 'path': os.path.join(settings.MEDIA_URL, dirpath, filename) - }) - response.write(', '.join(image_list)) - response.write(');') - return response - - class PageAddForm(PermissionRequiredMixin, generic.CreateView): + """ + Renders an Form to create a new page for users with conforming permissions. + """ + form_class = forms.PageForm template_name = 'content/page_form.html' permission_required = 'content.add_page' @@ -132,8 +166,16 @@ class PageAddForm(PermissionRequiredMixin, generic.CreateView): parent = models.Page.objects.get(path=path) return {'parent': parent} + @method_decorator(csp_update(SCRIPT_SRC="'unsafe-eval'")) + def dispatch(self, *args, **kwargs): + return super(PageAddForm, self).dispatch(*args, **kwargs) + class PageEditForm(PermissionRequiredMixin, generic.UpdateView): + """ + Renders an Form to edit a page for users with conforming permissions. + """ + form_class = forms.PageForm permission_required = 'content.change_page' @@ -145,6 +187,10 @@ class PageEditForm(PermissionRequiredMixin, generic.UpdateView): path = path[:-1] return models.Page.objects.get(path=path) + @method_decorator(csp_update(SCRIPT_SRC="'unsafe-eval'")) + def dispatch(self, *args, **kwargs): + return super(PageEditForm, self).dispatch(*args, **kwargs) + class PageHtml(generic.DetailView): @@ -184,23 +230,6 @@ class PagePdf(generic.DeleteView): raise Http404('File not Found %s.pdf' % self.kwargs['path']) -class PageList(generic.View): - # noinspection PyMethodMayBeStatic - - def get(self, kwargs): - response = HttpResponse(content_type='text/javascript') - response.write('var tinyMCELinkList = new Array(') - page_list = [] - for page in models.Page.objects.filter(status=models.STATUS_PUBLISHED): - page_list.append('["%(name)s", "%(path)s"]' % { - 'name': page.menu_name, - 'path': page.get_absolute_url() - }) - response.write(', '.join(page_list)) - response.write(');') - return response - - class StartPage(generic.TemplateView): template_name = 'index.html' diff --git a/src/events/context_processors.py b/src/events/context_processors.py index 0708049..e04a2a2 100644 --- a/src/events/context_processors.py +++ b/src/events/context_processors.py @@ -13,10 +13,10 @@ def upcoming_events(request): next_event = cache.get('next_event', False) upcoming_events = cache.get('upcoming_events', False) - if current_event == False: + if not current_event: current_event = Event.objects.current_event() cache.set('current_event', current_event, 360) - if next_event == False: + if not next_event: next_event = Event.objects.next_event() cache.set('next_event', next_event, 360) diff --git a/src/events/fixtures/test_events.json b/src/events/fixtures/test_events.json new file mode 100644 index 0000000..9f04b66 --- /dev/null +++ b/src/events/fixtures/test_events.json @@ -0,0 +1,1291 @@ +[ +{ + "model": "events.event", + "pk": 1, + "fields": { + "name": "Kasu Spieleabend", + "description": "Etwa alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-08-16T12:00:00Z", + "end": "2015-08-16T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": null, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 2, + "fields": { + "name": "Kasu Spieleabend", + "description": "Unserer 2 W\u00f6chigen regul\u00e4ren Treffen", + "location": 1, + "start": "2015-08-23T12:00:00Z", + "end": "2015-08-23T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 4, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-01-02T13:00:00Z", + "end": "2015-01-03T01:00:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 5, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-01-04T13:00:00Z", + "end": "2015-01-04T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 6, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-01-16T13:00:00Z", + "end": "2015-01-16T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 7, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-02-01T13:00:00Z", + "end": "2015-02-01T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 8, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-02-13T13:00:00Z", + "end": "2015-02-13T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 9, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-02-27T13:00:00Z", + "end": "2015-02-28T02:00:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 10, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-03-15T13:00:00Z", + "end": "2015-03-15T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 11, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-04-19T12:00:00Z", + "end": "2015-04-19T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 12, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-05-01T12:00:00Z", + "end": "2015-05-01T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 13, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-05-17T12:00:00Z", + "end": "2015-05-17T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 14, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-05-29T12:00:00Z", + "end": "2015-05-29T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 15, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-06-12T12:00:00Z", + "end": "2015-06-12T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 16, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-06-28T12:00:00Z", + "end": "2015-06-28T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 17, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-07-10T12:00:00Z", + "end": "2015-07-10T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 18, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-07-19T12:00:00Z", + "end": "2015-07-19T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 19, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-07-31T12:00:00Z", + "end": "2015-07-31T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 23, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-10-02T13:00:00Z", + "end": "2015-10-02T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 24, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-10-18T12:00:00Z", + "end": "2015-10-18T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 25, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-10-30T14:00:00Z", + "end": "2015-10-30T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 27, + "fields": { + "name": "Kasu Spieleabend", + "description": "Alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-12-04T14:00:00Z", + "end": "2015-12-04T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 28, + "fields": { + "name": "Kasu Weihnachtsfeier", + "description": "

    Unsere Vereinsweihnachtsfeier mit Keksen, Punsch und Engerl-Bengerl :)

    \r\n", + "location": 1, + "start": "2015-12-20T13:00:00Z", + "end": "2015-12-20T22:59:00Z", + "url": "", + "image": "events/2015-12-20/kasu-weihnachtsfeier.jpg", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": null, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 62, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Etwa alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt. Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2015-09-18T13:00:00Z", + "end": "2015-09-18T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 63, + "fields": { + "name": "Kasu Halloween-Feier", + "description": "

    Wir feiern wieder Halloween!

    \r\n\r\n

    Wir laden euch ein zu einem netten Abend mit Mahjong und bizzarren Filmen.

    \r\n\r\n

    Da am gleichen Abend ein GO-Spieleabend ist, werden wir mit den Filmen erst abends (gegen 19 Uhr) beginnen, zum Spielen könnt ihr aber schon ab 16 Uhr vorbei kommen.

    \r\n\r\n

    Mitgebrachte Knabbereien und Getränke sind immer gerne gesehen :)

    \r\n", + "location": 1, + "start": "2015-10-31T15:00:00Z", + "end": "2015-10-31T22:30:00Z", + "url": "", + "image": "events/2015-10-31/kasu-halloween-feier.jpg", + "mahjong_tournament": false, + "mahjong_season": 2015, + "photo_count": 0, + "event_series": null, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 64, + "fields": { + "name": "Kasu Spieleabend", + "description": "Etwa alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-10-09T12:00:00Z", + "end": "2015-10-09T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 65, + "fields": { + "name": "KASU Ranking Turnier 2016", + "description": "

    Erstes Turnier des Jahres 2016.

    \r\n", + "location": 1, + "start": "2016-01-03T13:00:00Z", + "end": "2016-01-03T21:00:00Z", + "url": "", + "image": "", + "mahjong_tournament": true, + "mahjong_season": 2016, + "photo_count": 0, + "event_series": null, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 66, + "fields": { + "name": "Kasu Spieleabend", + "description": "Etwa alle 2 Wochen, abwechselnd Freitag oder Sonntag findet unser Spieleabend im Go 7 statt.\r\nJede Person ist willkommen, egal ob Clubmitglied oder nicht.\r\nGespielt werden kann alles was gef\u00e4llt. Es sind immer Leute da um unsere Spiele zu erkl\u00e4ren.\r\nF\u00fcr Vereinsmitglieder gibt es die M\u00f6glichkeit Rankingspiele zu absolvieren.\r\nTagesgeb\u00fchr f\u00fcr die R\u00e4ume: \u20ac 2,-- pro Person wenn gespielt wurde.", + "location": 1, + "start": "2015-11-20T14:00:00Z", + "end": "2015-11-20T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 1, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 67, + "fields": { + "name": "Kasu Generalversammlung und Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n\r\n

    Das erste Treffen 2016 beinhaltet auch die Kasu-Generalversammlung, welche um 18:00 beginnen wird. Dabei werden der Vorstand neu gewählt und wichtige Themen für das kommende Jahr besprochen. Wenn ihr euer Stimmrecht als Vereinsmitglied nutzen wollt, seid dabei!

    \r\n", + "location": 1, + "start": "2016-01-15T14:00:00Z", + "end": "2016-01-15T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": null, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 68, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Achtung!!!

    \r\n\r\n

    Beim heutigen Treffen wird voraussichtlich erst gegen 14:15 jemand da sein, um aufzusperren.

    \r\n\r\n

    Bitte bringt also etwas Geduld mit oder lasst euch mit der Anreise etwas Zeit :)

    \r\n\r\n

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-01-31T13:00:00Z", + "end": "2016-01-31T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": null, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 69, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-02-12T14:00:00Z", + "end": "2016-02-12T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 70, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-02-26T14:00:00Z", + "end": "2016-02-26T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 71, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-03-06T13:00:00Z", + "end": "2016-03-06T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 72, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-03-11T14:00:00Z", + "end": "2016-03-11T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 73, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-03-18T14:00:00Z", + "end": "2016-03-18T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 74, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-03-27T12:00:00Z", + "end": "2016-03-27T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 75, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-04-03T12:00:00Z", + "end": "2016-04-03T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 76, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-04-08T13:00:00Z", + "end": "2016-04-08T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 77, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-04-15T13:00:00Z", + "end": "2016-04-15T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 78, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-04-24T12:00:00Z", + "end": "2016-04-24T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 79, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-04-29T13:00:00Z", + "end": "2016-04-29T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 80, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-05-06T13:00:00Z", + "end": "2016-05-06T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 81, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-05-15T12:00:00Z", + "end": "2016-05-15T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 82, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-05-22T12:00:00Z", + "end": "2016-05-22T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 83, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-05-27T13:00:00Z", + "end": "2016-05-27T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 84, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    ACHTUNG! Späterer Beginn!!

    \r\n\r\n

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-06-05T13:00:00Z", + "end": "2016-06-05T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 85, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-06-17T13:00:00Z", + "end": "2016-06-17T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 86, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-06-26T12:00:00Z", + "end": "2016-06-26T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 87, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-07-10T12:00:00Z", + "end": "2016-07-10T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 88, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-07-24T12:00:00Z", + "end": "2016-07-24T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 89, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-08-07T12:00:00Z", + "end": "2016-08-07T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 93, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-08-21T12:00:00Z", + "end": "2016-08-21T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 94, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-09-04T12:00:00Z", + "end": "2016-09-04T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 95, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-09-16T13:00:00Z", + "end": "2016-09-16T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 97, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-10-16T12:00:00Z", + "end": "2016-10-16T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 98, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-10-30T13:00:00Z", + "end": "2016-10-30T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 100, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-11-27T13:00:00Z", + "end": "2016-11-27T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 103, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-07-01T13:00:00Z", + "end": "2016-07-01T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 104, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-07-15T13:00:00Z", + "end": "2016-07-15T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 105, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    ACHTUNG! Späterer Beginn!!

    \r\n\r\n

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-07-31T13:00:00Z", + "end": "2016-07-31T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 106, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-08-12T13:00:00Z", + "end": "2016-08-12T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 108, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-09-09T13:00:00Z", + "end": "2016-09-09T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 109, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-09-25T12:00:00Z", + "end": "2016-09-25T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 110, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-10-07T13:00:00Z", + "end": "2016-10-07T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 111, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7. Heute auch Vorbereitung für das Turnier am Samstag und Sonntag!

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-10-21T13:00:00Z", + "end": "2016-10-21T21:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-16T08:41:25.360Z" + } +}, +{ + "model": "events.event", + "pk": 112, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-11-04T14:00:00Z", + "end": "2016-11-04T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-10-12T20:24:39.910Z" + } +}, +{ + "model": "events.event", + "pk": 113, + "fields": { + "name": "Kasu Spieleabend", + "description": "

    Unser regelmäßiger Spieleabend im GO7.

    \r\n\r\n

    Jede Person ist willkommen, egal ob Clubmitglied oder nicht. Gespielt werden kann alles was gefällt. Es sind immer Leute da um unsere Spiele zu erklären. Für Vereinsmitglieder gibt es die Möglichkeit Rankingspiele zu absolvieren. Tagesgebühr für die Räume: € 2,-- pro Person wenn gespielt wurde.

    \r\n", + "location": 1, + "start": "2016-11-18T14:00:00Z", + "end": "2016-11-18T22:59:00Z", + "url": "", + "image": "", + "mahjong_tournament": false, + "mahjong_season": null, + "photo_count": 0, + "event_series": 68, + "date_created": null, + "date_modified": "2016-11-14T18:07:15.507Z" + } +}, +{ + "model": "events.location", + "pk": 1, + "fields": { + "name": "Go 7", + "description": "Das GO7 ist der zentrale Treffpunkt f\u00fcr Gospieler in Wien, aber an gewissen Tagen ist auch Kasu im GO7 anzutreffen.\r\n\r\nDer gro\u00dfz\u00fcge Hauptspielraum fasst bis zu 50 Personen, daneben gibt es 2 kleinere R\u00e4ume, wobei einer f\u00fcr die Raucher reserviert ist.\r\n\r\nDer Einfang befindet sich gleich bei der U3 Zieglergasse. Seit dem Umbau der Mah\u00fc ist der leicht versteckte Zugang zum Haus leicht zu finden. Es wurde ein digitales GO Brett vor dem Haus fix installiert.", + "image": "events/location/1.png", + "url": "http://goverband.at/wp/?page_id=758", + "postal_code": "1060", + "street_address": "Mariahilferstra\u00dfe 82", + "locality": "Wien", + "country": "AT", + "date_created": null, + "date_modified": "2016-10-12T20:24:44.566Z" + } +} +] diff --git a/src/events/forms.py b/src/events/forms.py index 2eeecfe..b68b5e4 100644 --- a/src/events/forms.py +++ b/src/events/forms.py @@ -8,7 +8,6 @@ from django.utils.translation import ugettext as _ from django.contrib.auth import get_user_model from . import models -from utils.html5.widgets import DateTimeInput user_query = get_user_model().objects.all() @@ -47,16 +46,17 @@ class EventForm(forms.ModelForm): start = forms.DateTimeField( label=_('start'), required=True, - widget=DateTimeInput() # widget=SplitDateTimeWidget() + widget=forms.SplitHiddenDateTimeWidget() ) end = forms.DateTimeField( label=_('end'), required=False, - widget=DateTimeInput() # widget=SplitDateTimeWidget() + widget=forms.SplitDateTimeWidget() ) class Meta(object): model = models.Event exclude = ('event_count', 'event_series', ) + EventSeriesFormset = forms.inlineformset_factory( models.Event, models.Event, fields=('start', 'end'), form=EventForm) diff --git a/src/events/locale/de/LC_MESSAGES/django.po b/src/events/locale/de/LC_MESSAGES/django.po index 19936a0..df46dfe 100644 --- a/src/events/locale/de/LC_MESSAGES/django.po +++ b/src/events/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: kasu.events\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-09-28 00:25+0200\n" +"POT-Creation-Date: 2017-05-10 23:16+0200\n" "PO-Revision-Date: 2016-09-28 00:24+0200\n" "Last-Translator: Christian Berg \n" "Language-Team: Kasu \n" @@ -19,58 +19,58 @@ msgstr "" "X-Generator: Poedit 1.8.9\n" "X-Translated-Using: django-rosetta 0.7.6\n" -#: src/events/admin.py:16 src/events/models.py:109 +#: events/admin.py:16 events/models.py:114 msgid "Event Series" msgstr "Veranstaltungsreihen" -#: src/events/forms.py:23 +#: events/forms.py:23 msgid "Images" msgstr "Bilder" -#: src/events/forms.py:49 +#: events/forms.py:49 msgid "start" msgstr "Beginn" -#: src/events/forms.py:53 +#: events/forms.py:53 msgid "end" msgstr "Ende" -#: src/events/models.py:82 src/events/models.py:186 src/events/models.py:228 +#: events/models.py:84 events/models.py:207 events/models.py:260 msgid "Name" msgstr "Name" -#: src/events/models.py:83 src/events/models.py:187 src/events/models.py:233 +#: events/models.py:85 events/models.py:208 events/models.py:268 msgid "Description" msgstr "Beschreibung" -#: src/events/models.py:85 src/events/templates/events/event_detail.html:31 -#: src/events/templates/events/event_detail.html:74 -#: src/events/templates/events/event_list.html:28 -#: src/events/templates/events/photo_upload.html:13 +#: events/models.py:87 events/templates/events/event_detail.html:29 +#: events/templates/events/event_detail.html:87 +#: events/templates/events/event_list.html:28 +#: events/templates/events/photo_upload.html:13 msgid "Start" msgstr "Beginn" -#: src/events/models.py:86 src/events/templates/events/event_detail.html:32 -#: src/events/templates/events/event_detail.html:75 +#: events/models.py:88 events/templates/events/event_detail.html:30 +#: events/templates/events/event_detail.html:89 msgid "End" msgstr "Ende" -#: src/events/models.py:87 src/events/models.py:195 -#: src/events/templates/events/event_detail.html:36 -#: src/events/templates/events/event_detail.html:70 -#: src/events/templates/events/event_detail.html:76 +#: events/models.py:89 events/models.py:216 +#: events/templates/events/event_detail.html:34 +#: events/templates/events/event_detail.html:80 +#: events/templates/events/event_detail.html:92 msgid "Homepage" msgstr "Homepage" -#: src/events/models.py:89 src/events/models.py:189 src/events/models.py:229 +#: events/models.py:91 events/models.py:210 events/models.py:262 msgid "Image" msgstr "Bild" -#: src/events/models.py:96 +#: events/models.py:98 msgid "Mahjong Tournament" msgstr "Mahjong Turnier" -#: src/events/models.py:98 +#: events/models.py:100 msgid "" "This event is a tournament, different rules apply for the kyu " "ranking." @@ -78,11 +78,11 @@ msgstr "" "Diese Veranstaltung ist ein Turnier, es gelten andere Regeln für das Kyu " "Ranking." -#: src/events/models.py:102 +#: events/models.py:104 msgid "Mahjong Season" msgstr "Mahjong Saison" -#: src/events/models.py:110 +#: events/models.py:115 msgid "" "Wenn dieser Event zu einer Veranstaltungsreihe gehört werden Ort, " "Beschreibung, Bild und Homepage von dem hier angegebenen Event " @@ -91,236 +91,244 @@ msgstr "" "Wenn dieser Termin zu einer Veranstaltungsreihe gehört werden Ort, " "Beschreibung, Bild und Homepage von dem hier angegebenen Event übernommen." -#: src/events/models.py:117 +#: events/models.py:124 events/models.py:226 events/models.py:290 +msgid "first created at" +msgstr "" + +#: events/models.py:129 events/models.py:231 events/models.py:295 +msgid "latest updated at" +msgstr "" + +#: events/models.py:134 msgid "Event" msgstr "Termin" -#: src/events/models.py:118 +#: events/models.py:135 msgid "Events" msgstr "Termine" -#: src/events/models.py:131 +#: events/models.py:148 msgid "A event can't end before it had started" msgstr "Eine Veranstaltung kann nicht enden bevor sie begonnen hat" -#: src/events/models.py:196 +#: events/models.py:217 msgid "Postal Code" msgstr "Postleitzahl" -#: src/events/models.py:197 +#: events/models.py:218 msgid "Street Address" msgstr "Straße" -#: src/events/models.py:198 +#: events/models.py:219 msgid "Locality" msgstr "Ort" -#: src/events/models.py:199 +#: events/models.py:220 msgid "Country" msgstr "Land" -#: src/events/models.py:202 +#: events/models.py:235 msgid "Venue" msgstr "Veranstaltungsort" -#: src/events/models.py:203 +#: events/models.py:236 msgid "Venues" msgstr "Veranstaltungsorte" -#: src/events/models.py:239 +#: events/models.py:274 msgid "Startpage" msgstr "Startseite" -#: src/events/models.py:242 +#: events/models.py:277 msgid "Display this Photo on the Startpage Teaser" msgstr "Foto als Teaser auf der Startseite verwenden." -#: src/events/models.py:244 +#: events/models.py:279 msgid "Published on" msgstr "Veröffentlicht am" -#: src/events/models.py:246 +#: events/models.py:281 msgid "Number of views" msgstr "Wie oft gesehen" -#: src/events/models.py:258 src/events/templates/events/event_archive.html:38 -#: src/events/templates/events/event_list.html:18 +#: events/models.py:306 events/templates/events/event_archive.html:38 +#: events/templates/events/event_list.html:18 msgid "Event Image" msgstr "Veranstaltungsbild" -#: src/events/models.py:259 +#: events/models.py:307 msgid "Event Images" msgstr "Veranstaltungsbilder" -#: src/events/templates/events/event_archive.html:5 -#: src/events/templates/events/event_archive.html:9 +#: events/templates/events/event_archive.html:5 +#: events/templates/events/event_archive.html:9 msgid "Event Archive" msgstr "Veranstaltungsarchiv" -#: src/events/templates/events/event_archive.html:42 -#: src/events/templates/events/event_detail.html:72 -#: src/events/templates/events/event_list.html:22 -#: src/events/templates/events/photo_detail.html:48 +#: events/templates/events/event_archive.html:42 +#: events/templates/events/event_detail.html:85 +#: events/templates/events/event_list.html:22 +#: events/templates/events/photo_detail.html:53 msgid "Date" msgstr "Datum" -#: src/events/templates/events/event_archive.html:47 +#: events/templates/events/event_archive.html:47 msgid "Time" msgstr "Zeit" -#: src/events/templates/events/event_archive.html:49 -#: src/events/templates/events/photo_upload.html:16 +#: events/templates/events/event_archive.html:49 +#: events/templates/events/photo_upload.html:16 msgid "from" msgstr "von" -#: src/events/templates/events/event_archive.html:49 -#: src/events/templates/events/photo_upload.html:16 +#: events/templates/events/event_archive.html:49 +#: events/templates/events/photo_upload.html:16 msgid "to" msgstr "bis" -#: src/events/templates/events/event_archive.html:57 -#: src/events/templates/events/event_detail.html:33 -#: src/events/templates/events/event_detail.html:62 -#: src/events/templates/events/event_list.html:32 -#: src/events/templates/events/photo_upload.html:23 +#: events/templates/events/event_archive.html:57 +#: events/templates/events/event_detail.html:31 +#: events/templates/events/event_detail.html:72 +#: events/templates/events/event_list.html:32 +#: events/templates/events/photo_upload.html:23 msgid "Location" msgstr "Ort" -#: src/events/templates/events/event_archive.html:58 -#: src/events/templates/events/event_list.html:35 -#: src/events/templates/events/photo_upload.html:25 -#: src/events/templates/events/photo_upload.html:26 +#: events/templates/events/event_archive.html:58 +#: events/templates/events/event_list.html:35 +#: events/templates/events/photo_upload.html:25 +#: events/templates/events/photo_upload.html:26 msgid "Comments" msgstr "Kommentare" -#: src/events/templates/events/event_archive.html:59 -#: src/events/templates/events/event_detail.html:38 -#: src/events/templates/events/event_detail.html:48 -#: src/events/templates/events/photo_upload.html:28 -#: src/events/templates/events/photo_upload.html:29 +#: events/templates/events/event_archive.html:59 +#: events/templates/events/event_detail.html:36 +#: events/templates/events/event_detail.html:48 +#: events/templates/events/photo_upload.html:28 +#: events/templates/events/photo_upload.html:29 msgid "Photos" msgstr "Fotos" -#: src/events/templates/events/event_archive.html:60 -#: src/events/templates/events/event_archive.html:61 -#: src/events/templates/events/event_detail.html:37 -#: src/events/templates/events/event_detail.html:49 +#: events/templates/events/event_archive.html:60 +#: events/templates/events/event_archive.html:61 +#: events/templates/events/event_detail.html:35 +#: events/templates/events/event_detail.html:51 msgid "Hanchans" msgstr "Hanchans" -#: src/events/templates/events/event_detail.html:39 +#: events/templates/events/event_detail.html:37 msgid "tourney" msgstr "Turnier" -#: src/events/templates/events/event_detail.html:39 +#: events/templates/events/event_detail.html:37 msgid "other rules apply here" msgstr "hier gelten andere Regeln" -#: src/events/templates/events/event_detail.html:47 +#: events/templates/events/event_detail.html:45 msgid "Info" msgstr "Info" -#: src/events/templates/events/event_detail.html:50 +#: events/templates/events/event_detail.html:54 msgid "Mai-Star Games" msgstr "Mai-Star Spiele" -#: src/events/templates/events/event_detail.html:52 +#: events/templates/events/event_detail.html:57 msgid "Event Ranking" msgstr "Veranstaltungs Wertung" -#: src/events/templates/events/event_detail.html:90 +#: events/templates/events/event_detail.html:100 msgid "Share on Facebook" msgstr "Auf Facebook teilen" -#: src/events/templates/events/event_detail.html:96 +#: events/templates/events/event_detail.html:104 msgid "Share on Google+" msgstr "Auf Google+ teilen" -#: src/events/templates/events/event_detail.html:100 +#: events/templates/events/event_detail.html:109 msgid "Share on Twitter" msgstr "Auf Twitter teilen" -#: src/events/templates/events/event_detail.html:104 +#: events/templates/events/event_detail.html:113 msgid "Show on Google Maps" msgstr "Auf Google Maps zeigen" -#: src/events/templates/events/event_detail.html:121 -#: src/events/templates/events/event_form.html:9 src/events/views.py:106 +#: events/templates/events/event_detail.html:127 +#: events/templates/events/event_form.html:9 events/views.py:106 msgid "Edit Event" msgstr "Termin bearbeiten" -#: src/events/templates/events/event_detail.html:124 +#: events/templates/events/event_detail.html:131 msgid "Add Dates" msgstr "Termine hinzufügen" -#: src/events/templates/events/event_form.html:9 -#: src/events/templates/events/page.html:9 src/events/views.py:108 +#: events/templates/events/event_form.html:9 +#: events/templates/events/page.html:9 events/views.py:108 msgid "Add Event" msgstr "Neuer Termin" -#: src/events/templates/events/event_form.html:19 -#: src/events/templates/events/photo_list.html:35 +#: events/templates/events/event_form.html:19 +#: events/templates/events/photo_list.html:35 msgid "reset" msgstr "Zurücksetzen" -#: src/events/templates/events/event_form.html:20 -#: src/events/templates/events/eventseries_form.html:23 +#: events/templates/events/event_form.html:20 +#: events/templates/events/eventseries_form.html:25 msgid "save" msgstr "Speichern" -#: src/events/templates/events/event_list.html:4 -#: src/events/templates/events/event_list.html:5 +#: events/templates/events/event_list.html:4 +#: events/templates/events/event_list.html:5 msgid "Upcoming Events" msgstr "Bevorstehende Veranstaltungen" -#: src/events/templates/events/eventseries_form.html:22 +#: events/templates/events/eventseries_form.html:24 msgid "back" msgstr "Zurück" -#: src/events/templates/events/photo_confirm_delete.html:17 +#: events/templates/events/photo_confirm_delete.html:17 msgid "Cancel" msgstr "Abbrechen" -#: src/events/templates/events/photo_confirm_delete.html:21 -#: src/events/templates/events/photo_list.html:21 +#: events/templates/events/photo_confirm_delete.html:21 +#: events/templates/events/photo_list.html:21 msgid "Delete" msgstr "Löschen" -#: src/events/templates/events/photo_detail.html:39 +#: events/templates/events/photo_detail.html:44 msgid "previous" msgstr "Zurück" -#: src/events/templates/events/photo_detail.html:47 +#: events/templates/events/photo_detail.html:52 msgid "Photographer" msgstr "Fotograf" -#: src/events/templates/events/photo_detail.html:53 +#: events/templates/events/photo_detail.html:58 msgid "share on" msgstr "Teile auf" -#: src/events/templates/events/photo_detail.html:76 +#: events/templates/events/photo_detail.html:81 msgid "download" msgstr "Herunterladen" -#: src/events/templates/events/photo_detail.html:77 +#: events/templates/events/photo_detail.html:82 msgid "Rotate counter clockwise" msgstr "mit dem Uhrzeiger drehen" -#: src/events/templates/events/photo_detail.html:78 +#: events/templates/events/photo_detail.html:83 msgid "Rotate clockwise" msgstr "gegen den Uhrzeiger drehen" -#: src/events/templates/events/photo_detail.html:79 +#: events/templates/events/photo_detail.html:84 msgid "Save" msgstr "Speichern" -#: src/events/templates/events/photo_list.html:36 -#: src/events/templates/events/photo_upload.html:35 -#: src/events/templates/events/photo_upload.html:49 +#: events/templates/events/photo_list.html:36 +#: events/templates/events/photo_upload.html:35 +#: events/templates/events/photo_upload.html:49 msgid "Upload" msgstr "Hochladen" -#: src/events/views.py:209 +#: events/views.py:209 msgid "Event does not exist" msgstr "Veranstaltung gibt es nicht" diff --git a/src/events/migrations/0006_auto_20160916_1800.py b/src/events/migrations/0006_auto_20160916_1800.py index 33fe3cf..95a84ed 100644 --- a/src/events/migrations/0006_auto_20160916_1800.py +++ b/src/events/migrations/0006_auto_20160916_1800.py @@ -13,12 +13,14 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( name='photo', - options={'ordering': ['created_date'], 'get_latest_by': 'created_date', 'verbose_name': 'Veranstaltungsbild', 'verbose_name_plural': 'Veranstaltungsbilder'}, + options={'ordering': ['created_date'], 'get_latest_by': 'created_date', + 'verbose_name': 'Veranstaltungsbild', 'verbose_name_plural': 'Veranstaltungsbilder'}, ), migrations.AlterField( model_name='event', name='mahjong_season', - field=models.PositiveSmallIntegerField(null=True, verbose_name='Mahjong Saison', blank=True), + field=models.PositiveSmallIntegerField( + null=True, verbose_name='Mahjong Saison', blank=True), ), migrations.AlterField( model_name='photo', @@ -28,11 +30,13 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='photo', name='on_startpage', - field=models.BooleanField(default=False, help_text='Foto als Teaser auf der Startseite verwenden.', db_index=True, verbose_name='Startseite'), + field=models.BooleanField( + default=False, help_text='Foto als Teaser auf der Startseite verwenden.', db_index=True, verbose_name='Startseite'), ), migrations.AlterField( model_name='photo', name='views', - field=models.PositiveIntegerField(default=0, verbose_name='Wie oft gesehen', editable=False), + field=models.PositiveIntegerField( + default=0, verbose_name='Wie oft gesehen', editable=False), ), ] diff --git a/src/events/migrations/0007_auto_20161012_2224.py b/src/events/migrations/0007_auto_20161012_2224.py index 1d9d4be..ed5a5dd 100644 --- a/src/events/migrations/0007_auto_20161012_2224.py +++ b/src/events/migrations/0007_auto_20161012_2224.py @@ -16,34 +16,40 @@ class Migration(migrations.Migration): migrations.AddField( model_name='event', name='date_created', - field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='first created at', db_index=True), + field=models.DateTimeField( + auto_now_add=True, null=True, verbose_name='first created at', db_index=True), ), migrations.AddField( model_name='event', name='date_modified', - field=models.DateTimeField(default=datetime.datetime(2016, 10, 12, 20, 24, 39, 910492, tzinfo=utc), verbose_name='latest updated at', auto_now=True), + field=models.DateTimeField(default=datetime.datetime( + 2016, 10, 12, 20, 24, 39, 910492, tzinfo=utc), verbose_name='latest updated at', auto_now=True), preserve_default=False, ), migrations.AddField( model_name='location', name='date_created', - field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='first created at', db_index=True), + field=models.DateTimeField( + auto_now_add=True, null=True, verbose_name='first created at', db_index=True), ), migrations.AddField( model_name='location', name='date_modified', - field=models.DateTimeField(default=datetime.datetime(2016, 10, 12, 20, 24, 44, 566305, tzinfo=utc), verbose_name='latest updated at', auto_now=True), + field=models.DateTimeField(default=datetime.datetime( + 2016, 10, 12, 20, 24, 44, 566305, tzinfo=utc), verbose_name='latest updated at', auto_now=True), preserve_default=False, ), migrations.AddField( model_name='photo', name='date_created', - field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='first created at', db_index=True), + field=models.DateTimeField( + auto_now_add=True, null=True, verbose_name='first created at', db_index=True), ), migrations.AddField( model_name='photo', name='date_modified', - field=models.DateTimeField(default=datetime.datetime(2016, 10, 12, 20, 24, 50, 509970, tzinfo=utc), verbose_name='latest updated at', auto_now=True), + field=models.DateTimeField(default=datetime.datetime( + 2016, 10, 12, 20, 24, 50, 509970, tzinfo=utc), verbose_name='latest updated at', auto_now=True), preserve_default=False, ), ] diff --git a/src/events/models.py b/src/events/models.py index 1f608e8..a9e0a73 100644 --- a/src/events/models.py +++ b/src/events/models.py @@ -346,7 +346,6 @@ class Photo(models.Model): )[0] except IndexError: return None - return self.get_next_by_created_date(event=self.event) @property def previous_photo(self): @@ -366,4 +365,3 @@ class Photo(models.Model): )[0] except IndexError: return None - return self.get_previous_by_created_date(event=self.event) diff --git a/src/events/sitemaps.py b/src/events/sitemaps.py index b6c91e4..2682e05 100644 --- a/src/events/sitemaps.py +++ b/src/events/sitemaps.py @@ -1,11 +1,11 @@ from django.contrib.sitemaps import Sitemap from django.utils import timezone -from .models import Event, Photo +from .models import Event +from .models import Photo class EventSitemap(Sitemap): changefreq = "never" - priority = 0.6 protocol = 'https' def items(self): @@ -13,9 +13,8 @@ class EventSitemap(Sitemap): def priority(self, event): delta = timezone.now() - event.start - delta = abs(delta.days / 300.0 ) + delta = abs(delta.days / 300.0) return max(1 - delta, 0.1) def lastmod(self, event): return event.date_modified - diff --git a/src/events/templates/events/event_form.html b/src/events/templates/events/event_form.html index 0d2a21b..55ff52a 100755 --- a/src/events/templates/events/event_form.html +++ b/src/events/templates/events/event_form.html @@ -12,7 +12,6 @@

    Achtung! Das ist eine Veranstaltungsreihe! Diese kann man im Moment nur im Admin-Interface vernünfig bearbeiten.
    Du bearbeitest hier den "Hauptevent" der Reihe ({{event.event_set.count}}). Alle Änderungen (abgesehen von Name, Start und Ende) werden von den darauf folgendem Veranstaltungen übernommen. -

    {% endif %}

    diff --git a/src/events/templates/events/photo_upload.html b/src/events/templates/events/photo_upload.html index 6165b78..0db68d4 100755 --- a/src/events/templates/events/photo_upload.html +++ b/src/events/templates/events/photo_upload.html @@ -35,7 +35,7 @@ {%trans "Upload" %} {% endif %}

    - + {% endfor %} diff --git a/src/events/views.py b/src/events/views.py index aa371af..95076a4 100644 --- a/src/events/views.py +++ b/src/events/views.py @@ -6,7 +6,8 @@ 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 -from extra_views import ModelFormSetView, InlineFormSetView +from extra_views import InlineFormSetView +from extra_views import ModelFormSetView from django.http import HttpResponse, Http404 from django.shortcuts import redirect from django.utils import timezone diff --git a/src/kasu/locale/de/LC_MESSAGES/django.po b/src/kasu/locale/de/LC_MESSAGES/django.po index 4119c0c..c20cde8 100644 --- a/src/kasu/locale/de/LC_MESSAGES/django.po +++ b/src/kasu/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: kasu.utils\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-09-28 00:25+0200\n" +"POT-Creation-Date: 2017-05-10 23:16+0200\n" "PO-Revision-Date: 2016-09-28 00:24+0200\n" "Last-Translator: Christian Berg \n" "Language-Team: Kasu \n" @@ -19,175 +19,174 @@ msgstr "" "X-Generator: Poedit 1.8.9\n" "X-Translated-Using: django-rosetta 0.7.6\n" -#: src/kasu/settings.py:160 +#: kasu/settings.py:160 msgid "German" msgstr "Deutsch" -#: src/kasu/settings.py:160 +#: kasu/settings.py:160 msgid "English" msgstr "Englisch" -#: src/kasu/templates/404.html:8 +#: kasu/templates/404.html:8 msgid "The page your requested does not exist on this server." msgstr "Die angeforderte Seite existiert auf diesem Server nicht." -#: src/kasu/templates/base.html:22 +#: kasu/templates/base.html:22 msgid "Current News" msgstr "Aktuelle Neuigkeiten" -#: src/kasu/templates/base.html:24 src/kasu/templates/index.html:40 +#: kasu/templates/base.html:24 kasu/templates/index.html:40 msgid "Recent Comments" msgstr "Kürzliche Kommentare" -#: src/kasu/templates/base.html:44 +#: kasu/templates/base.html:59 msgid "Menu" msgstr "Menü" -#: src/kasu/templates/base.html:65 +#: kasu/templates/base.html:80 msgid "Current Event" msgstr "Aktuelle Veranstaltung" -#: src/kasu/templates/base.html:68 +#: kasu/templates/base.html:83 msgid "Since" msgstr "seit" -#: src/kasu/templates/base.html:69 src/kasu/templates/base.html.py:81 +#: kasu/templates/base.html:84 kasu/templates/base.html:96 msgid "Start" msgstr "Beginn" -#: src/kasu/templates/base.html:72 src/kasu/templates/base.html.py:84 +#: kasu/templates/base.html:87 kasu/templates/base.html:99 msgid "Location" msgstr "Ort" -#: src/kasu/templates/base.html:75 src/kasu/templates/base.html.py:86 +#: kasu/templates/base.html:90 kasu/templates/base.html:101 msgid "More Details" msgstr "Mehr Details" -#: src/kasu/templates/base.html:77 +#: kasu/templates/base.html:92 msgid "Next Event" msgstr "Nächste Veranstaltung" -#: src/kasu/templates/base.html:80 +#: kasu/templates/base.html:95 msgid "in" msgstr "in" -#: src/kasu/templates/base.html:89 +#: kasu/templates/base.html:104 msgid "Upcoming events" msgstr "Bevorstehende Veranstaltungen" -#: src/kasu/templates/base.html:139 +#: kasu/templates/base.html:154 msgid "Add Subpage" msgstr "Unterseite Hinzufügen" -#: src/kasu/templates/base.html:144 +#: kasu/templates/base.html:159 msgid "Edit Page" msgstr "Seite bearbeiten" -#: src/kasu/templates/base.html:152 +#: kasu/templates/base.html:167 msgid "Imprint" msgstr "Impressum" -#: src/kasu/templates/base.html:153 +#: kasu/templates/base.html:168 msgid "contact" msgstr "Kontakt" -#: src/kasu/templates/base.html:158 +#: kasu/templates/base.html:173 msgid "Language" msgstr "Sprache" -#: src/kasu/templates/base.html:167 +#: kasu/templates/base.html:182 msgid "Go" msgstr "Los" -#: src/kasu/templates/base.html:172 +#: kasu/templates/base.html:187 msgid "Logged in as" msgstr "Angemeldet als" -#: src/kasu/templates/base.html:174 +#: kasu/templates/base.html:189 msgid "Admin" msgstr "Admin" -#: src/kasu/templates/base.html:175 +#: kasu/templates/base.html:190 msgid "Logout" msgstr "Abmelden" -#: src/kasu/templates/base.html:177 +#: kasu/templates/base.html:192 msgid "no user logged in" msgstr "Niemand angemeldet" -#: src/kasu/templates/base.html:178 src/kasu/templates/comments/form.html:43 +#: kasu/templates/base.html:193 kasu/templates/comments/form.html:43 msgid "register" msgstr "Registrieren" -#: src/kasu/templates/base.html:179 src/kasu/templates/comments/form.html:44 +#: kasu/templates/base.html:194 kasu/templates/comments/form.html:44 msgid "login" msgstr "anmelden" -#: src/kasu/templates/base.html:181 +#: kasu/templates/base.html:196 msgid "Login with Facebook" msgstr "über Facebook anmelden" -#: src/kasu/templates/base.html:183 +#: kasu/templates/base.html:198 msgid "Login with Twitter" msgstr "über Twitter anmelden" -#: src/kasu/templates/base.html:185 +#: kasu/templates/base.html:200 msgid "Login with Google" msgstr "über Google anmelden" -#: src/kasu/templates/comments/form.html:5 +#: kasu/templates/comments/form.html:5 msgid "New Comment" msgstr "Neuer Kommentar" -#: src/kasu/templates/comments/form.html:20 +#: kasu/templates/comments/form.html:20 msgid "now" msgstr "Jetzt" -#: src/kasu/templates/comments/form.html:25 +#: kasu/templates/comments/form.html:25 msgid "Preview" msgstr "Vorschau" -#: src/kasu/templates/comments/form.html:26 +#: kasu/templates/comments/form.html:26 msgid "Post" msgstr "Schreiben" -#: src/kasu/templates/comments/form.html:34 +#: kasu/templates/comments/form.html:34 msgid "not logged in" msgstr "Nicht angemeldet" -#: src/kasu/templates/comments/form.html:38 +#: kasu/templates/comments/form.html:38 msgid "Register now, or Login to leave a comment here." msgstr "Jetzt registrieren, oder anmelden um einen Kommentar zu schreiben." -#: src/kasu/templates/comments/list.html:2 src/kasu/templates/index.html:25 +#: kasu/templates/comments/list.html:2 kasu/templates/index.html:25 msgid "Comments" msgstr "Kommentare" -#: src/kasu/templates/comments/posted.html:4 -#: src/kasu/templates/comments/posted.html:7 +#: kasu/templates/comments/posted.html:4 kasu/templates/comments/posted.html:7 msgid "Thank you for your comment" msgstr "Danke für deinen Kommentar." -#: src/kasu/templates/comments/preview.html:4 -#: src/kasu/templates/comments/preview.html:6 +#: kasu/templates/comments/preview.html:4 +#: kasu/templates/comments/preview.html:6 msgid "Preview your comment" msgstr "Vorschau deines Kommentars" -#: src/kasu/templates/comments/preview.html:10 +#: kasu/templates/comments/preview.html:10 msgid "Please correct the error below" msgid_plural "Please correct the errors below" msgstr[0] "Bitte den Fehler weiter unten beheben" msgstr[1] "Bitte die Fehler weiter unten beheben" -#: src/kasu/templates/index.html:4 +#: kasu/templates/index.html:4 msgid "traditional Asian game culture" msgstr "traditionelle asiatische Spielkultur" -#: src/kasu/templates/index.html:33 +#: kasu/templates/index.html:33 msgid "Read More" msgstr "Mehr lesen" -#: src/kasu/templates/index.html:47 +#: kasu/templates/index.html:47 #, python-format msgid "" "\n" @@ -204,1019 +203,1019 @@ msgstr "" " \n" " " -#: src/kasu/templates/index.html:59 +#: kasu/templates/index.html:59 msgid "Kasu in the social network" msgstr "Kasu im sozialem Netzwerk" -#: src/kasu/templates/index.html:62 src/kasu/templates/index.html.py:65 +#: kasu/templates/index.html:62 kasu/templates/index.html:65 msgid "Visit us on" msgstr "Besuche uns auf" -#: src/kasu/templates/index.html:74 +#: kasu/templates/index.html:74 msgid "Add Article" msgstr "Artikel hinzufügen" -#: src/kasu/templates/paginator.html:4 +#: kasu/templates/paginator.html:4 msgid "Previous" msgstr "Vorherige" -#: src/kasu/templates/paginator.html:12 +#: kasu/templates/paginator.html:12 msgid "Next" msgstr "Nächste" -#: src/kasu/utils.py:19 +#: kasu/utils.py:19 msgid "United Kingdom" msgstr "Vereinigtes Königreich" -#: src/kasu/utils.py:20 +#: kasu/utils.py:20 msgid "Afghanistan" msgstr "Afghanistan" -#: src/kasu/utils.py:21 +#: kasu/utils.py:21 msgid "Aland Islands" msgstr "Aland Islands" -#: src/kasu/utils.py:22 +#: kasu/utils.py:22 msgid "Albania" msgstr "Albanien" -#: src/kasu/utils.py:23 +#: kasu/utils.py:23 msgid "Algeria" msgstr "Algerien" -#: src/kasu/utils.py:24 +#: kasu/utils.py:24 msgid "American Samoa" msgstr "Amerikanisch-Samoa" -#: src/kasu/utils.py:25 +#: kasu/utils.py:25 msgid "Andorra" msgstr "Andorra" -#: src/kasu/utils.py:26 +#: kasu/utils.py:26 msgid "Angola" msgstr "Angola" -#: src/kasu/utils.py:27 +#: kasu/utils.py:27 msgid "Anguilla" msgstr "Anguilla" -#: src/kasu/utils.py:28 +#: kasu/utils.py:28 msgid "Antarctica" msgstr "Antarktika" -#: src/kasu/utils.py:29 +#: kasu/utils.py:29 msgid "Antigua and Barbuda" msgstr "Antigua und Barbuda" -#: src/kasu/utils.py:30 +#: kasu/utils.py:30 msgid "Argentina" msgstr "Argentinien" -#: src/kasu/utils.py:31 +#: kasu/utils.py:31 msgid "Armenia" msgstr "Armenien" -#: src/kasu/utils.py:32 +#: kasu/utils.py:32 msgid "Aruba" msgstr "Aruba" -#: src/kasu/utils.py:33 +#: kasu/utils.py:33 msgid "Australia" msgstr "Australien" -#: src/kasu/utils.py:34 +#: kasu/utils.py:34 msgid "Austria" msgstr "Österreich" -#: src/kasu/utils.py:35 +#: kasu/utils.py:35 msgid "Azerbaijan" msgstr "Aserbaidschan" -#: src/kasu/utils.py:36 +#: kasu/utils.py:36 msgid "Bahamas" msgstr "Bahamas" -#: src/kasu/utils.py:37 +#: kasu/utils.py:37 msgid "Bahrain" msgstr "Bahrein" -#: src/kasu/utils.py:38 +#: kasu/utils.py:38 msgid "Bangladesh" msgstr "Bangladesch" -#: src/kasu/utils.py:39 +#: kasu/utils.py:39 msgid "Barbados" msgstr "Barbados" -#: src/kasu/utils.py:40 +#: kasu/utils.py:40 msgid "Belarus" msgstr "Weißrussland" -#: src/kasu/utils.py:41 +#: kasu/utils.py:41 msgid "Belgium" msgstr "Belgien" -#: src/kasu/utils.py:42 +#: kasu/utils.py:42 msgid "Belize" msgstr "Belize" -#: src/kasu/utils.py:43 +#: kasu/utils.py:43 msgid "Benin" msgstr "Benin" -#: src/kasu/utils.py:44 +#: kasu/utils.py:44 msgid "Bermuda" msgstr "Bermuda" -#: src/kasu/utils.py:45 +#: kasu/utils.py:45 msgid "Bhutan" msgstr "Bhutan" -#: src/kasu/utils.py:46 +#: kasu/utils.py:46 msgid "Bolivia" msgstr "Bolivien" -#: src/kasu/utils.py:47 +#: kasu/utils.py:47 msgid "Bosnia and Herzegovina" msgstr "Bosnien und Herzegowina" -#: src/kasu/utils.py:48 +#: kasu/utils.py:48 msgid "Botswana" msgstr "Botswana" -#: src/kasu/utils.py:49 +#: kasu/utils.py:49 msgid "Bouvet Island" msgstr "Bouvet Island" -#: src/kasu/utils.py:50 +#: kasu/utils.py:50 msgid "Brazil" msgstr "Brasilien" -#: src/kasu/utils.py:51 +#: kasu/utils.py:51 msgid "British Indian Ocean Territory" msgstr "British Indian Ocean Territory" -#: src/kasu/utils.py:52 +#: kasu/utils.py:52 msgid "Brunei Darussalam" msgstr "Brunei Darussalam" -#: src/kasu/utils.py:53 +#: kasu/utils.py:53 msgid "Bulgaria" msgstr "Bulgarien" -#: src/kasu/utils.py:54 +#: kasu/utils.py:54 msgid "Burkina Faso" msgstr "Burkina Faso" -#: src/kasu/utils.py:55 +#: kasu/utils.py:55 msgid "Burundi" msgstr "Burundi" -#: src/kasu/utils.py:56 +#: kasu/utils.py:56 msgid "Cambodia" msgstr "Kambodscha" -#: src/kasu/utils.py:57 +#: kasu/utils.py:57 msgid "Cameroon" msgstr "Kamerun" -#: src/kasu/utils.py:58 +#: kasu/utils.py:58 msgid "Canada" msgstr "Kanada" -#: src/kasu/utils.py:59 +#: kasu/utils.py:59 msgid "Cape Verde" msgstr "Cape Verde" -#: src/kasu/utils.py:60 +#: kasu/utils.py:60 msgid "Cayman Islands" msgstr "Cayman Islands" -#: src/kasu/utils.py:61 +#: kasu/utils.py:61 msgid "Central African Republic" msgstr "Zentralafrikanische Republik" -#: src/kasu/utils.py:62 +#: kasu/utils.py:62 msgid "Chad" msgstr "Tschad" -#: src/kasu/utils.py:63 +#: kasu/utils.py:63 msgid "Chile" msgstr "Chile" -#: src/kasu/utils.py:64 +#: kasu/utils.py:64 msgid "China" msgstr "China" -#: src/kasu/utils.py:65 +#: kasu/utils.py:65 msgid "Christmas Island" msgstr "Christmas Island" -#: src/kasu/utils.py:66 +#: kasu/utils.py:66 msgid "Cocos (Keeling) Islands" msgstr "Cocos (Keeling) Islands" -#: src/kasu/utils.py:67 +#: kasu/utils.py:67 msgid "Colombia" msgstr "Kolumbien" -#: src/kasu/utils.py:68 +#: kasu/utils.py:68 msgid "Comoros" msgstr "Komoren" -#: src/kasu/utils.py:69 +#: kasu/utils.py:69 msgid "Congo" msgstr "Kongo" -#: src/kasu/utils.py:70 +#: kasu/utils.py:70 msgid "Congo, The Democratic Republic of the" msgstr "Kongo, Demokratische Republik" -#: src/kasu/utils.py:71 +#: kasu/utils.py:71 msgid "Cook Islands" msgstr "Cook-Inseln" -#: src/kasu/utils.py:72 +#: kasu/utils.py:72 msgid "Costa Rica" msgstr "Costa Rica" -#: src/kasu/utils.py:73 +#: kasu/utils.py:73 msgid "Cote d'Ivoire" msgstr "Cote d'Ivoire" -#: src/kasu/utils.py:74 +#: kasu/utils.py:74 msgid "Croatia" msgstr "Kroatien" -#: src/kasu/utils.py:75 +#: kasu/utils.py:75 msgid "Cuba" msgstr "Kuba" -#: src/kasu/utils.py:76 +#: kasu/utils.py:76 msgid "Cyprus" msgstr "Zypern" -#: src/kasu/utils.py:77 +#: kasu/utils.py:77 msgid "Czech Republic" msgstr "Tschechische Republik" -#: src/kasu/utils.py:78 +#: kasu/utils.py:78 msgid "Denmark" msgstr "Dänemark" -#: src/kasu/utils.py:79 +#: kasu/utils.py:79 msgid "Djibouti" msgstr "Dschibuti" -#: src/kasu/utils.py:80 +#: kasu/utils.py:80 msgid "Dominica" msgstr "Dominica" -#: src/kasu/utils.py:81 +#: kasu/utils.py:81 msgid "Dominican Republic" msgstr "Dominikanische Republik" -#: src/kasu/utils.py:82 +#: kasu/utils.py:82 msgid "Ecuador" msgstr "Ecuador" -#: src/kasu/utils.py:83 +#: kasu/utils.py:83 msgid "Egypt" msgstr "Ägypten" -#: src/kasu/utils.py:84 +#: kasu/utils.py:84 msgid "El Salvador" msgstr "El Salvador" -#: src/kasu/utils.py:85 +#: kasu/utils.py:85 msgid "Equatorial Guinea" msgstr "Äquatorial-Guinea" -#: src/kasu/utils.py:86 +#: kasu/utils.py:86 msgid "Eritrea" msgstr "Eritrea" -#: src/kasu/utils.py:87 +#: kasu/utils.py:87 msgid "Estonia" msgstr "Estland" -#: src/kasu/utils.py:88 +#: kasu/utils.py:88 msgid "Ethiopia" msgstr "Äthiopien" -#: src/kasu/utils.py:89 +#: kasu/utils.py:89 msgid "Falkland Islands (Malvinas)" msgstr "Falklandinseln (Malvinas)" -#: src/kasu/utils.py:90 +#: kasu/utils.py:90 msgid "Faroe Islands" msgstr "Färöer-Inseln" -#: src/kasu/utils.py:91 +#: kasu/utils.py:91 msgid "Fiji" msgstr "Fidschi" -#: src/kasu/utils.py:92 +#: kasu/utils.py:92 msgid "Finland" msgstr "Finnland" -#: src/kasu/utils.py:93 +#: kasu/utils.py:93 msgid "France" msgstr "Frankreich" -#: src/kasu/utils.py:94 +#: kasu/utils.py:94 msgid "French Guiana" msgstr "Französisch-Guayana" -#: src/kasu/utils.py:95 +#: kasu/utils.py:95 msgid "French Polynesia" msgstr "Französisch-Polynesien" -#: src/kasu/utils.py:96 +#: kasu/utils.py:96 msgid "French Southern Territories" msgstr "Französisch Südliche Territorien" -#: src/kasu/utils.py:97 +#: kasu/utils.py:97 msgid "Gabon" msgstr "Gabun" -#: src/kasu/utils.py:98 +#: kasu/utils.py:98 msgid "Gambia" msgstr "Gambia" -#: src/kasu/utils.py:99 +#: kasu/utils.py:99 msgid "Georgia" msgstr "Georgia" -#: src/kasu/utils.py:100 +#: kasu/utils.py:100 msgid "Germany" msgstr "Deutschland" -#: src/kasu/utils.py:101 +#: kasu/utils.py:101 msgid "Ghana" msgstr "Ghana" -#: src/kasu/utils.py:102 +#: kasu/utils.py:102 msgid "Gibraltar" msgstr "Gibraltar" -#: src/kasu/utils.py:103 +#: kasu/utils.py:103 msgid "Greece" msgstr "Griechenland" -#: src/kasu/utils.py:104 +#: kasu/utils.py:104 msgid "Greenland" msgstr "Grönland" -#: src/kasu/utils.py:105 +#: kasu/utils.py:105 msgid "Grenada" msgstr "Grenada" -#: src/kasu/utils.py:106 +#: kasu/utils.py:106 msgid "Guadeloupe" msgstr "Guadeloupe" -#: src/kasu/utils.py:107 +#: kasu/utils.py:107 msgid "Guam" msgstr "Guam" -#: src/kasu/utils.py:108 +#: kasu/utils.py:108 msgid "Guatemala" msgstr "Guatemala" -#: src/kasu/utils.py:109 +#: kasu/utils.py:109 msgid "Guernsey" msgstr "Guernsey" -#: src/kasu/utils.py:110 +#: kasu/utils.py:110 msgid "Guinea" msgstr "Guinea" -#: src/kasu/utils.py:111 +#: kasu/utils.py:111 msgid "Guinea-Bissau" msgstr "Guinea-Bissau" -#: src/kasu/utils.py:112 +#: kasu/utils.py:112 msgid "Guyana" msgstr "Guyana" -#: src/kasu/utils.py:113 +#: kasu/utils.py:113 msgid "Haiti" msgstr "Haiti" -#: src/kasu/utils.py:114 +#: kasu/utils.py:114 msgid "Heard Island and McDonald Islands" msgstr "Heard und McDonald Inseln" -#: src/kasu/utils.py:115 +#: kasu/utils.py:115 msgid "Holy See (Vatican City State)" msgstr "Heiliger Stuhl (Vatikanstadt)" -#: src/kasu/utils.py:116 +#: kasu/utils.py:116 msgid "Honduras" msgstr "Honduras" -#: src/kasu/utils.py:117 +#: kasu/utils.py:117 msgid "Hong Kong" msgstr "Hongkong" -#: src/kasu/utils.py:118 +#: kasu/utils.py:118 msgid "Hungary" msgstr "Ungarn" -#: src/kasu/utils.py:119 +#: kasu/utils.py:119 msgid "Iceland" msgstr "Island" -#: src/kasu/utils.py:120 +#: kasu/utils.py:120 msgid "India" msgstr "Indien" -#: src/kasu/utils.py:121 +#: kasu/utils.py:121 msgid "Indonesia" msgstr "Indonesien" -#: src/kasu/utils.py:122 +#: kasu/utils.py:122 msgid "Iran, Islamic Republic of" msgstr "Iran, Islamische Republik" -#: src/kasu/utils.py:123 +#: kasu/utils.py:123 msgid "Iraq" msgstr "Irak" -#: src/kasu/utils.py:124 +#: kasu/utils.py:124 msgid "Ireland" msgstr "Irland" -#: src/kasu/utils.py:125 +#: kasu/utils.py:125 msgid "Isle of Man" msgstr "Isle of Man" -#: src/kasu/utils.py:126 +#: kasu/utils.py:126 msgid "Israel" msgstr "Israel" -#: src/kasu/utils.py:127 +#: kasu/utils.py:127 msgid "Italy" msgstr "Italien" -#: src/kasu/utils.py:128 +#: kasu/utils.py:128 msgid "Jamaica" msgstr "Jamaika" -#: src/kasu/utils.py:129 +#: kasu/utils.py:129 msgid "Japan" msgstr "Japan" -#: src/kasu/utils.py:130 +#: kasu/utils.py:130 msgid "Jersey" msgstr "Jersey" -#: src/kasu/utils.py:131 +#: kasu/utils.py:131 msgid "Jordan" msgstr "Jordan" -#: src/kasu/utils.py:132 +#: kasu/utils.py:132 msgid "Kazakhstan" msgstr "Kasachstan" -#: src/kasu/utils.py:133 +#: kasu/utils.py:133 msgid "Kenya" msgstr "Kenia" -#: src/kasu/utils.py:134 +#: kasu/utils.py:134 msgid "Kiribati" msgstr "Kiribati" -#: src/kasu/utils.py:135 +#: kasu/utils.py:135 msgid "Korea, Democratic People's Republic of" msgstr "Korea, Demokratische Volksrepublik" -#: src/kasu/utils.py:136 +#: kasu/utils.py:136 msgid "Korea, Republic of" msgstr "Korea, Republik" -#: src/kasu/utils.py:137 +#: kasu/utils.py:137 msgid "Kuwait" msgstr "Kuwait" -#: src/kasu/utils.py:138 +#: kasu/utils.py:138 msgid "Kyrgyzstan" msgstr "Kirgisistan" -#: src/kasu/utils.py:139 +#: kasu/utils.py:139 msgid "Lao People's Democratic Republic" msgstr "Lao Demokratischen Volksrepublik" -#: src/kasu/utils.py:140 +#: kasu/utils.py:140 msgid "Latvia" msgstr "Lettland" -#: src/kasu/utils.py:141 +#: kasu/utils.py:141 msgid "Lebanon" msgstr "Libanon" -#: src/kasu/utils.py:142 +#: kasu/utils.py:142 msgid "Lesotho" msgstr "Lesotho" -#: src/kasu/utils.py:143 +#: kasu/utils.py:143 msgid "Liberia" msgstr "Liberia" -#: src/kasu/utils.py:144 +#: kasu/utils.py:144 msgid "Libyan Arab Jamahiriya" msgstr "Libyen" -#: src/kasu/utils.py:145 +#: kasu/utils.py:145 msgid "Liechtenstein" msgstr "Liechtenstein" -#: src/kasu/utils.py:146 +#: kasu/utils.py:146 msgid "Lithuania" msgstr "Litauen" -#: src/kasu/utils.py:147 +#: kasu/utils.py:147 msgid "Luxembourg" msgstr "Luxemburg" -#: src/kasu/utils.py:148 +#: kasu/utils.py:148 msgid "Macao" msgstr "Macao" -#: src/kasu/utils.py:149 +#: kasu/utils.py:149 msgid "Macedonia, The Former Yugoslav Republic of" msgstr "Mazedonien, die ehemalige jugoslawische Republik" -#: src/kasu/utils.py:150 +#: kasu/utils.py:150 msgid "Madagascar" msgstr "Madagaskar" -#: src/kasu/utils.py:151 +#: kasu/utils.py:151 msgid "Malawi" msgstr "Malawi" -#: src/kasu/utils.py:152 +#: kasu/utils.py:152 msgid "Malaysia" msgstr "Malaysia" -#: src/kasu/utils.py:153 +#: kasu/utils.py:153 msgid "Maldives" msgstr "Malediven" -#: src/kasu/utils.py:154 +#: kasu/utils.py:154 msgid "Mali" msgstr "Mali" -#: src/kasu/utils.py:155 +#: kasu/utils.py:155 msgid "Malta" msgstr "Malta" -#: src/kasu/utils.py:156 +#: kasu/utils.py:156 msgid "Marshall Islands" msgstr "Marshall Islands" -#: src/kasu/utils.py:157 +#: kasu/utils.py:157 msgid "Martinique" msgstr "Martinique" -#: src/kasu/utils.py:158 +#: kasu/utils.py:158 msgid "Mauritania" msgstr "Mauretanien" -#: src/kasu/utils.py:159 +#: kasu/utils.py:159 msgid "Mauritius" msgstr "Mauritius" -#: src/kasu/utils.py:160 +#: kasu/utils.py:160 msgid "Mayotte" msgstr "Mayotte" -#: src/kasu/utils.py:161 +#: kasu/utils.py:161 msgid "Mexico" msgstr "Mexiko" -#: src/kasu/utils.py:162 +#: kasu/utils.py:162 msgid "Micronesia, Federated States of" msgstr "Mikronesien, Föderierte Staaten von" -#: src/kasu/utils.py:163 +#: kasu/utils.py:163 msgid "Moldova" msgstr "Moldawien" -#: src/kasu/utils.py:164 +#: kasu/utils.py:164 msgid "Monaco" msgstr "Monaco" -#: src/kasu/utils.py:165 +#: kasu/utils.py:165 msgid "Mongolia" msgstr "Mongolei" -#: src/kasu/utils.py:166 +#: kasu/utils.py:166 msgid "Montenegro" msgstr "Montenegro" -#: src/kasu/utils.py:167 +#: kasu/utils.py:167 msgid "Montserrat" msgstr "Montserrat" -#: src/kasu/utils.py:168 +#: kasu/utils.py:168 msgid "Morocco" msgstr "Marokko" -#: src/kasu/utils.py:169 +#: kasu/utils.py:169 msgid "Mozambique" msgstr "Mosambik" -#: src/kasu/utils.py:170 +#: kasu/utils.py:170 msgid "Myanmar" msgstr "Myanmar" -#: src/kasu/utils.py:171 +#: kasu/utils.py:171 msgid "Namibia" msgstr "Namibia" -#: src/kasu/utils.py:172 +#: kasu/utils.py:172 msgid "Nauru" msgstr "Nauru" -#: src/kasu/utils.py:173 +#: kasu/utils.py:173 msgid "Nepal" msgstr "Nepal" -#: src/kasu/utils.py:174 +#: kasu/utils.py:174 msgid "Netherlands" msgstr "Niederlande" -#: src/kasu/utils.py:175 +#: kasu/utils.py:175 msgid "Netherlands Antilles" msgstr "Niederländische Antillen" -#: src/kasu/utils.py:176 +#: kasu/utils.py:176 msgid "New Caledonia" msgstr "Neukaledonien" -#: src/kasu/utils.py:177 +#: kasu/utils.py:177 msgid "New Zealand" msgstr "New Zealand" -#: src/kasu/utils.py:178 +#: kasu/utils.py:178 msgid "Nicaragua" msgstr "Nicaragua" -#: src/kasu/utils.py:179 +#: kasu/utils.py:179 msgid "Niger" msgstr "Niger" -#: src/kasu/utils.py:180 +#: kasu/utils.py:180 msgid "Nigeria" msgstr "Nigeria" -#: src/kasu/utils.py:181 +#: kasu/utils.py:181 msgid "Niue" msgstr "Niue" -#: src/kasu/utils.py:182 +#: kasu/utils.py:182 msgid "Norfolk Island" msgstr "Norfolk Island" -#: src/kasu/utils.py:183 +#: kasu/utils.py:183 msgid "Northern Mariana Islands" msgstr "Northern Mariana Islands" -#: src/kasu/utils.py:184 +#: kasu/utils.py:184 msgid "Norway" msgstr "Norwegen" -#: src/kasu/utils.py:185 +#: kasu/utils.py:185 msgid "Oman" msgstr "Oman" -#: src/kasu/utils.py:186 +#: kasu/utils.py:186 msgid "Pakistan" msgstr "Pakistan" -#: src/kasu/utils.py:187 +#: kasu/utils.py:187 msgid "Palau" msgstr "Palau" -#: src/kasu/utils.py:188 +#: kasu/utils.py:188 msgid "Palestinian Territory, Occupied" msgstr "Palästinensische Autonomiegebiete" -#: src/kasu/utils.py:189 +#: kasu/utils.py:189 msgid "Panama" msgstr "Panama" -#: src/kasu/utils.py:190 +#: kasu/utils.py:190 msgid "Papua New Guinea" msgstr "Papua-Neuguinea" -#: src/kasu/utils.py:191 +#: kasu/utils.py:191 msgid "Paraguay" msgstr "Paraguay" -#: src/kasu/utils.py:192 +#: kasu/utils.py:192 msgid "Peru" msgstr "Peru" -#: src/kasu/utils.py:193 +#: kasu/utils.py:193 msgid "Philippines" msgstr "Philippinen" -#: src/kasu/utils.py:194 +#: kasu/utils.py:194 msgid "Pitcairn" msgstr "Pitcairn" -#: src/kasu/utils.py:195 +#: kasu/utils.py:195 msgid "Poland" msgstr "Polen" -#: src/kasu/utils.py:196 +#: kasu/utils.py:196 msgid "Portugal" msgstr "Portugal" -#: src/kasu/utils.py:197 +#: kasu/utils.py:197 msgid "Puerto Rico" msgstr "Puerto Rico" -#: src/kasu/utils.py:198 +#: kasu/utils.py:198 msgid "Qatar" msgstr "Katar" -#: src/kasu/utils.py:199 +#: kasu/utils.py:199 msgid "Reunion" msgstr "Wiedervereinigung" -#: src/kasu/utils.py:200 +#: kasu/utils.py:200 msgid "Romania" msgstr "Rumänien" -#: src/kasu/utils.py:201 +#: kasu/utils.py:201 msgid "Russian Federation" msgstr "Russischen Föderation" -#: src/kasu/utils.py:202 +#: kasu/utils.py:202 msgid "Rwanda" msgstr "Ruanda" -#: src/kasu/utils.py:203 +#: kasu/utils.py:203 msgid "Saint Barthelemy" msgstr "Saint Barthelemy" -#: src/kasu/utils.py:204 +#: kasu/utils.py:204 msgid "Saint Helena" msgstr "Saint Helena" -#: src/kasu/utils.py:205 +#: kasu/utils.py:205 msgid "Saint Kitts and Nevis" msgstr "Saint Kitts und Nevis" -#: src/kasu/utils.py:206 +#: kasu/utils.py:206 msgid "Saint Lucia" msgstr "Santa Lucia" -#: src/kasu/utils.py:207 +#: kasu/utils.py:207 msgid "Saint Martin" msgstr "Santa Martin" -#: src/kasu/utils.py:208 +#: kasu/utils.py:208 msgid "Saint Pierre and Miquelon" msgstr "Saint Pierre und Miquelon" -#: src/kasu/utils.py:209 +#: kasu/utils.py:209 msgid "Saint Vincent and the Grenadines" msgstr "Saint Vincent und die Grenadinen" -#: src/kasu/utils.py:210 +#: kasu/utils.py:210 msgid "Samoa" msgstr "Samoa" -#: src/kasu/utils.py:211 +#: kasu/utils.py:211 msgid "San Marino" msgstr "San Marino" -#: src/kasu/utils.py:212 +#: kasu/utils.py:212 msgid "Sao Tome and Principe" msgstr "Sao Tome und Principe" -#: src/kasu/utils.py:213 +#: kasu/utils.py:213 msgid "Saudi Arabia" msgstr "Saudi-Arabien" -#: src/kasu/utils.py:214 +#: kasu/utils.py:214 msgid "Senegal" msgstr "Senegal" -#: src/kasu/utils.py:215 +#: kasu/utils.py:215 msgid "Serbia" msgstr "Serbien" -#: src/kasu/utils.py:216 +#: kasu/utils.py:216 msgid "Seychelles" msgstr "Seychellen" -#: src/kasu/utils.py:217 +#: kasu/utils.py:217 msgid "Sierra Leone" msgstr "Sierra Leone" -#: src/kasu/utils.py:218 +#: kasu/utils.py:218 msgid "Singapore" msgstr "Singapur" -#: src/kasu/utils.py:219 +#: kasu/utils.py:219 msgid "Slovakia" msgstr "Slowakei" -#: src/kasu/utils.py:220 +#: kasu/utils.py:220 msgid "Slovenia" msgstr "Slowenien" -#: src/kasu/utils.py:221 +#: kasu/utils.py:221 msgid "Solomon Islands" msgstr "Salomon-Inseln" -#: src/kasu/utils.py:222 +#: kasu/utils.py:222 msgid "Somalia" msgstr "Somalia" -#: src/kasu/utils.py:223 +#: kasu/utils.py:223 msgid "South Africa" msgstr "Südafrika" -#: src/kasu/utils.py:224 +#: kasu/utils.py:224 msgid "South Georgia and the South Sandwich Islands" msgstr "Südgeorgien und die Südlichen Sandwichinseln" -#: src/kasu/utils.py:225 +#: kasu/utils.py:225 msgid "Spain" msgstr "Spanien" -#: src/kasu/utils.py:226 +#: kasu/utils.py:226 msgid "Sri Lanka" msgstr "Sri Lanka" -#: src/kasu/utils.py:227 +#: kasu/utils.py:227 msgid "Sudan" msgstr "Sudan" -#: src/kasu/utils.py:228 +#: kasu/utils.py:228 msgid "Suriname" msgstr "Suriname" -#: src/kasu/utils.py:229 +#: kasu/utils.py:229 msgid "Svalbard and Jan Mayen" msgstr "Svalbard und Jan Mayen" -#: src/kasu/utils.py:230 +#: kasu/utils.py:230 msgid "Swaziland" msgstr "Swaziland" -#: src/kasu/utils.py:231 +#: kasu/utils.py:231 msgid "Sweden" msgstr "Schweden" -#: src/kasu/utils.py:232 +#: kasu/utils.py:232 msgid "Switzerland" msgstr "Schweiz" -#: src/kasu/utils.py:233 +#: kasu/utils.py:233 msgid "Syrian Arab Republic" msgstr "Arabische Republik Syrien" -#: src/kasu/utils.py:234 +#: kasu/utils.py:234 msgid "Taiwan, Province of China" msgstr "Taiwan, Province of China" -#: src/kasu/utils.py:235 +#: kasu/utils.py:235 msgid "Tajikistan" msgstr "Tadschikistan" -#: src/kasu/utils.py:236 +#: kasu/utils.py:236 msgid "Tanzania, United Republic of" msgstr "Tansania, Vereinigte Republik" -#: src/kasu/utils.py:237 +#: kasu/utils.py:237 msgid "Thailand" msgstr "Thailand" -#: src/kasu/utils.py:238 +#: kasu/utils.py:238 msgid "Timor-Leste" msgstr "Timor-Leste" -#: src/kasu/utils.py:239 +#: kasu/utils.py:239 msgid "Togo" msgstr "Togo" -#: src/kasu/utils.py:240 +#: kasu/utils.py:240 msgid "Tokelau" msgstr "Tokelau" -#: src/kasu/utils.py:241 +#: kasu/utils.py:241 msgid "Tonga" msgstr "Tonga" -#: src/kasu/utils.py:242 +#: kasu/utils.py:242 msgid "Trinidad and Tobago" msgstr "Trinidad und Tobago" -#: src/kasu/utils.py:243 +#: kasu/utils.py:243 msgid "Tunisia" msgstr "Tunesien" -#: src/kasu/utils.py:244 +#: kasu/utils.py:244 msgid "Turkey" msgstr "Türkei" -#: src/kasu/utils.py:245 +#: kasu/utils.py:245 msgid "Turkmenistan" msgstr "Turkmenistan" -#: src/kasu/utils.py:246 +#: kasu/utils.py:246 msgid "Turks and Caicos Islands" msgstr "Turks-und Caicosinseln" -#: src/kasu/utils.py:247 +#: kasu/utils.py:247 msgid "Tuvalu" msgstr "Tuvalu" -#: src/kasu/utils.py:248 +#: kasu/utils.py:248 msgid "Uganda" msgstr "Uganda" -#: src/kasu/utils.py:249 +#: kasu/utils.py:249 msgid "Ukraine" msgstr "Ukraine" -#: src/kasu/utils.py:250 +#: kasu/utils.py:250 msgid "United Arab Emirates" msgstr "Vereinigte Arabische Emirate" -#: src/kasu/utils.py:251 +#: kasu/utils.py:251 msgid "United States" msgstr "Vereinigte Staaten" -#: src/kasu/utils.py:252 +#: kasu/utils.py:252 msgid "United States Minor Outlying Islands" msgstr "United States Minor Outlying Islands" -#: src/kasu/utils.py:253 +#: kasu/utils.py:253 msgid "Uruguay" msgstr "Uruguay" -#: src/kasu/utils.py:254 +#: kasu/utils.py:254 msgid "Uzbekistan" msgstr "Usbekistan" -#: src/kasu/utils.py:255 +#: kasu/utils.py:255 msgid "Vanuatu" msgstr "Vanuatu" -#: src/kasu/utils.py:256 +#: kasu/utils.py:256 msgid "Venezuela" msgstr "Venezuela" -#: src/kasu/utils.py:257 +#: kasu/utils.py:257 msgid "Viet Nam" msgstr "Vietnam" -#: src/kasu/utils.py:258 +#: kasu/utils.py:258 msgid "Virgin Islands, British" msgstr "Virgin Islands, British" -#: src/kasu/utils.py:259 +#: kasu/utils.py:259 msgid "Virgin Islands, U.S." msgstr "Virgin Islands, US" -#: src/kasu/utils.py:260 +#: kasu/utils.py:260 msgid "Wallis and Futuna" msgstr "Wallis und Futuna" -#: src/kasu/utils.py:261 +#: kasu/utils.py:261 msgid "Western Sahara" msgstr "Westsahara" -#: src/kasu/utils.py:262 +#: kasu/utils.py:262 msgid "Yemen" msgstr "Jemen" -#: src/kasu/utils.py:263 +#: kasu/utils.py:263 msgid "Zambia" msgstr "Sambia" -#: src/kasu/utils.py:264 +#: kasu/utils.py:264 msgid "Zimbabwe" msgstr "Zimbabwe" -#: src/kasu/utils.py:270 +#: kasu/utils.py:270 msgid "Rejected" msgstr "Zurückgewiesen" -#: src/kasu/utils.py:271 +#: kasu/utils.py:271 msgid "Waiting..." msgstr "Wartend..." -#: src/kasu/utils.py:272 +#: kasu/utils.py:272 msgid "Published" msgstr "Veröffentlicht" diff --git a/src/kasu/settings.py b/src/kasu/settings.py index 4b708c5..f47b1ec 100644 --- a/src/kasu/settings.py +++ b/src/kasu/settings.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- from os import path -gettext = lambda s: s + +def gettext(s): return s + ADMINS = (('Max Mustermann', 'email@example.com'),) ALLOWED_HOSTS = ['.kasu.at'] @@ -39,6 +41,7 @@ PREREQ_APPS = [ 'django.contrib.sitemaps', 'django.contrib.staticfiles', 'django_comments', + 'captcha', 'ckeditor', 'ckeditor_uploader', 'easy_thumbnails', @@ -95,7 +98,7 @@ TEMPLATES = [ 'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages', 'events.context_processors.upcoming_events', -'social_django.context_processors.backends', + 'social_django.context_processors.backends', 'social_django.context_processors.login_redirect' ], 'loaders': [ @@ -108,16 +111,6 @@ TEMPLATES = [ }, ] -#Settings for Security Middleware -CSP_DEFAULT_SRC = ("'self'",) -CSP_IMG_SRC = ("'self'", "'unsafe-eval'") -CSP_SCRIPT_SRC = ("'self'", "'unsafe-inline'", "'unsafe-eval'") -CSP_STYLE_SRC = ("'self'", "'unsafe-inline'") -SECURE_BROWSER_XSS_FILTER = True -SECURE_CONTENT_TYPE_NOSNIFF = True -SECURE_HSTS_INCLUDE_SUBDOMAINS = True -SECURE_HSTS_SECONDS = 31536000 -SECURE_SSL_REDIRECT = True SESSION_COOKIE_DOMAIN = 'kasu.at' # Die ganze Domain Kasu SESSION_COOKIE_AGE = 15768000 # Session dauer: 4 Wochen SESSION_COOKIE_SECURE = True @@ -169,21 +162,21 @@ CKEDITOR_CONFIGS = {'default': { 'width': '100%', 'extraPlugins': 'divarea', 'toolbarGroups': """[ - { name: 'clipboard', groups: [ 'clipboard', 'undo' ] }, - { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] }, + { name: 'clipboard', groups: [ 'clipboard', 'undo' ] }, + { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] }, { name: 'links' }, { name: 'insert' }, { name: 'forms' }, { name: 'tools' }, - { name: 'document', groups: [ 'mode', 'document', 'doctools' ] }, + { name: 'document', groups: [ 'mode', 'document', 'doctools' ] }, { name: 'others' }, '/', { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] }, - { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] }, + { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] }, { name: 'styles' }, { name: 'colors' }, { name: 'about' } - ];""" + ];""" } } @@ -191,11 +184,9 @@ CKEDITOR_CONFIGS = {'default': { FACEBOOK_APP_ID = '' FACEBOOK_ACCESS_TOKEN = '' -# Settings for the redactor WYSIWYG Editor -REDACTOR_OPTIONS = {'lang': 'de', 'linebreaks': True} -REDACTOR_UPLOAD = 'uploads/' - # crendetials for reCaptcha (set in local_settings) +NOCAPTCHA = True +RECAPTCHA_USE_SSL = True RECAPTCHA_PUBLIC_KEY = '' RECAPTCHA_PRIVATE_KEY = '' RECAPTCHA_THEME = 'red' diff --git a/src/kasu/static/js/piwik.js b/src/kasu/static/js/piwik.js index 4003a58..961574a 100644 --- a/src/kasu/static/js/piwik.js +++ b/src/kasu/static/js/piwik.js @@ -1,8 +1,14 @@ -var idSite = 1; -var piwikTrackingApiUrl = 'https://kasu.at/piwik/piwik.php'; - -var _paq = _paq || []; -_paq.push(['setTrackerUrl', piwikTrackingApiUrl]); -_paq.push(['setSiteId', idSite]); -_paq.push(['trackPageView']); -_paq.push(['enableLinkTracking']); +/* Piwik */ + var _paq = _paq || []; + /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ + _paq.push(['trackPageView']); + _paq.push(['enableLinkTracking']); + (function() { + var u="//kasu.at/piwik/"; + _paq.push(['setTrackerUrl', u+'piwik.php']); + _paq.push(['setSiteId', '1']); + var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; + g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); + })(); + +/* End Piwik Code */ \ No newline at end of file diff --git a/src/kasu/templates/404.html b/src/kasu/templates/404.html index be8dd07..dac2bbe 100644 --- a/src/kasu/templates/404.html +++ b/src/kasu/templates/404.html @@ -4,7 +4,7 @@ {% block title %}404 - Seite nicht gefunden{% endblock %} {% block teaser %} -404 - Nōten! +

    404 - Nōten!

    {% trans 'The page your requested does not exist on this server.' %}

    {% endblock %} diff --git a/src/kasu/templates/base.html b/src/kasu/templates/base.html index e9f1fd9..49f65ce 100644 --- a/src/kasu/templates/base.html +++ b/src/kasu/templates/base.html @@ -35,21 +35,7 @@ {% endblock %} {% block extra_head %}{% endblock %} - - - +
    - {% endblock %} diff --git a/src/membership/urls.py b/src/membership/urls.py index f46ea6f..fe65be6 100644 --- a/src/membership/urls.py +++ b/src/membership/urls.py @@ -4,7 +4,8 @@ Created on 03.10.2011 @author: christian """ import django.contrib.auth.views as auth_views -from django.conf.urls import url, include +from django.conf.urls import include +from django.conf.urls import url from . import views diff --git a/src/membership/views.py b/src/membership/views.py index cd2018d..08ec84c 100644 --- a/src/membership/views.py +++ b/src/membership/views.py @@ -12,10 +12,14 @@ from mahjong_ranking.models import KyuDanRanking, SeasonRanking from utils import mixins from . import forms, models RECAPTCHA_CSP = { - 'SCRIPT_SRC': ['https://www.google.com/recaptcha/', 'https://www.gstatic.com/recaptcha/'], - 'FRAME_SRC': 'https://www.google.com/recaptcha/', - 'STYLE_SRC': "'unsafe-inline'" + 'SCRIPT_SRC': ( + 'https://www.google.com/recaptcha/', + 'https://www.gstatic.com/recaptcha/' + ), + 'CHILD_SRC': ('https://www.google.com/recaptcha/',) } + + class ActivateRegistration(generic.DetailView): """ Activates the Registration of an User and logs him in diff --git a/src/utils/__init__.py b/src/utils/__init__.py index e276d4d..1803d2d 100644 --- a/src/utils/__init__.py +++ b/src/utils/__init__.py @@ -11,13 +11,14 @@ from .html_cleaner import HtmlCleaner from .massmailer import MassMailer +CLEANER = HtmlCleaner() STATUS_REJECTED, STATUS_WAITING, STATUS_PUBLISHED = -1, 0, 1 STATUS_CHOICES = ( (STATUS_REJECTED, _('Rejected')), (STATUS_WAITING, _('Waiting...')), (STATUS_PUBLISHED, _('Published')), ) -cleaner = HtmlCleaner() + class OverwriteStorage(FileSystemStorage): diff --git a/src/utils/forms.py b/src/utils/forms.py deleted file mode 100644 index dea314c..0000000 --- a/src/utils/forms.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -Created on 24.11.2011 - -@author: christian -""" -import datetime - -from django import forms - - -class DateInput(forms.widgets.DateInput): - input_type = 'date' - attrs = {'class': 'dateinput'} - - def __init__(self, attrs=None, **kwargs): - forms.widgets.DateInput.__init__(self, - attrs=attrs, - format='%Y-%m-%d', - **kwargs) - - -class NumberInput(forms.widgets.Input): - input_type = 'number' - - -class TimeInput(forms.widgets.Select): - - def __init__(self, attrs=None, ): - timeset = datetime.datetime(2000, 1, 1, 0, 0) - choices = [('', '-------')] - while timeset < datetime.datetime(2000, 1, 2, 0, 0): - choices.append(( - timeset.strftime('%H:%M:%S'), - timeset.strftime('%H:%M') - )) - timeset += datetime.timedelta(minutes=30) - forms.widgets.Select.__init__(self, attrs=attrs, choices=choices) - - def render(self, name, value, attrs=None, choices=()): - return forms.widgets.Select.render(self, name, value, attrs=attrs, - choices=choices) - - -class SplitDateTimeWidget(forms.widgets.MultiWidget): - """ - A Widget that splits datetime input into two boxes. - """ - - def __init__(self, attrs=None, date_format='%Y-%m-%d'): - widgets = ( - DateInput(attrs=attrs, format=date_format), - TimeInput(attrs=attrs) - ) - super(SplitDateTimeWidget, self).__init__(widgets, attrs) - - def decompress(self, value): - if value: - return [value.date(), value.time()] - return [None, None] diff --git a/src/utils/html5/__init__.py b/src/utils/html5/__init__.py deleted file mode 100644 index 1d43238..0000000 --- a/src/utils/html5/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/python -from . import base - -registry = base.LookupRegistry() - - -def url_autodiscover(): - import copy - from django.conf import settings - from django.utils.importlib import import_module - from django.utils.module_loading import module_has_submodule - - for app in settings.INSTALLED_APPS: - mod = import_module(app) - # Attempt to import the app's lookups module. - try: - before_import_registry = copy.copy(registry._registry) - import_module('%s.lookups' % app) - except: - registry._registry = before_import_registry - - if module_has_submodule(mod, 'lookups'): - raise diff --git a/src/utils/html5/base.py b/src/utils/html5/base.py deleted file mode 100644 index e58b4e4..0000000 --- a/src/utils/html5/base.py +++ /dev/null @@ -1,139 +0,0 @@ -import re - -from django.core.urlresolvers import reverse -from django.core.serializers.json import DjangoJSONEncoder -from django.http import HttpResponse -from django.utils.encoding import smart_text, force_text - - -try: - import json -except ImportError: - from django.utils import simplejson as json - - -class LookupBase(object): - - @classmethod - def name(cls): - app_name = cls.__module__.split('.')[-2].lower() - class_name = cls.__name__.lower() - name = u'%s-%s' % (app_name, class_name) - return name - - @classmethod - def url(cls): - return reverse('autocomplete-lookup', args=[cls.name()]) - - def get_query(self, request, term): - return [] - - def get_item_label(self, item): - return smart_text(item) - - def get_item_id(self, item): - return smart_text(item) - - def get_item_value(self, item): - return smart_text(item) - - def get_item(self, value): - return value - - def create_item(self, value): - raise NotImplemented() - - def format_item(self, item): - return { - 'id': self.get_item_id(item), - 'value': self.get_item_value(item), - 'label': self.get_item_label(item) - } - - def results(self, request): - term = request.GET.get('term', '') - raw_data = self.get_query(request, term) - data = [] - for item in raw_data: - data.append(self.format_item(item)) - content = json.dumps(data, cls=DjangoJSONEncoder, ensure_ascii=False) - return HttpResponse(content, content_type='application/json') - - -class LookupAlreadyRegistered(Exception): - pass - - -class LookupNotRegistered(Exception): - pass - - -class LookupInvalid(Exception): - pass - - -class LookupRegistry(object): - - def __init__(self): - self._registry = {} - - def validate(self, lookup): - if not issubclass(lookup, LookupBase): - raise LookupInvalid(u'Registered lookups must inherit from the \ - LookupBase class') - - def register(self, lookup): - self.validate(lookup) - name = force_text(lookup.name()) - if name in self._registry: - raise LookupAlreadyRegistered(u'The name %s is already registered' - % name) - self._registry[name] = lookup - - def unregister(self, lookup): - self.validate(lookup) - name = force_text(lookup.name()) - if name not in self._registry: - raise LookupNotRegistered(u'The name %s is not registered' % name) - del self._registry[name] - - def get(self, key): - return self._registry.get(key, None) - - -class ModelLookup(LookupBase): - model = None - filters = {} - search_field = '' - - def get_query(self, request, term): - qs = self.get_queryset() - if term and self.search_field: - qs = qs.filter(**{self.search_field: term}) - return qs - - def get_queryset(self): - qs = self.model._default_manager.get_query_set() - if self.filters: - qs = qs.filter(**self.filters) - return qs - - def get_item_id(self, item): - return item.pk - - def get_item(self, value): - item = None - if value: - try: - item = self.get_queryset().filter(pk=value)[0] - except IndexError: - pass - return item - - def create_item(self, value): - data = {} - if self.search_field: - field_name = re.sub(r'__\w+$', '', self.search_field) - if field_name: - data = {field_name: value} - return self.model(**data) diff --git a/src/utils/html5/forms.py b/src/utils/html5/forms.py deleted file mode 100644 index d859779..0000000 --- a/src/utils/html5/forms.py +++ /dev/null @@ -1,250 +0,0 @@ -""" -Created on 08.05.2011 - -@author: christian -""" -import re - -from django.conf import settings -from django.core import validators -from django.core.validators import EMPTY_VALUES -from django.forms import utils, Form, ModelForm, ValidationError -import sys -import django.forms.fields -from django.utils.translation import gettext as _ - - -from . import widgets - - -class Html5Mixin(object): - - def widget_attrs(self, widget): - """ - Overwrites the standard Widget Attributes to add some HTML5 Stuff - :param widget: A Widget Object - """ - attrs = super(Html5Mixin, self).widget_attrs(widget) - if self.required and not isinstance(widget, widgets.CheckboxInput): - attrs['required'] = 'required' - if self.help_text: - attrs['title'] = self.help_text - attrs['placeholder'] = self.help_text - if hasattr(self, 'placeholder'): - attrs['placeholder'] = self.placeholder - - if self.accesskey: - attrs['accesskey'] = self.accesskey - return attrs - - def __init__(self, *args, **kwargs): - self.accesskey = kwargs.pop('accesskey', None) - super(Html5Mixin, self).__init__(*args, **kwargs) - - -class AutoCompleteSelectField(Html5Mixin, django.forms.Field): - widget = widgets.AutoCompleteSelectWidget - default_error_messages = { - 'invalid_choice': _(u'Select a valid choice. That choice is not one \ - of the available choices.'), - } - - def __init__(self, lookup_class, *args, **kwargs): - self.lookup_class = lookup_class - self.allow_new = kwargs.pop('allow_new', False) - if not kwargs['widget']: - kwargs['widget'] = self.widget(lookup_class, - allow_new=self.allow_new) - super(AutoCompleteSelectField, self).__init__(*args, **kwargs) - - def to_python(self, value): - if value in EMPTY_VALUES: - return None - if isinstance(value, list): - # Input comes from an AutoComplete widget. It's two - # components: text and id - if len(value) != 2: - raise django.forms.ValidationError( - self.error_messages['invalid_choice']) - lookup = self.lookup_class() - if value[1] in EMPTY_VALUES: - if not self.allow_new: - if value[0]: - raise django.forms.ValidationError( - self.error_messages['invalid_choice']) - else: - return None - value = lookup.create_item(value[0]) - else: - value = lookup.get_item(value[1]) - if value is None: - raise django.forms.ValidationError( - self.error_messages['invalid_choice']) - return value - - -class AutoComboboxSelectField(AutoCompleteSelectField): - widget = widgets.AutoComboboxSelectWidget - - -class AutoCompleteSelectMultipleField(Html5Mixin, django.forms.Field): - widget = widgets.AutoCompleteSelectMultipleWidget - - default_error_messages = { - 'invalid_choice': _(u'Select a valid choice. \ - That choice is not one of the available choices.'), - } - - def __init__(self, lookup_class, *args, **kwargs): - self.lookup_class = lookup_class - kwargs['widget'] = self.widget(lookup_class) - self.attrs['autofocus'] = 'autofocus' - super(AutoCompleteSelectMultipleField, self).__init__(*args, **kwargs) - - def to_python(self, value): - if value in EMPTY_VALUES: - return None - lookup = self.lookup_class() - items = [] - for v in value: - if v not in EMPTY_VALUES: - item = lookup.get_item(v) - if item is None: - raise django.forms.ValidationError( - self.error_messages['invalid_choice']) - items.append(item) - return items - - -class AutoComboboxSelectMultipleField(AutoCompleteSelectMultipleField): - widget = widgets.AutoComboboxSelectMultipleWidget - - -class BooleanField(Html5Mixin, django.forms.BooleanField): - widget = widgets.CheckboxInput - - -class CharField(Html5Mixin, django.forms.CharField): - pass - - -class DateField(Html5Mixin, django.forms.fields.DateField): - placeholder = _('yyyy-mm-dd') - widget = widgets.DateInput - - -class DateTimeField(Html5Mixin, django.forms.fields.DateTimeField): - widget = widgets.DateTimeInput - - -class EmailField(Html5Mixin, django.forms.fields.EmailField): - widget = widgets.EmailInput - - -class FileField(Html5Mixin, django.forms.fields.FileField): - widget = widgets.NumberInput - pass - - -class FloatField(Html5Mixin, django.forms.fields.FloatField): - pass - - -class HiddenInput(Html5Mixin, django.forms.HiddenInput): - pass - - -class IntegerField(Html5Mixin, django.forms.fields.IntegerField): - widget = widgets.NumberInput - - def widget_attrs(self, widget): - attrs = super(IntegerField, self).widget_attrs(widget) - if isinstance(widget, widgets.NumberInput): - if self.min_value is not None: - attrs['min'] = self.min_value - if self.max_value is not None: - attrs['max'] = self.max_value - return attrs - - -class ModelChoiceField(Html5Mixin, django.forms.ModelChoiceField): - pass - - -class PasswordInput(Html5Mixin, django.forms.PasswordInput): - pass - - -class PhoneField(Html5Mixin, django.forms.CharField): - widget = widgets.PhoneInput - - def __init__(self, regex=None, max_length=None, min_length=None, - error_message=None, *args, **kwargs): - super(PhoneField, self).__init__(max_length, min_length, - *args, **kwargs) - self._set_regex(u'^[0-9+-/ ]+$') - - def _get_regex(self): - return self._regex - - def _set_regex(self, regex=u'^[0-9+-/ ]+$'): - regex = re.compile(regex, re.UNICODE) - self._regex = regex - if hasattr(self, '_regex_validator') \ - and self._regex_validator in self.validators: - self.validators.remove(self._regex_validator) - self._regex_validator = validators.RegexValidator(regex=regex) - self.validators.append(self._regex_validator) - - regex = property(_get_regex, _set_regex) - - -class ReCaptchaField(django.forms.fields.CharField): - - def __init__(self, *args, **kwargs): - self.widget = widgets.ReCaptchaInput - self.required = True - super(ReCaptchaField, self).__init__(*args, **kwargs) - - def get_remote_ip(self): - f = sys._getframe() - while f: - if 'request' in f.f_locals: - request = f.f_locals['request'] - if request: - remote_ip = request.META.get('REMOTE_ADDR', None) - forwarded_ip = request.META.get('HTTP_X_FORWARDED_FOR', None) - return forwarded_ip or remote_ip - f = f.f_back - - def clean(self, values): - """ test the google recaptcha""" - import json, urllib, urllib2 - - url = "https://www.google.com/recaptcha/api/siteverify" - challenge_value = values[0] - data = urllib.urlencode({ - 'secret': settings.RECAPTCHA_PRIVATE_KEY, - 'response': values[1], - 'remoteip': self.get_remote_ip() - }) - req = urllib2.Request(url, data) - response = urllib2.urlopen(req) - result = json.loads(response.read()) - - # result["success"] will be True on a success - if not result["success"]: - raise ValidationError( - _(u'Only humans are allowed to submit this form.')) - - -class RegexField(Html5Mixin, django.forms.RegexField): - pass - - -class SlugField(Html5Mixin, django.forms.SlugField): - pass - - -class URLField(Html5Mixin, django.forms.fields.URLField): - widget = widgets.URLInput diff --git a/src/utils/html5/models.py b/src/utils/html5/models.py deleted file mode 100644 index f1ee133..0000000 --- a/src/utils/html5/models.py +++ /dev/null @@ -1,148 +0,0 @@ -""" -Created on 08.05.2011 - -@author: christian -""" -from django.db import models -from django.db.models import ForeignKey # @UnusedImport -from django.utils import six - -from . import forms, widgets - - -class BooleanField(models.BooleanField): - - def formfield(self, **kwargs): - defaults = {'form_class': forms.BooleanField} - defaults.update(kwargs) - return super(BooleanField, self).formfield(**defaults) - - -class CharField(models.CharField): - - def formfield(self, **kwargs): - defaults = {'form_class': forms.CharField} - defaults.update(kwargs) - return super(CharField, self).formfield(**defaults) - - -class DateField(models.DateField): - - def formfield(self, **kwargs): - defaults = { - 'form_class': forms.DateField} - defaults.update(kwargs) - return super(DateField, self).formfield(**defaults) - - -class DateTimeField(models.DateTimeField): - - def formfield(self, **kwargs): - defaults = {'form_class': forms.DateTimeField} - defaults.update(kwargs) - return super(DateTimeField, self).formfield(**defaults) - - -class EmailField(models.EmailField): - - def formfield(self, **kwargs): - defaults = {'form_class': forms.EmailField} - defaults.update(kwargs) - return super(EmailField, self).formfield(**defaults) - - -class FileField(models.FileField): - - def formfield(self, **kwargs): - defaults = {'form_class': forms.FileField} - defaults.update(kwargs) - return super(FileField, self).formfield(**defaults) - - -class ForeignKey(models.ForeignKey): - - def formfield(self, **kwargs): - db = kwargs.pop('using', None) - if isinstance(self.rel.to, six.string_types): - raise ValueError("Cannot create form field for %r yet, because " - "its related model %r has not been loaded yet" % - (self.name, self.rel.to)) - queryset = self.rel.to._default_manager.using(db) - queryset = queryset.complex_filter(self.rel.limit_choices_to) - defaults = { - 'form_class': forms.ModelChoiceField, - 'queryset': queryset, - 'to_field_name': self.rel.field_name, - } - defaults.update(kwargs) - return super(ForeignKey, self).formfield(**defaults) - - -class ImageField(models.ImageField): - pass - - -class IntegerField(models.IntegerField): - - def formfield(self, **kwargs): - defaults = {'form_class': forms.IntegerField} - defaults.update(kwargs) - return super(IntegerField, self).formfield(**defaults) - - -class NullBooleanField(models.NullBooleanField): - - def formfield(self, **kwargs): - defaults = {'form_class': forms.BooleanField} - defaults.update(kwargs) - return super(NullBooleanField, self).formfield(**defaults) - - -class PositiveSmallIntegerField(models.PositiveSmallIntegerField): - - def formfield(self, **kwargs): - defaults = { - 'form_class': forms.IntegerField, - 'min_value': 0, - 'max_value': 32767 - } - defaults.update(kwargs) - return super(PositiveSmallIntegerField, self).formfield(**defaults) - - -class PositiveIntegerField(models.IntegerField): - - def formfield(self, **kwargs): - defaults = { - 'form_class': forms.IntegerField, - 'min_value': 0 - } - defaults.update(kwargs) - return super(PositiveIntegerField, self).formfield(**defaults) - - -class SlugField(models.SlugField): - - def formfield(self, **kwargs): - defaults = {'form_class': forms.SlugField} - defaults.update(kwargs) - return super(SlugField, self).formfield(**defaults) - - -class TextField(models.TextField): - - def formfield(self, **kwargs): - defaults = { - 'form_class': forms.CharField, - 'widget': widgets.Textarea - } - defaults.update(kwargs) - return super(TextField, self).formfield(**defaults) - - -class URLField(models.URLField): - - def formfield(self, **kwargs): - defaults = {'form_class': forms.URLField} - defaults.update(kwargs) - return super(URLField, self).formfield(**defaults) diff --git a/src/utils/html5/views.py b/src/utils/html5/views.py deleted file mode 100644 index 7d6e322..0000000 --- a/src/utils/html5/views.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -Created on 05.08.2011 - -@author: christian -""" -from django.http import Http404 - -from . import registry - - -def get_lookup(request, lookup_name): - lookup_cls = registry.get(lookup_name) - if lookup_cls is None: - raise Http404(u'Lookup %s not found' % lookup_name) - lookup = lookup_cls() - return lookup.results(request) diff --git a/src/utils/html5/widgets.py b/src/utils/html5/widgets.py deleted file mode 100644 index 6a4c31a..0000000 --- a/src/utils/html5/widgets.py +++ /dev/null @@ -1,324 +0,0 @@ -""" -Created on 08.05.2011 - -@author: christian -""" -from django.conf import settings -from django.forms import widgets -from django.forms.utils import flatatt -from django.utils import formats -from django.utils.encoding import force_text -from django.utils.html import conditional_escape -from django.utils.http import urlencode -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) - self.qs = {} - super(AutoCompleteWidget, self).__init__(*args, **kwargs) - if attrs is not None: - self.attrs = attrs.copy() - else: - self.attrs = {} - - def update_query_parameters(self, qs_dict): - self.qs.update(qs_dict) - - def build_attrs(self, extra_attrs=None, **kwargs): - attrs = super(AutoCompleteWidget, self).build_attrs(extra_attrs, - **kwargs) # @IgnorePep8 - url = self.lookup_class.url() - if self.qs: - url = '%s?%s' % (url, urlencode(self.qs)) - attrs[u'data-selectable-url'] = url - attrs[u'data-selectable-type'] = 'text' - attrs[u'data-selectable-allow-new'] = str(self.allow_new).lower() - return attrs - - -class AutoComboboxWidget(AutoCompleteWidget): - - def build_attrs(self, extra_attrs=None, **kwargs): - attrs = super(AutoComboboxWidget, self).build_attrs(extra_attrs, - **kwargs) # @IgnorePep8 - attrs[u'data-selectable-type'] = 'combobox' - return attrs - - -class DateInput(widgets.DateInput): - input_type = 'date' - attrs = {'class': 'dateinput'} - - def __init__(self, attrs=None, date_format='%Y-%m-%d'): - super(DateInput, self).__init__(attrs) - if date_format: - self.format = date_format - self.manual_format = True - else: - self.format = formats.get_format('DATE_INPUT_FORMATS')[0] - self.manual_format = False - - -class DateTimeInput(widgets.MultiWidget): - """ - A Widget that splits datetime input into two boxes. - """ - - def __init__(self, attrs=None, date_format='%Y-%m-%d', - time_format='%H:%M'): # @IgnorePep8 - widgets = ( - DateInput(attrs=attrs, date_format=date_format), - TimeInput(attrs=attrs, time_format=time_format) - ) - super(DateTimeInput, self).__init__(widgets, attrs) - - def decompress(self, value): - if value: - return [value.date(), value.time()] - return [None, None] - - -class CheckboxInput(widgets.CheckboxInput): - input_type = 'checkbox' - is_checkbox = True - - def render(self, name, value, attrs=None): - final_attrs = self.build_attrs(attrs, type='checkbox', name=name) - title = force_text(self.attrs.get('title', '')) - try: - result = self.check_test(value) - except: # Silently catch exceptions - result = False - if result: - final_attrs['checked'] = 'checked' - if not ( - value is True or value is False or value is None or value == ''): # @IgnorePep8 - # Only add the 'value' attribute if a value is non-empty. - final_attrs['value'] = force_text(value) - - return mark_safe(u' %s' % (flatatt(final_attrs), title)) - - -class EmailInput(widgets.TextInput): - input_type = 'email' - - -class NumberInput(widgets.TextInput): - input_type = 'number' - - def __init__(self, attrs=None): - widgets.Input.__init__(self, attrs=attrs) - - -class PhoneInput(widgets.TextInput): - input_type = 'tel' - - -class RangeInput(widgets.TextInput): - input_type = 'range' - - -class ReCaptchaInput(widgets.Widget): - """ - Der HTML Code von Googles ReCaptcha als Form Widget - """ - recaptcha_challenge_name = 'g-recaptcha-response' - recaptcha_response_name = 'g-recaptcha-response' - - def render(self, name, value, attrs=None): - html_code = u''' - -
    - '''.format(public_key=settings.RECAPTCHA_PUBLIC_KEY) - return mark_safe(html_code) - - def value_from_datadict(self, data, files, name): - return [data.get(self.recaptcha_challenge_name, None), - data.get(self.recaptcha_response_name, None)] - - -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'} - if attrs: - default_attrs.update(attrs) - super(Textarea, self).__init__(default_attrs) - - def render(self, name, value, attrs=None): - value = '' if value is None else value - final_attrs = self.build_attrs(attrs, name=name) - return mark_safe(u'%s' % (flatatt(final_attrs), - conditional_escape( - force_text( - value)))) - - -class TextInput(widgets.TextInput): - pass - - -class TimeInput(widgets.TimeInput): - input_type = 'time' - - def __init__(self, attrs=None, time_format='%H:%M'): - default_attrs = {'maxlength': 5, 'size': 5} - if attrs: - default_attrs.update(attrs) - super(TimeInput, self).__init__(default_attrs) - - if time_format: - self.format = time_format - self.manual_format = True - else: - self.format = formats.get_format('TIME_INPUT_FORMATS')[0] - self.manual_format = False - - -class URLInput(widgets.Input): - input_type = 'url' - - -class LookupMultipleHiddenInput(widgets.MultipleHiddenInput): - - def __init__(self, lookup_class, *args, **kwargs): - self.lookup_class = lookup_class - super(LookupMultipleHiddenInput, self).__init__(*args, **kwargs) - - def render(self, name, value, attrs=None, choices=()): - lookup = self.lookup_class() - value = [] if value is None else value - final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) - id_ = final_attrs.get('id', None) - inputs = [] - model = getattr(self.lookup_class, 'model', None) - for i, v in enumerate(value): - item = None - if model and isinstance(v, model): - item = v - v = lookup.get_item_id(item) - input_attrs = dict(value=force_text(v), **final_attrs) - if id_: - # An ID attribute was given. Add a numeric index as a suffix - # so that the inputs don't all have the same ID attribute. - input_attrs['id'] = '%s_%s' % (id_, i) - if v: - item = item or lookup.get_item(v) - input_attrs['title'] = lookup.get_item_value(item) - inputs.append(u'' % flatatt(input_attrs)) - return mark_safe(u'\n'.join(inputs)) - - def build_attrs(self, extra_attrs=None, **kwargs): - attrs = super(LookupMultipleHiddenInput, self).build_attrs(extra_attrs, - **kwargs) # @IgnorePep8 - attrs[u'data-selectable-type'] = 'hidden-multiple' - return attrs - - -class AutoCompleteSelectMultipleWidget(SelectableMultiWidget): - - def __init__(self, lookup_class, *args, **kwargs): - self.lookup_class = lookup_class - widgets = [ - AutoCompleteWidget(lookup_class, allow_new=False, - attrs={u'data-selectable-multiple': 'true'}), - LookupMultipleHiddenInput(lookup_class) - ] - super(AutoCompleteSelectMultipleWidget, self).__init__(widgets, *args, - **kwargs) # @IgnorePep8 - - def value_from_datadict(self, data, files, name): - return self.widgets[1].value_from_datadict(data, files, name + '_1') - - def render(self, name, value, attrs=None): - if value and not hasattr(value, '__iter__'): - value = [value] - value = [u'', value] - return super(AutoCompleteSelectMultipleWidget, self).render(name, value, - attrs) # @IgnorePep8 - - -class AutoComboboxSelectMultipleWidget(SelectableMultiWidget): - - def __init__(self, lookup_class, *args, **kwargs): - self.lookup_class = lookup_class - widgets = [ - AutoComboboxWidget(lookup_class, allow_new=False, - attrs={u'data-selectable-multiple': 'true'}), - LookupMultipleHiddenInput(lookup_class) - ] - super(AutoComboboxSelectMultipleWidget, self).__init__(widgets, *args, - **kwargs) # @IgnorePep8 - - def value_from_datadict(self, data, files, name): - return self.widgets[1].value_from_datadict(data, files, name + '_1') - - def render(self, name, value, attrs=None): - if value and not hasattr(value, '__iter__'): - value = [value] - value = [u'', value] - return super(AutoComboboxSelectMultipleWidget, self).render(name, value, - attrs) # @IgnorePep8 - - -class AutoCompleteSelectWidget(SelectableMultiWidget): - - def __init__(self, lookup_class, *args, **kwargs): - self.lookup_class = lookup_class - self.allow_new = kwargs.pop('allow_new', False) - widget_set = [ - AutoCompleteWidget(lookup_class, allow_new=self.allow_new), - widgets.HiddenInput(attrs={u'data-selectable-type': 'hidden'}) - ] - super(AutoCompleteSelectWidget, self).__init__(widget_set, *args, - **kwargs) # @IgnorePep8 - - def decompress(self, value): - if value: - lookup = self.lookup_class() - model = getattr(self.lookup_class, 'model', None) - if model and isinstance(value, model): - item = value - value = lookup.get_item_id(item) - else: - item = lookup.get_item(value) - item_value = lookup.get_item_value(item) - return [item_value, value] - return [None, None] - - -class AutoComboboxSelectWidget(SelectableMultiWidget): - - def __init__(self, lookup_class, *args, **kwargs): - self.lookup_class = lookup_class - self.allow_new = kwargs.pop('allow_new', False) - widget_set = [ - AutoComboboxWidget(lookup_class, allow_new=self.allow_new), - widgets.HiddenInput(attrs={u'data-selectable-type': 'hidden'}) - ] - super(AutoComboboxSelectWidget, self).__init__(widget_set, *args, - **kwargs) # @IgnorePep8 - - def decompress(self, value): - if value: - lookup = self.lookup_class() - model = getattr(self.lookup_class, 'model', None) - if model and isinstance(value, model): - item = value - value = lookup.get_item_id(item) - else: - item = lookup.get_item(value) - item_value = lookup.get_item_value(item) - return [item_value, value] - return [None, None] diff --git a/src/utils/html_cleaner.py b/src/utils/html_cleaner.py index 54785a3..379c16d 100644 --- a/src/utils/html_cleaner.py +++ b/src/utils/html_cleaner.py @@ -4,10 +4,14 @@ Created on 19.10.2011 @author: christian """ from bs4 import BeautifulSoup -#TODO: Nach BeatutifulSoup 4 convertieren +# TODO: Nach BeatutifulSoup 4 convertieren class HtmlCleaner(object): + """ + Tries to clean up HTML code, to reomve all possibilities of an XSS Attack + and unwanted inline Javascript and CSS. + """ ACCEPTABLE_ELEMENTS = ['a', 'abbr', 'acronym', 'address', 'area', 'b', 'big', 'blockquote', 'br', 'button', 'caption', 'center', 'cite', @@ -23,7 +27,7 @@ class HtmlCleaner(object): 'tfoot', 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var'] - ACCEPTABELE_ATTRIBUTES = [ + ACCEPTABLE_ATTRIBUTES = [ 'abbr', 'accept', 'accept-charset', 'accesskey', 'action', 'align', 'class', 'alt', 'axis', 'char', 'charoff', 'charset', 'checked', 'cite', 'clear', 'cols', @@ -35,17 +39,28 @@ class HtmlCleaner(object): 'span', 'src', 'start', 'summary', 'tabindex', 'target', 'title', 'type', 'usemap', 'valign', 'value', 'vspace', 'width'] - counter = 1 tag_removed = False def clean_attributes(self, tag): + """ + reomves all attributes from an element that arenÄt whitelisted. + :param tag: an BeautifulSoup Tag element that should be scrubbed + :return: None + """ for attr in list(tag.attrs.keys()): - if attr not in self.ACCEPTABELE_ATTRIBUTES: + if attr not in self.ACCEPTABLE_ATTRIBUTES: del tag[attr] elif tag[attr].count('script:'): del tag[attr] def clean_tag(self, tag): + """ + Removes the entire tag with all its content, when its not on the + whitelist. If the tag is acceptable it will be passed to + clean_attributes + :param tag: BeautifulSoup Tag element that should be scrubbed + :return: None + """ if tag.name not in self.ACCEPTABLE_ELEMENTS: tag.extract() # remove the bad ones self.tag_removed = True @@ -55,12 +70,12 @@ class HtmlCleaner(object): def clean_html(self, fragment=''): """ Reparses and cleans the html from XSS Attacks until it stops changing. - @param fragment: + :param str fragment: HTML Text that should be cleaned up + :return str: scrubbed HTML Text """ while True: soup = BeautifulSoup(fragment, "html.parser") self.tag_removed = False - self.counter += 1 for tag in soup.find_all(True): self.clean_tag(tag) fragment = str(soup) diff --git a/src/utils/locale/de/LC_MESSAGES/django.po b/src/utils/locale/de/LC_MESSAGES/django.po index 2dda81b..4a6a454 100644 --- a/src/utils/locale/de/LC_MESSAGES/django.po +++ b/src/utils/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: kasu.utils\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-09-28 00:25+0200\n" +"POT-Creation-Date: 2017-05-10 23:16+0200\n" "PO-Revision-Date: 2016-09-28 00:24+0200\n" "Last-Translator: Christian Berg \n" "Language-Team: Kasu \n" @@ -18,1056 +18,1053 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.8.9\n" -#: src/utils/__init__.py:16 +#: utils/__init__.py:16 msgid "Rejected" msgstr "Zurückgewiesen" -#: src/utils/__init__.py:17 +#: utils/__init__.py:17 msgid "Waiting..." msgstr "Wartend..." -#: src/utils/__init__.py:18 +#: utils/__init__.py:18 msgid "Published" msgstr "Veröffentlicht" -#: src/utils/countries.py:4 +#: utils/countries.py:4 msgid "United Kingdom" msgstr "Vereinigtes Königreich" -#: src/utils/countries.py:5 +#: utils/countries.py:5 msgid "Afghanistan" msgstr "Afghanistan" -#: src/utils/countries.py:6 +#: utils/countries.py:6 msgid "Aland Islands" msgstr "Aland Islands" -#: src/utils/countries.py:7 +#: utils/countries.py:7 msgid "Albania" msgstr "Albanien" -#: src/utils/countries.py:8 +#: utils/countries.py:8 msgid "Algeria" msgstr "Algerien" -#: src/utils/countries.py:9 +#: utils/countries.py:9 msgid "American Samoa" msgstr "Amerikanisch-Samoa" -#: src/utils/countries.py:10 +#: utils/countries.py:10 msgid "Andorra" msgstr "Andorra" -#: src/utils/countries.py:11 +#: utils/countries.py:11 msgid "Angola" msgstr "Angola" -#: src/utils/countries.py:12 +#: utils/countries.py:12 msgid "Anguilla" msgstr "Anguilla" -#: src/utils/countries.py:13 +#: utils/countries.py:13 msgid "Antarctica" msgstr "Antarktika" -#: src/utils/countries.py:14 +#: utils/countries.py:14 msgid "Antigua and Barbuda" msgstr "Antigua und Barbuda" -#: src/utils/countries.py:15 +#: utils/countries.py:15 msgid "Argentina" msgstr "Argentinien" -#: src/utils/countries.py:16 +#: utils/countries.py:16 msgid "Armenia" msgstr "Armenien" -#: src/utils/countries.py:17 +#: utils/countries.py:17 msgid "Aruba" msgstr "Aruba" -#: src/utils/countries.py:18 +#: utils/countries.py:18 msgid "Australia" msgstr "Australien" -#: src/utils/countries.py:19 +#: utils/countries.py:19 msgid "Austria" msgstr "Österreich" -#: src/utils/countries.py:20 +#: utils/countries.py:20 msgid "Azerbaijan" msgstr "Aserbaidschan" -#: src/utils/countries.py:21 +#: utils/countries.py:21 msgid "Bahamas" msgstr "Bahamas" -#: src/utils/countries.py:22 +#: utils/countries.py:22 msgid "Bahrain" msgstr "Bahrein" -#: src/utils/countries.py:23 +#: utils/countries.py:23 msgid "Bangladesh" msgstr "Bangladesch" -#: src/utils/countries.py:24 +#: utils/countries.py:24 msgid "Barbados" msgstr "Barbados" -#: src/utils/countries.py:25 +#: utils/countries.py:25 msgid "Belarus" msgstr "Weißrussland" -#: src/utils/countries.py:26 +#: utils/countries.py:26 msgid "Belgium" msgstr "Belgien" -#: src/utils/countries.py:27 +#: utils/countries.py:27 msgid "Belize" msgstr "Belize" -#: src/utils/countries.py:28 +#: utils/countries.py:28 msgid "Benin" msgstr "Benin" -#: src/utils/countries.py:29 +#: utils/countries.py:29 msgid "Bermuda" msgstr "Bermuda" -#: src/utils/countries.py:30 +#: utils/countries.py:30 msgid "Bhutan" msgstr "Bhutan" -#: src/utils/countries.py:31 +#: utils/countries.py:31 msgid "Bolivia" msgstr "Bolivien" -#: src/utils/countries.py:32 +#: utils/countries.py:32 msgid "Bosnia and Herzegovina" msgstr "Bosnien und Herzegowina" -#: src/utils/countries.py:33 +#: utils/countries.py:33 msgid "Botswana" msgstr "Botswana" -#: src/utils/countries.py:34 +#: utils/countries.py:34 msgid "Bouvet Island" msgstr "Bouvet Island" -#: src/utils/countries.py:35 +#: utils/countries.py:35 msgid "Brazil" msgstr "Brasilien" -#: src/utils/countries.py:36 +#: utils/countries.py:36 msgid "British Indian Ocean Territory" msgstr "British Indian Ocean Territory" -#: src/utils/countries.py:37 +#: utils/countries.py:37 msgid "Brunei Darussalam" msgstr "Brunei Darussalam" -#: src/utils/countries.py:38 +#: utils/countries.py:38 msgid "Bulgaria" msgstr "Bulgarien" -#: src/utils/countries.py:39 +#: utils/countries.py:39 msgid "Burkina Faso" msgstr "Burkina Faso" -#: src/utils/countries.py:40 +#: utils/countries.py:40 msgid "Burundi" msgstr "Burundi" -#: src/utils/countries.py:41 +#: utils/countries.py:41 msgid "Cambodia" msgstr "Kambodscha" -#: src/utils/countries.py:42 +#: utils/countries.py:42 msgid "Cameroon" msgstr "Kamerun" -#: src/utils/countries.py:43 +#: utils/countries.py:43 msgid "Canada" msgstr "Kanada" -#: src/utils/countries.py:44 +#: utils/countries.py:44 msgid "Cape Verde" msgstr "Cape Verde" -#: src/utils/countries.py:45 +#: utils/countries.py:45 msgid "Cayman Islands" msgstr "Cayman Islands" -#: src/utils/countries.py:46 +#: utils/countries.py:46 msgid "Central African Republic" msgstr "Zentralafrikanische Republik" -#: src/utils/countries.py:47 +#: utils/countries.py:47 msgid "Chad" msgstr "Tschad" -#: src/utils/countries.py:48 +#: utils/countries.py:48 msgid "Chile" msgstr "Chile" -#: src/utils/countries.py:49 +#: utils/countries.py:49 msgid "China" msgstr "China" -#: src/utils/countries.py:50 +#: utils/countries.py:50 msgid "Christmas Island" msgstr "Christmas Island" -#: src/utils/countries.py:51 +#: utils/countries.py:51 msgid "Cocos (Keeling) Islands" msgstr "Cocos (Keeling) Islands" -#: src/utils/countries.py:52 +#: utils/countries.py:52 msgid "Colombia" msgstr "Kolumbien" -#: src/utils/countries.py:53 +#: utils/countries.py:53 msgid "Comoros" msgstr "Komoren" -#: src/utils/countries.py:54 +#: utils/countries.py:54 msgid "Congo" msgstr "Kongo" -#: src/utils/countries.py:55 +#: utils/countries.py:55 msgid "Congo, The Democratic Republic of the" msgstr "Kongo, Demokratische Republik" -#: src/utils/countries.py:56 +#: utils/countries.py:56 msgid "Cook Islands" msgstr "Cook-Inseln" -#: src/utils/countries.py:57 +#: utils/countries.py:57 msgid "Costa Rica" msgstr "Costa Rica" -#: src/utils/countries.py:58 +#: utils/countries.py:58 msgid "Cote d'Ivoire" msgstr "Cote d'Ivoire" -#: src/utils/countries.py:59 +#: utils/countries.py:59 msgid "Croatia" msgstr "Kroatien" -#: src/utils/countries.py:60 +#: utils/countries.py:60 msgid "Cuba" msgstr "Kuba" -#: src/utils/countries.py:61 +#: utils/countries.py:61 msgid "Cyprus" msgstr "Zypern" -#: src/utils/countries.py:62 +#: utils/countries.py:62 msgid "Czech Republic" msgstr "Tschechische Republik" -#: src/utils/countries.py:63 +#: utils/countries.py:63 msgid "Denmark" msgstr "Dänemark" -#: src/utils/countries.py:64 +#: utils/countries.py:64 msgid "Djibouti" msgstr "Dschibuti" -#: src/utils/countries.py:65 +#: utils/countries.py:65 msgid "Dominica" msgstr "Dominica" -#: src/utils/countries.py:66 +#: utils/countries.py:66 msgid "Dominican Republic" msgstr "Dominikanische Republik" -#: src/utils/countries.py:67 +#: utils/countries.py:67 msgid "Ecuador" msgstr "Ecuador" -#: src/utils/countries.py:68 +#: utils/countries.py:68 msgid "Egypt" msgstr "Ägypten" -#: src/utils/countries.py:69 +#: utils/countries.py:69 msgid "El Salvador" msgstr "El Salvador" -#: src/utils/countries.py:70 +#: utils/countries.py:70 msgid "Equatorial Guinea" msgstr "Äquatorial-Guinea" -#: src/utils/countries.py:71 +#: utils/countries.py:71 msgid "Eritrea" msgstr "Eritrea" -#: src/utils/countries.py:72 +#: utils/countries.py:72 msgid "Estonia" msgstr "Estland" -#: src/utils/countries.py:73 +#: utils/countries.py:73 msgid "Ethiopia" msgstr "Äthiopien" -#: src/utils/countries.py:74 +#: utils/countries.py:74 msgid "Falkland Islands (Malvinas)" msgstr "Falklandinseln (Malvinas)" -#: src/utils/countries.py:75 +#: utils/countries.py:75 msgid "Faroe Islands" msgstr "Färöer-Inseln" -#: src/utils/countries.py:76 +#: utils/countries.py:76 msgid "Fiji" msgstr "Fidschi" -#: src/utils/countries.py:77 +#: utils/countries.py:77 msgid "Finland" msgstr "Finnland" -#: src/utils/countries.py:78 +#: utils/countries.py:78 msgid "France" msgstr "Frankreich" -#: src/utils/countries.py:79 +#: utils/countries.py:79 msgid "French Guiana" msgstr "Französisch-Guayana" -#: src/utils/countries.py:80 +#: utils/countries.py:80 msgid "French Polynesia" msgstr "Französisch-Polynesien" -#: src/utils/countries.py:81 +#: utils/countries.py:81 msgid "French Southern Territories" msgstr "Französisch Südliche Territorien" -#: src/utils/countries.py:82 +#: utils/countries.py:82 msgid "Gabon" msgstr "Gabun" -#: src/utils/countries.py:83 +#: utils/countries.py:83 msgid "Gambia" msgstr "Gambia" -#: src/utils/countries.py:84 +#: utils/countries.py:84 msgid "Georgia" msgstr "Georgia" -#: src/utils/countries.py:85 +#: utils/countries.py:85 msgid "Germany" msgstr "Deutschland" -#: src/utils/countries.py:86 +#: utils/countries.py:86 msgid "Ghana" msgstr "Ghana" -#: src/utils/countries.py:87 +#: utils/countries.py:87 msgid "Gibraltar" msgstr "Gibraltar" -#: src/utils/countries.py:88 +#: utils/countries.py:88 msgid "Greece" msgstr "Griechenland" -#: src/utils/countries.py:89 +#: utils/countries.py:89 msgid "Greenland" msgstr "Grönland" -#: src/utils/countries.py:90 +#: utils/countries.py:90 msgid "Grenada" msgstr "Grenada" -#: src/utils/countries.py:91 +#: utils/countries.py:91 msgid "Guadeloupe" msgstr "Guadeloupe" -#: src/utils/countries.py:92 +#: utils/countries.py:92 msgid "Guam" msgstr "Guam" -#: src/utils/countries.py:93 +#: utils/countries.py:93 msgid "Guatemala" msgstr "Guatemala" -#: src/utils/countries.py:94 +#: utils/countries.py:94 msgid "Guernsey" msgstr "Guernsey" -#: src/utils/countries.py:95 +#: utils/countries.py:95 msgid "Guinea" msgstr "Guinea" -#: src/utils/countries.py:96 +#: utils/countries.py:96 msgid "Guinea-Bissau" msgstr "Guinea-Bissau" -#: src/utils/countries.py:97 +#: utils/countries.py:97 msgid "Guyana" msgstr "Guyana" -#: src/utils/countries.py:98 +#: utils/countries.py:98 msgid "Haiti" msgstr "Haiti" -#: src/utils/countries.py:99 +#: utils/countries.py:99 msgid "Heard Island and McDonald Islands" msgstr "Heard und McDonald Inseln" -#: src/utils/countries.py:100 +#: utils/countries.py:100 msgid "Holy See (Vatican City State)" msgstr "Heiliger Stuhl (Vatikanstadt)" -#: src/utils/countries.py:101 +#: utils/countries.py:101 msgid "Honduras" msgstr "Honduras" -#: src/utils/countries.py:102 +#: utils/countries.py:102 msgid "Hong Kong" msgstr "Hongkong" -#: src/utils/countries.py:103 +#: utils/countries.py:103 msgid "Hungary" msgstr "Ungarn" -#: src/utils/countries.py:104 +#: utils/countries.py:104 msgid "Iceland" msgstr "Island" -#: src/utils/countries.py:105 +#: utils/countries.py:105 msgid "India" msgstr "Indien" -#: src/utils/countries.py:106 +#: utils/countries.py:106 msgid "Indonesia" msgstr "Indonesien" -#: src/utils/countries.py:107 +#: utils/countries.py:107 msgid "Iran, Islamic Republic of" msgstr "Iran, Islamische Republik" -#: src/utils/countries.py:108 +#: utils/countries.py:108 msgid "Iraq" msgstr "Irak" -#: src/utils/countries.py:109 +#: utils/countries.py:109 msgid "Ireland" msgstr "Irland" -#: src/utils/countries.py:110 +#: utils/countries.py:110 msgid "Isle of Man" msgstr "Isle of Man" -#: src/utils/countries.py:111 +#: utils/countries.py:111 msgid "Israel" msgstr "Israel" -#: src/utils/countries.py:112 +#: utils/countries.py:112 msgid "Italy" msgstr "Italien" -#: src/utils/countries.py:113 +#: utils/countries.py:113 msgid "Jamaica" msgstr "Jamaika" -#: src/utils/countries.py:114 +#: utils/countries.py:114 msgid "Japan" msgstr "Japan" -#: src/utils/countries.py:115 +#: utils/countries.py:115 msgid "Jersey" msgstr "Jersey" -#: src/utils/countries.py:116 +#: utils/countries.py:116 msgid "Jordan" msgstr "Jordan" -#: src/utils/countries.py:117 +#: utils/countries.py:117 msgid "Kazakhstan" msgstr "Kasachstan" -#: src/utils/countries.py:118 +#: utils/countries.py:118 msgid "Kenya" msgstr "Kenia" -#: src/utils/countries.py:119 +#: utils/countries.py:119 msgid "Kiribati" msgstr "Kiribati" -#: src/utils/countries.py:120 +#: utils/countries.py:120 msgid "Korea, Democratic People's Republic of" msgstr "Korea, Demokratische Volksrepublik" -#: src/utils/countries.py:121 +#: utils/countries.py:121 msgid "Korea, Republic of" msgstr "Korea, Republik" -#: src/utils/countries.py:122 +#: utils/countries.py:122 msgid "Kuwait" msgstr "Kuwait" -#: src/utils/countries.py:123 +#: utils/countries.py:123 msgid "Kyrgyzstan" msgstr "Kirgisistan" -#: src/utils/countries.py:124 +#: utils/countries.py:124 msgid "Lao People's Democratic Republic" msgstr "Lao Demokratischen Volksrepublik" -#: src/utils/countries.py:125 +#: utils/countries.py:125 msgid "Latvia" msgstr "Lettland" -#: src/utils/countries.py:126 +#: utils/countries.py:126 msgid "Lebanon" msgstr "Libanon" -#: src/utils/countries.py:127 +#: utils/countries.py:127 msgid "Lesotho" msgstr "Lesotho" -#: src/utils/countries.py:128 +#: utils/countries.py:128 msgid "Liberia" msgstr "Liberia" -#: src/utils/countries.py:129 +#: utils/countries.py:129 msgid "Libyan Arab Jamahiriya" msgstr "Libyen" -#: src/utils/countries.py:130 +#: utils/countries.py:130 msgid "Liechtenstein" msgstr "Liechtenstein" -#: src/utils/countries.py:131 +#: utils/countries.py:131 msgid "Lithuania" msgstr "Litauen" -#: src/utils/countries.py:132 +#: utils/countries.py:132 msgid "Luxembourg" msgstr "Luxemburg" -#: src/utils/countries.py:133 +#: utils/countries.py:133 msgid "Macao" msgstr "Macao" -#: src/utils/countries.py:134 +#: utils/countries.py:134 msgid "Macedonia, The Former Yugoslav Republic of" msgstr "Mazedonien, die ehemalige jugoslawische Republik" -#: src/utils/countries.py:135 +#: utils/countries.py:135 msgid "Madagascar" msgstr "Madagaskar" -#: src/utils/countries.py:136 +#: utils/countries.py:136 msgid "Malawi" msgstr "Malawi" -#: src/utils/countries.py:137 +#: utils/countries.py:137 msgid "Malaysia" msgstr "Malaysia" -#: src/utils/countries.py:138 +#: utils/countries.py:138 msgid "Maldives" msgstr "Malediven" -#: src/utils/countries.py:139 +#: utils/countries.py:139 msgid "Mali" msgstr "Mali" -#: src/utils/countries.py:140 +#: utils/countries.py:140 msgid "Malta" msgstr "Malta" -#: src/utils/countries.py:141 +#: utils/countries.py:141 msgid "Marshall Islands" msgstr "Marshall Islands" -#: src/utils/countries.py:142 +#: utils/countries.py:142 msgid "Martinique" msgstr "Martinique" -#: src/utils/countries.py:143 +#: utils/countries.py:143 msgid "Mauritania" msgstr "Mauretanien" -#: src/utils/countries.py:144 +#: utils/countries.py:144 msgid "Mauritius" msgstr "Mauritius" -#: src/utils/countries.py:145 +#: utils/countries.py:145 msgid "Mayotte" msgstr "Mayotte" -#: src/utils/countries.py:146 +#: utils/countries.py:146 msgid "Mexico" msgstr "Mexiko" -#: src/utils/countries.py:147 +#: utils/countries.py:147 msgid "Micronesia, Federated States of" msgstr "Mikronesien, Föderierte Staaten von" -#: src/utils/countries.py:148 +#: utils/countries.py:148 msgid "Moldova" msgstr "Moldawien" -#: src/utils/countries.py:149 +#: utils/countries.py:149 msgid "Monaco" msgstr "Monaco" -#: src/utils/countries.py:150 +#: utils/countries.py:150 msgid "Mongolia" msgstr "Mongolei" -#: src/utils/countries.py:151 +#: utils/countries.py:151 msgid "Montenegro" msgstr "Montenegro" -#: src/utils/countries.py:152 +#: utils/countries.py:152 msgid "Montserrat" msgstr "Montserrat" -#: src/utils/countries.py:153 +#: utils/countries.py:153 msgid "Morocco" msgstr "Marokko" -#: src/utils/countries.py:154 +#: utils/countries.py:154 msgid "Mozambique" msgstr "Mosambik" -#: src/utils/countries.py:155 +#: utils/countries.py:155 msgid "Myanmar" msgstr "Myanmar" -#: src/utils/countries.py:156 +#: utils/countries.py:156 msgid "Namibia" msgstr "Namibia" -#: src/utils/countries.py:157 +#: utils/countries.py:157 msgid "Nauru" msgstr "Nauru" -#: src/utils/countries.py:158 +#: utils/countries.py:158 msgid "Nepal" msgstr "Nepal" -#: src/utils/countries.py:159 +#: utils/countries.py:159 msgid "Netherlands" msgstr "Niederlande" -#: src/utils/countries.py:160 +#: utils/countries.py:160 msgid "Netherlands Antilles" msgstr "Niederländische Antillen" -#: src/utils/countries.py:161 +#: utils/countries.py:161 msgid "New Caledonia" msgstr "Neukaledonien" -#: src/utils/countries.py:162 +#: utils/countries.py:162 msgid "New Zealand" msgstr "New Zealand" -#: src/utils/countries.py:163 +#: utils/countries.py:163 msgid "Nicaragua" msgstr "Nicaragua" -#: src/utils/countries.py:164 +#: utils/countries.py:164 msgid "Niger" msgstr "Niger" -#: src/utils/countries.py:165 +#: utils/countries.py:165 msgid "Nigeria" msgstr "Nigeria" -#: src/utils/countries.py:166 +#: utils/countries.py:166 msgid "Niue" msgstr "Niue" -#: src/utils/countries.py:167 +#: utils/countries.py:167 msgid "Norfolk Island" msgstr "Norfolk Island" -#: src/utils/countries.py:168 +#: utils/countries.py:168 msgid "Northern Mariana Islands" msgstr "Northern Mariana Islands" -#: src/utils/countries.py:169 +#: utils/countries.py:169 msgid "Norway" msgstr "Norwegen" -#: src/utils/countries.py:170 +#: utils/countries.py:170 msgid "Oman" msgstr "Oman" -#: src/utils/countries.py:171 +#: utils/countries.py:171 msgid "Pakistan" msgstr "Pakistan" -#: src/utils/countries.py:172 +#: utils/countries.py:172 msgid "Palau" msgstr "Palau" -#: src/utils/countries.py:173 +#: utils/countries.py:173 msgid "Palestinian Territory, Occupied" msgstr "Palästinensische Autonomiegebiete" -#: src/utils/countries.py:174 +#: utils/countries.py:174 msgid "Panama" msgstr "Panama" -#: src/utils/countries.py:175 +#: utils/countries.py:175 msgid "Papua New Guinea" msgstr "Papua-Neuguinea" -#: src/utils/countries.py:176 +#: utils/countries.py:176 msgid "Paraguay" msgstr "Paraguay" -#: src/utils/countries.py:177 +#: utils/countries.py:177 msgid "Peru" msgstr "Peru" -#: src/utils/countries.py:178 +#: utils/countries.py:178 msgid "Philippines" msgstr "Philippinen" -#: src/utils/countries.py:179 +#: utils/countries.py:179 msgid "Pitcairn" msgstr "Pitcairn" -#: src/utils/countries.py:180 +#: utils/countries.py:180 msgid "Poland" msgstr "Polen" -#: src/utils/countries.py:181 +#: utils/countries.py:181 msgid "Portugal" msgstr "Portugal" -#: src/utils/countries.py:182 +#: utils/countries.py:182 msgid "Puerto Rico" msgstr "Puerto Rico" -#: src/utils/countries.py:183 +#: utils/countries.py:183 msgid "Qatar" msgstr "Katar" -#: src/utils/countries.py:184 +#: utils/countries.py:184 msgid "Reunion" msgstr "Wiedervereinigung" -#: src/utils/countries.py:185 +#: utils/countries.py:185 msgid "Romania" msgstr "Rumänien" -#: src/utils/countries.py:186 +#: utils/countries.py:186 msgid "Russian Federation" msgstr "Russischen Föderation" -#: src/utils/countries.py:187 +#: utils/countries.py:187 msgid "Rwanda" msgstr "Ruanda" -#: src/utils/countries.py:188 +#: utils/countries.py:188 msgid "Saint Barthelemy" msgstr "Saint Barthelemy" -#: src/utils/countries.py:189 +#: utils/countries.py:189 msgid "Saint Helena" msgstr "Saint Helena" -#: src/utils/countries.py:190 +#: utils/countries.py:190 msgid "Saint Kitts and Nevis" msgstr "Saint Kitts und Nevis" -#: src/utils/countries.py:191 +#: utils/countries.py:191 msgid "Saint Lucia" msgstr "Santa Lucia" -#: src/utils/countries.py:192 +#: utils/countries.py:192 msgid "Saint Martin" msgstr "Santa Martin" -#: src/utils/countries.py:193 +#: utils/countries.py:193 msgid "Saint Pierre and Miquelon" msgstr "Saint Pierre und Miquelon" -#: src/utils/countries.py:194 +#: utils/countries.py:194 msgid "Saint Vincent and the Grenadines" msgstr "Saint Vincent und die Grenadinen" -#: src/utils/countries.py:195 +#: utils/countries.py:195 msgid "Samoa" msgstr "Samoa" -#: src/utils/countries.py:196 +#: utils/countries.py:196 msgid "San Marino" msgstr "San Marino" -#: src/utils/countries.py:197 +#: utils/countries.py:197 msgid "Sao Tome and Principe" msgstr "Sao Tome und Principe" -#: src/utils/countries.py:198 +#: utils/countries.py:198 msgid "Saudi Arabia" msgstr "Saudi-Arabien" -#: src/utils/countries.py:199 +#: utils/countries.py:199 msgid "Senegal" msgstr "Senegal" -#: src/utils/countries.py:200 +#: utils/countries.py:200 msgid "Serbia" msgstr "Serbien" -#: src/utils/countries.py:201 +#: utils/countries.py:201 msgid "Seychelles" msgstr "Seychellen" -#: src/utils/countries.py:202 +#: utils/countries.py:202 msgid "Sierra Leone" msgstr "Sierra Leone" -#: src/utils/countries.py:203 +#: utils/countries.py:203 msgid "Singapore" msgstr "Singapur" -#: src/utils/countries.py:204 +#: utils/countries.py:204 msgid "Slovakia" msgstr "Slowakei" -#: src/utils/countries.py:205 +#: utils/countries.py:205 msgid "Slovenia" msgstr "Slowenien" -#: src/utils/countries.py:206 +#: utils/countries.py:206 msgid "Solomon Islands" msgstr "Salomon-Inseln" -#: src/utils/countries.py:207 +#: utils/countries.py:207 msgid "Somalia" msgstr "Somalia" -#: src/utils/countries.py:208 +#: utils/countries.py:208 msgid "South Africa" msgstr "Südafrika" -#: src/utils/countries.py:209 +#: utils/countries.py:209 msgid "South Georgia and the South Sandwich Islands" msgstr "Südgeorgien und die Südlichen Sandwichinseln" -#: src/utils/countries.py:210 +#: utils/countries.py:210 msgid "Spain" msgstr "Spanien" -#: src/utils/countries.py:211 +#: utils/countries.py:211 msgid "Sri Lanka" msgstr "Sri Lanka" -#: src/utils/countries.py:212 +#: utils/countries.py:212 msgid "Sudan" msgstr "Sudan" -#: src/utils/countries.py:213 +#: utils/countries.py:213 msgid "Suriname" msgstr "Suriname" -#: src/utils/countries.py:214 +#: utils/countries.py:214 msgid "Svalbard and Jan Mayen" msgstr "Svalbard und Jan Mayen" -#: src/utils/countries.py:215 +#: utils/countries.py:215 msgid "Swaziland" msgstr "Swaziland" -#: src/utils/countries.py:216 +#: utils/countries.py:216 msgid "Sweden" msgstr "Schweden" -#: src/utils/countries.py:217 +#: utils/countries.py:217 msgid "Switzerland" msgstr "Schweiz" -#: src/utils/countries.py:218 +#: utils/countries.py:218 msgid "Syrian Arab Republic" msgstr "Arabische Republik Syrien" -#: src/utils/countries.py:219 +#: utils/countries.py:219 msgid "Taiwan, Province of China" msgstr "Taiwan, Province of China" -#: src/utils/countries.py:220 +#: utils/countries.py:220 msgid "Tajikistan" msgstr "Tadschikistan" -#: src/utils/countries.py:221 +#: utils/countries.py:221 msgid "Tanzania, United Republic of" msgstr "Tansania, Vereinigte Republik" -#: src/utils/countries.py:222 +#: utils/countries.py:222 msgid "Thailand" msgstr "Thailand" -#: src/utils/countries.py:223 +#: utils/countries.py:223 msgid "Timor-Leste" msgstr "Timor-Leste" -#: src/utils/countries.py:224 +#: utils/countries.py:224 msgid "Togo" msgstr "Togo" -#: src/utils/countries.py:225 +#: utils/countries.py:225 msgid "Tokelau" msgstr "Tokelau" -#: src/utils/countries.py:226 +#: utils/countries.py:226 msgid "Tonga" msgstr "Tonga" -#: src/utils/countries.py:227 +#: utils/countries.py:227 msgid "Trinidad and Tobago" msgstr "Trinidad und Tobago" -#: src/utils/countries.py:228 +#: utils/countries.py:228 msgid "Tunisia" msgstr "Tunesien" -#: src/utils/countries.py:229 +#: utils/countries.py:229 msgid "Turkey" msgstr "Türkei" -#: src/utils/countries.py:230 +#: utils/countries.py:230 msgid "Turkmenistan" msgstr "Turkmenistan" -#: src/utils/countries.py:231 +#: utils/countries.py:231 msgid "Turks and Caicos Islands" msgstr "Turks-und Caicosinseln" -#: src/utils/countries.py:232 +#: utils/countries.py:232 msgid "Tuvalu" msgstr "Tuvalu" -#: src/utils/countries.py:233 +#: utils/countries.py:233 msgid "Uganda" msgstr "Uganda" -#: src/utils/countries.py:234 +#: utils/countries.py:234 msgid "Ukraine" msgstr "Ukraine" -#: src/utils/countries.py:235 +#: utils/countries.py:235 msgid "United Arab Emirates" msgstr "Vereinigte Arabische Emirate" -#: src/utils/countries.py:236 +#: utils/countries.py:236 msgid "United States" msgstr "Vereinigte Staaten" -#: src/utils/countries.py:237 +#: utils/countries.py:237 msgid "United States Minor Outlying Islands" msgstr "United States Minor Outlying Islands" -#: src/utils/countries.py:238 +#: utils/countries.py:238 msgid "Uruguay" msgstr "Uruguay" -#: src/utils/countries.py:239 +#: utils/countries.py:239 msgid "Uzbekistan" msgstr "Usbekistan" -#: src/utils/countries.py:240 +#: utils/countries.py:240 msgid "Vanuatu" msgstr "Vanuatu" -#: src/utils/countries.py:241 +#: utils/countries.py:241 msgid "Venezuela" msgstr "Venezuela" -#: src/utils/countries.py:242 +#: utils/countries.py:242 msgid "Viet Nam" msgstr "Vietnam" -#: src/utils/countries.py:243 +#: utils/countries.py:243 msgid "Virgin Islands, British" msgstr "Virgin Islands, British" -#: src/utils/countries.py:244 +#: utils/countries.py:244 msgid "Virgin Islands, U.S." msgstr "Virgin Islands, US" -#: src/utils/countries.py:245 +#: utils/countries.py:245 msgid "Wallis and Futuna" msgstr "Wallis und Futuna" -#: src/utils/countries.py:246 +#: utils/countries.py:246 msgid "Western Sahara" msgstr "Westsahara" -#: src/utils/countries.py:247 +#: utils/countries.py:247 msgid "Yemen" msgstr "Jemen" -#: src/utils/countries.py:248 +#: utils/countries.py:248 msgid "Zambia" msgstr "Sambia" -#: src/utils/countries.py:249 +#: utils/countries.py:249 msgid "Zimbabwe" msgstr "Zimbabwe" -#: src/utils/html5/forms.py:48 +#: utils/html5/forms.py:48 msgid "" "Select a valid choice. That choice is not one of the available " "choices." msgstr "" "Bitte eine gültige Auswahl treffen. Diese Option steht nicht zur Verfügung." -#: src/utils/html5/forms.py:94 +#: utils/html5/forms.py:94 msgid "" "Select a valid choice. That choice is not one of the available " "choices." msgstr "" "Bitte eine gültige Auswahl treffen. Diese Option steht nicht zur Verfügung." -#: src/utils/html5/forms.py:132 +#: utils/html5/forms.py:132 msgid "yyyy-mm-dd" msgstr "tt.mm.jjjj" -#: src/utils/html5/forms.py:238 +#: utils/html5/forms.py:238 msgid "Only humans are allowed to submit this form." msgstr "Nur Menschen dürfen dieses Formular übermitteln." -#: src/utils/management/commands/compresscss.py:22 -#: src/utils/management/commands/compressjs.py:21 +#: utils/management/commands/compresscss.py:22 +#: utils/management/commands/compressjs.py:21 msgid "Reads raw CSS from stdin, and writes compressed CSS to stdout." msgstr "Lese das CSS von stdin, und gebe es in komprimierter Form wieder aus." -#: src/utils/management/commands/compresscss.py:222 -#: src/utils/management/commands/scss-compiler.py:29 +#: utils/management/commands/scss-compiler.py:20 +msgid "Compile SCSS rules." +msgstr "kompiliere SCSS Regeln." + +#: utils/management/commands/scss-compiler.py:29 #, python-format msgid "Compressing CSS for %s" msgstr "Komprimiere CSS für %s" -#: src/utils/management/commands/compressjs.py:29 -#, python-format -msgid "Compressing JavaScript for %s" -msgstr "Komprimiere JavaScript für %s" - -#: src/utils/management/commands/scss-compiler.py:20 -msgid "Compile SCSS rules." -msgstr "kompiliere SCSS Regeln." - -#: src/utils/mixins.py:28 +#: utils/mixins.py:28 msgid "You need to be logged in" msgstr "Eine Anmeldung ist erforderlich" -#: src/utils/mixins.py:60 +#: utils/mixins.py:60 msgid "You don't have the permission to do this" msgstr "Du hast nicht genügend Rechte dafür." -#: src/utils/mixins.py:100 +#: utils/mixins.py:100 msgid "You don't have the permissions for this" msgstr "Du hast nicht genügend Rechte dafür." +#~ msgid "Compressing JavaScript for %s" +#~ msgstr "Komprimiere JavaScript für %s" + #~ msgid "dd.mm.yyyy" #~ msgstr "tt.mm.jjjj" diff --git a/src/utils/management/__init__.py b/src/utils/management/__init__.py deleted file mode 100644 index 013e4b7..0000000 --- a/src/utils/management/__init__.py +++ /dev/null @@ -1 +0,0 @@ -#!/usr/bin/python diff --git a/src/utils/management/commands/__init__.py b/src/utils/management/commands/__init__.py deleted file mode 100644 index 013e4b7..0000000 --- a/src/utils/management/commands/__init__.py +++ /dev/null @@ -1 +0,0 @@ -#!/usr/bin/python diff --git a/src/utils/management/commands/compresscss.py b/src/utils/management/commands/compresscss.py deleted file mode 100644 index 6c6b4c7..0000000 --- a/src/utils/management/commands/compresscss.py +++ /dev/null @@ -1,239 +0,0 @@ -""" -Created on 06.06.2011 - -@author: christian -""" -import fnmatch -from optparse import make_option -import os -import re - -from django.conf import settings -from django.contrib.sites.models import Site -from django.core.management.base import BaseCommand -from django.utils.translation import ugettext as _ - - -class Command(BaseCommand): - """ - classdocs - """ - can_import_settings = True - help = _("Reads raw CSS from stdin, and writes compressed CSS to stdout.") - option_list = BaseCommand.option_list + ( - make_option('-w', '--wrap', - type='int', - default=None, - metavar='N', - help="Wrap output to approximately N chars per line."), - ) - - def remove_comments(self, css): - """Remove all CSS comment blocks.""" - - iemac = False - preserve = False - comment_start = css.find("/*") - while comment_start >= 0: - # Preserve comments that look like `/*!...*/`. - # Slicing is used to make sure we don"t get an IndexError. - preserve = css[comment_start + 2:comment_start + 3] == "!" - - comment_end = css.find("*/", comment_start + 2) - if comment_end < 0: - if not preserve: - css = css[:comment_start] - break - elif comment_end >= (comment_start + 2): - if css[comment_end - 1] == "\\": - # This is an IE Mac-specific comment; leave this one - # and the following one alone. - comment_start = comment_end + 2 - iemac = True - elif iemac: - comment_start = comment_end + 2 - iemac = False - elif not preserve: - css = css[:comment_start] + css[comment_end + 2:] - else: - comment_start = comment_end + 2 - comment_start = css.find("/*", comment_start) - - return css - - def remove_unnecessary_whitespace(self, css): - """Remove unnecessary whitespace characters.""" - - def pseudoclasscolon(css): - """ - Prevents 'p :link' from becoming 'p:link'. - - Translates 'p :link' into 'p ___PSEUDOCLASSCOLON___link'; this is - translated back again later. - """ - - regex = re.compile(r"(^|\})(([^\{\:])+\:)+([^\{]*\{)") - match = regex.search(css) - while match: - css = ''.join([ - css[:match.start()], - match.group().replace(":", "___PSEUDOCLASSCOLON___"), - css[match.end():]]) - match = regex.search(css) - return css - - css = pseudoclasscolon(css) - # Remove spaces from before things. - css = re.sub(r"\s+([!{};:>+\(\)\],])", r"\1", css) - - # If there is a `@charset`, - # then only allow one, and move to the beginning. - css = re.sub(r"^(.*)(@charset \"[^\"]*\";)", r"\2\1", css) - css = re.sub(r"^(\s*@charset [^;]+;\s*)+", r"\1", css) - - # Put the space back in for a few cases, such as `@media screen` and - # `(-webkit-min-device-pixel-ratio:0)`. - css = re.sub(r"\band\(", "and (", css) - - # Put the colons back. - css = css.replace('___PSEUDOCLASSCOLON___', ':') - - # Remove spaces from after things. - css = re.sub(r"([!{}:;>+\(\[,])\s+", r"\1", css) - - 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`.""" - - regex = re.compile(r"rgb\s*\(\s*([0-9,\s]+)\s*\)") - match = regex.search(css) - while match: - colors = match.group(1).split(",") - hexcolor = '#%.2x%.2x%.2x' % map(int, colors) - css = css.replace(match.group(), hexcolor) - match = regex.search(css) - return css - - def condense_zero_units(self, css): - """Replace `0(px, em, %, etc)` with `0`.""" - - 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;`.""" - - css = css.replace(":0 0 0 0;", ":0;") - css = css.replace(":0 0 0;", ":0;") - css = css.replace(":0 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.""" - - regex = re.compile( - r"([^\"'=\s])(\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])") - match = regex.search(css) - while match: - first = match.group(3) + match.group(5) + match.group(7) - second = match.group(4) + match.group(6) + match.group(8) - if first.lower() == second.lower(): - css = css.replace(match.group(), - match.group(1) + match.group(2) + '#' + first) - match = regex.search(css, match.end() - 3) - else: - match = regex.search(css, match.end()) - return css - - def condense_whitespace(self, css): - """Condense multiple adjacent whitespace characters into one.""" - - return re.sub(r"\s+", " ", css) - - def condense_semicolons(self, css): - """Condense multiple adjacent semicolon characters into one.""" - - return re.sub(r";;+", ";", css) - - def wrap_css_lines(self, css, line_length): - """Wrap the lines of the given CSS to an approximate length.""" - - lines = [] - line_start = 0 - for i, char in enumerate(css): - # It's safe to break after `}` characters. - if char == '}' and (i - line_start >= line_length): - lines.append(css[line_start:i + 1]) - line_start = i + 1 - - if line_start < len(css): - 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) - # A pseudo class for the Box Model Hack - # (see http://tantek.com/CSS/Examples/boxmodelhack.html) - css = css.replace('"\\"}\\""', "___PSEUDOCLASSBMH___") - css = self.remove_unnecessary_whitespace(css) - css = self.remove_unnecessary_semicolons(css) - css = self.condense_zero_units(css) - css = self.condense_multidimensional_zeros(css) - css = self.condense_floating_points(css) - css = self.normalize_rgb_colors_to_hex(css) - css = self.condense_hex_colors(css) - if wrap is not None: - css = self.wrap_css_lines(css, wrap) - css = css.replace("___PSEUDOCLASSBMH___", '"\\"}\\""') - css = self.condense_semicolons(css) - # A Hack for IE compatiblity - # These crappy browser has issues with AND Statments in @MEDIA Blocks - css = css.replace('and(', 'and (') - return css.strip() - - def handle(self, *args, **options): - CSS_ROOT = os.path.join(settings.STATICFILES_DIRS[0], 'css') - for site in Site.objects.all(): - css_input = os.path.join(CSS_ROOT, site.domain) - css_output = '%s/%s.css' % (CSS_ROOT, - site.domain.replace('.', '_')) - - print("Compressing CSS for {}".format(site.name)) - print("Input Dir: {}".format(css_input)) - print("Output File: {}".format(css_output)) - - try: - os.makedirs(css_input) - except OSError: - pass - css = '' - """Read each .css file in the css_input directory, - and append their content""" - for file_name in fnmatch.filter(sorted(os.listdir(css_input)), - '*.css'): - print('Adding: {}'.format(file_name)) - with open(os.path.join(css_input, file_name), 'r') as css_file: - css += css_file.read() - with open(css_output, 'w') as css_file: - css_file.write(self.compress(css)) diff --git a/src/utils/management/commands/compressjs.py b/src/utils/management/commands/compressjs.py deleted file mode 100644 index 9382c69..0000000 --- a/src/utils/management/commands/compressjs.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Created on 06.06.2011 - -@author: christian -""" -import fnmatch -import os - -from django.conf import settings -from django.contrib.sites.models import Site -from django.core.management.base import BaseCommand -from django.utils.translation import ugettext as _ -import jsmin - - -class Command(BaseCommand): - """ - classdocs - """ - can_import_settings = True - help = _("Reads raw CSS from stdin, and writes compressed CSS to stdout.") - - def handle(self, *args, **options): - JS_ROOT = os.path.join(settings.STATICFILES_DIRS[0], 'js') - for site in Site.objects.all(): - js_input = os.path.join(JS_ROOT, site.domain) - js_output = '%s/%s.js' % (JS_ROOT, site.domain.replace('.', '_')) - - print("Compressing JavaScript for {}".format(site.name)) - print("Input Dir: {}".format(js_input)) - print("Output File: %s".format(js_output)) - - try: - os.makedirs(js_input) - except OSError: - pass - output = '' - # Read each .js file in the js_input directory, and append their - # content - js_files = fnmatch.filter(sorted(os.listdir(js_input)), '*.js') - for file_name in js_files: - print(" Adding: {}...".format(file_name)) - input_file_name = os.path.join(js_input, file_name) - with open(input_file_name, 'r') as input_file: - output += input_file.read() - with open(js_output, 'w') as output_file: - output_file.write(jsmin.jsmin(output)) diff --git a/src/utils/management/commands/scss-compiler.py b/src/utils/management/commands/scss-compiler.py deleted file mode 100644 index 2ab97b1..0000000 --- a/src/utils/management/commands/scss-compiler.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Created on 06.06.2011 - -@author: christian -""" -import os -import fnmatch - -from django.conf import settings -from django.contrib.sites.models import Site -from django.core.management.base import BaseCommand -from django.utils.translation import ugettext as _ - - -class Command(BaseCommand): - """ - classdocs - """ - can_import_settings = True - help = _("Compile SCSS rules.") - - def handle(self, *args, **options): - CSS_ROOT = os.path.join(settings.STATICFILES_DIRS[0], 'css') - for site in Site.objects.all(): - css_input = os.path.join(CSS_ROOT, site.domain) - css_output = '%s/%s.css' % (CSS_ROOT, - site.domain.replace('.', '_')) - - print _("Compressing CSS for %s") % site.name - print "Input Dir: %s" % css_input - print "Output File: %s" % css_output - - try: - os.makedirs(css_input) - except OSError: - pass - css = '' - """Read each .css file in the css_input directory, - and append their content""" - for file_name in fnmatch.filter(sorted(os.listdir(css_input)), - '*.css'): - print ' Adding: %s' % file_name - with open(os.path.join(css_input, file_name), 'r') as css_file: - css += css_file.read() - with open(css_output, 'w') as css_file: - css_file.write(self.compress(css)) - # file.write(css) diff --git a/src/utils/massmailer.py b/src/utils/massmailer.py index d1d977d..41b66e2 100644 --- a/src/utils/massmailer.py +++ b/src/utils/massmailer.py @@ -2,8 +2,7 @@ import logging from django.core import mail from django.contrib.sites.models import Site -from django.conf import settings -from django.template import loader, Context +from django.template import loader from django.contrib.auth import get_user_model @@ -48,7 +47,7 @@ class MassMailer(object): if isinstance(recipient, self.USER_MODEL): self.recipients.add(recipient) else: - self.log.warn('%s is not a User Object!', recipient) + self.log.warning('%s is not a User Object!', recipient) def add_recipients(self, user_list): for user in user_list: @@ -56,10 +55,10 @@ class MassMailer(object): self.log.debug('Adding %s as Recipient' % user) self.recipients.add(user) else: - self.log.warn('%s is not a User Object!', user) + self.log.warning('%s is not a User Object!', user) def process_mails(self): - mail_context = Context(self.context) + mail_context = self.context mail_context['site'] = Site.objects.get_current() self.txt_template = loader.get_template(self.txt_template) @@ -103,7 +102,7 @@ class MassMailer(object): try: mail.send() except: - self.log.warn("Mail failed for: %s", mail.to) + self.log.warning("Mail failed for: %s", mail.to) else: self.log.info("Mail sent successful to: %s" % mail.to) self.close_smtp_connection() diff --git a/src/utils/tests.py b/src/utils/tests.py index bfb7515..d8d668b 100644 --- a/src/utils/tests.py +++ b/src/utils/tests.py @@ -5,7 +5,8 @@ import html_cleaner class HtmlTestCase(unittest.TestCase): known_html = ( ('Fett!', 'Fett!'), - ('Bad Link', 'Bad Link'), + ('Bad Link', + 'Bad Link'), (''' @@ -32,8 +33,8 @@ class HtmlTestCase(unittest.TestCase): href="http://www.xxxxxxxxxxxx.com">http://www.xxxxxxxxxxxx.com
    (xxx) xxx-xxxx ''', - '' - ) + '' + ) ) def test_html_cleaner(self): @@ -42,5 +43,6 @@ class HtmlTestCase(unittest.TestCase): for original, tidy in self.known_html: self.assertEqual(cleaner.clean_html(original), tidy) + if __name__ == '__main__': unittest.main()