Merge branch 'app'
authorJan Szejko <janek37@gmail.com>
Wed, 19 Dec 2018 13:46:37 +0000 (14:46 +0100)
committerJan Szejko <janek37@gmail.com>
Wed, 19 Dec 2018 13:46:37 +0000 (14:46 +0100)
# Conflicts:
# lib/librarian

82 files changed:
lib/librarian
requirements/requirements-dev.txt
requirements/requirements.txt
src/api/handlers.py
src/api/migrations/0004_bookuserdata_last_changed.py [new file with mode: 0644]
src/api/models.py
src/api/urls.py
src/catalogue/management/commands/gencover.py
src/catalogue/models/book.py
src/catalogue/utils.py
src/contact/mailing.py
src/contact/models.py
src/contact/templates/contact/mail_managers_body.txt
src/newsletter/forms.py
src/newsletter/locale/de/LC_MESSAGES/django.mo
src/newsletter/locale/de/LC_MESSAGES/django.po
src/newsletter/locale/en/LC_MESSAGES/django.mo
src/newsletter/locale/en/LC_MESSAGES/django.po
src/newsletter/locale/es/LC_MESSAGES/django.mo
src/newsletter/locale/es/LC_MESSAGES/django.po
src/newsletter/locale/fr/LC_MESSAGES/django.mo
src/newsletter/locale/fr/LC_MESSAGES/django.po
src/newsletter/locale/it/LC_MESSAGES/django.mo
src/newsletter/locale/it/LC_MESSAGES/django.po
src/newsletter/locale/lt/LC_MESSAGES/django.mo
src/newsletter/locale/lt/LC_MESSAGES/django.po
src/newsletter/locale/pl/LC_MESSAGES/django.mo
src/newsletter/locale/pl/LC_MESSAGES/django.po
src/newsletter/locale/ru/LC_MESSAGES/django.mo
src/newsletter/locale/ru/LC_MESSAGES/django.po
src/newsletter/locale/uk/LC_MESSAGES/django.mo
src/newsletter/locale/uk/LC_MESSAGES/django.po
src/newsletter/templates/newsletter/subscribe_form.html
src/newtagging/models.py
src/paypal/forms.py
src/paypal/locale/de/LC_MESSAGES/django.mo [new file with mode: 0644]
src/paypal/locale/de/LC_MESSAGES/django.po [new file with mode: 0644]
src/paypal/locale/en/LC_MESSAGES/django.mo [new file with mode: 0644]
src/paypal/locale/en/LC_MESSAGES/django.po [new file with mode: 0644]
src/paypal/locale/es/LC_MESSAGES/django.mo [new file with mode: 0644]
src/paypal/locale/es/LC_MESSAGES/django.po [new file with mode: 0644]
src/paypal/locale/fr/LC_MESSAGES/django.mo [new file with mode: 0644]
src/paypal/locale/fr/LC_MESSAGES/django.po [new file with mode: 0644]
src/paypal/locale/it/LC_MESSAGES/django.mo [new file with mode: 0644]
src/paypal/locale/it/LC_MESSAGES/django.po [new file with mode: 0644]
src/paypal/locale/lt/LC_MESSAGES/django.mo [new file with mode: 0644]
src/paypal/locale/lt/LC_MESSAGES/django.po [new file with mode: 0644]
src/paypal/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
src/paypal/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
src/paypal/locale/ru/LC_MESSAGES/django.mo [new file with mode: 0644]
src/paypal/locale/ru/LC_MESSAGES/django.po [new file with mode: 0644]
src/paypal/locale/uk/LC_MESSAGES/django.mo [new file with mode: 0644]
src/paypal/locale/uk/LC_MESSAGES/django.po [new file with mode: 0644]
src/paypal/models.py
src/paypal/rest.py
src/paypal/templates/paypal/error.html
src/paypal/templates/paypal/form.html
src/paypal/urls.py
src/push/__init__.py [new file with mode: 0644]
src/push/admin.py [new file with mode: 0644]
src/push/forms.py [new file with mode: 0644]
src/push/migrations/0001_initial.py [new file with mode: 0644]
src/push/migrations/0002_auto_20180830_1627.py [new file with mode: 0644]
src/push/migrations/0003_auto_20180831_1135.py [new file with mode: 0644]
src/push/migrations/__init__.py [new file with mode: 0644]
src/push/models.py [new file with mode: 0644]
src/push/templates/push/notification_form.html [new file with mode: 0644]
src/push/templates/push/notification_sent.html [new file with mode: 0644]
src/push/urls.py [new file with mode: 0644]
src/push/utils.py [new file with mode: 0644]
src/push/views.py [new file with mode: 0644]
src/search/urls.py
src/wolnelektury/contact_forms.py
src/wolnelektury/settings/__init__.py
src/wolnelektury/settings/apps.py [new file with mode: 0644]
src/wolnelektury/settings/basic.py
src/wolnelektury/settings/contrib.py
src/wolnelektury/static/img/btn_subscribe_LG_pl.gif [new file with mode: 0644]
src/wolnelektury/static/scss/main/base.scss
src/wolnelektury/templates/base/superbase.html
src/wolnelektury/templates/contact/cojaczytam/form.html [new file with mode: 0644]
src/wolnelektury/urls.py

index 7dc1b2c..df1ef1a 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 7dc1b2c84ca9b53454d9eb0c296b4f005dcb780c
+Subproject commit df1ef1a80800b58ae6d818f5bdb0ff919c558d37
index 68d4d1e..2be42f8 100644 (file)
@@ -1,7 +1,8 @@
 -i https://py.mdrn.pl:8443/simple/
 
-django-debug-toolbar
+django-debug-toolbar<1.10
+django-debug-toolbar-template-timings
 Fabric
 sphinx
 pyinotify
-fnpdeploy>=0.2.2
+fnpdeploy>=0.2.3
index 7d22d7c..62c6180 100644 (file)
@@ -69,6 +69,8 @@ requests
 paypalrestsdk
 
 django-haystack<2.8.0
-django-migdal>=0.8.1
+django-migdal>=0.8.3
 
-python-slugify
\ No newline at end of file
+python-slugify
+
+firebase-admin
index b3edaef..d32981e 100644 (file)
@@ -6,6 +6,7 @@ import json
 
 from django.contrib.sites.models import Site
 from django.core.urlresolvers import reverse
+from django.db.models import Q
 from django.http.response import HttpResponse
 from django.utils.functional import lazy
 from django.db import models
@@ -30,8 +31,9 @@ from wolnelektury.utils import re_escape
 from . import emitters  # Register our emitters
 
 API_BASE = WL_BASE = MEDIA_BASE = lazy(
-    lambda: u'http://' + Site.objects.get_current().domain, unicode)()
+    lambda: u'https://' + Site.objects.get_current().domain, unicode)()
 
+SORT_KEY_SEP = '$'
 
 category_singular = {
     'authors': 'author',
@@ -47,6 +49,9 @@ for k, v in category_singular.items():
 
 book_tag_categories = ['author', 'epoch', 'kind', 'genre']
 
+book_list_fields = book_tag_categories + [
+    'href', 'title', 'url', 'cover', 'cover_thumb', 'slug', 'simple_thumb', 'has_audio', 'cover_color', 'full_sort_key']
+
 
 def read_tags(tags, request, allowed):
     """ Reads a path of filtering tags.
@@ -161,7 +166,30 @@ class BookDetails(object):
 
     @classmethod
     def cover_color(cls, book):
-        return WLCover.epoch_colors.get(book.extra_info['epoch'], '#000000')
+        return WLCover.epoch_colors.get(book.extra_info.get('epoch'), '#000000')
+
+    @classmethod
+    def full_sort_key(cls, book):
+        return '%s%s%s%s%s' % (book.sort_key_author, SORT_KEY_SEP, book.sort_key, SORT_KEY_SEP, book.id)
+
+    @staticmethod
+    def books_after(books, after, new_api):
+        if not new_api:
+            return books.filter(slug__gt=after)
+        try:
+            author, title, book_id = after.split(SORT_KEY_SEP)
+        except ValueError:
+            return Book.objects.none()
+        return books.filter(Q(sort_key_author__gt=author)
+                            | (Q(sort_key_author=author) & Q(sort_key__gt=title))
+                            | (Q(sort_key_author=author) & Q(sort_key=title) & Q(id__gt=int(book_id))))
+
+    @staticmethod
+    def order_books(books, new_api):
+        if new_api:
+            return books.order_by('sort_key_author', 'sort_key', 'id')
+        else:
+            return books.order_by('slug')
 
 
 class BookDetailHandler(BaseHandler, BookDetails):
@@ -191,8 +219,7 @@ class AnonymousBooksHandler(AnonymousBaseHandler, BookDetails):
     """
     allowed_methods = ('GET',)
     model = Book
-    fields = book_tag_categories + [
-        'href', 'title', 'url', 'cover', 'cover_thumb', 'slug', 'simple_thumb', 'has_audio', 'cover_color']
+    fields = book_list_fields
 
     @classmethod
     def genres(cls, book):
@@ -202,7 +229,7 @@ class AnonymousBooksHandler(AnonymousBaseHandler, BookDetails):
     @piwik_track
     def read(self, request, tags=None, top_level=False, audiobooks=False, daisy=False, pk=None,
              recommended=False, newest=False, books=None,
-             after=None, before=None, count=None):
+             after=None, count=None):
         """ Lists all books with given tags.
 
         :param tags: filtering tags; should be a path of categories
@@ -222,10 +249,9 @@ class AnonymousBooksHandler(AnonymousBaseHandler, BookDetails):
         except ValueError:
             return rc.NOT_FOUND
 
+        new_api = request.GET.get('new_api')
         if 'after' in request.GET:
             after = request.GET['after']
-        if 'before' in request.GET:
-            before = request.GET['before']
         if 'count' in request.GET:
             count = request.GET['count']
 
@@ -237,7 +263,7 @@ class AnonymousBooksHandler(AnonymousBaseHandler, BookDetails):
                 books = Book.tagged.with_all(tags)
         else:
             books = books if books is not None else Book.objects.all()
-        books = books.order_by('slug')
+        books = self.order_books(books, new_api)
 
         if top_level:
             books = books.filter(parent=None)
@@ -251,19 +277,17 @@ class AnonymousBooksHandler(AnonymousBaseHandler, BookDetails):
             books = books.order_by('-created_at')
 
         if after:
-            books = books.filter(slug__gt=after)
-        if before:
-            books = books.filter(slug__lt=before)
+            books = self.books_after(books, after, new_api)
 
-        books = books.only('slug', 'title', 'cover', 'cover_thumb')
+        if new_api:
+            books = books.only('slug', 'title', 'cover', 'cover_thumb', 'sort_key', 'sort_key_author')
+        else:
+            books = books.only('slug', 'title', 'cover', 'cover_thumb')
         for category in book_tag_categories:
             books = prefetch_relations(books, category)
 
         if count:
-            if before:
-                books = list(reversed(books.order_by('-slug')[:count]))
-            else:
-                books = books[:count]
+            books = books[:count]
 
         return books
 
@@ -274,9 +298,23 @@ class AnonymousBooksHandler(AnonymousBaseHandler, BookDetails):
 class BooksHandler(BookDetailHandler):
     allowed_methods = ('GET', 'POST')
     model = Book
-    fields = book_tag_categories + ['href', 'title', 'url', 'cover', 'cover_thumb', 'cover_color', 'slug']
+    fields = book_list_fields + ['liked']
     anonymous = AnonymousBooksHandler
 
+    # hack, because piston is stupid
+    @classmethod
+    def liked(cls, book):
+        return getattr(book, 'liked', None)
+
+    def read(self, request, **kwargs):
+        books = AnonymousBooksHandler().read(request, **kwargs)
+        likes = set(Book.tagged.with_any(request.user.tag_set.all()).values_list('id', flat=True))
+
+        new_books = [
+            BookProxy(book).set('liked', book.id in likes)
+            for book in books]
+        return QuerySetProxy(new_books)
+
     def create(self, request, *args, **kwargs):
         if not request.user.has_perm('catalogue.add_book'):
             return rc.FORBIDDEN
@@ -310,15 +348,16 @@ class BookProxy(models.Model):
     class Meta:
         managed = False
 
-    def __init__(self, book, key):
+    def __init__(self, book, key=None):
         self.book = book
         self.key = key
 
+    def set(self, attr, value):
+        self.__setattr__(attr, value)
+        return self
+
     def __getattr__(self, item):
-        if item not in ('book', 'key'):
-            return self.book.__getattribute__(item)
-        else:
-            return self.__getattribute__(item)
+        return self.book.__getattribute__(item)
 
 
 class QuerySetProxy(models.QuerySet):
@@ -329,9 +368,8 @@ class QuerySetProxy(models.QuerySet):
         return iter(self.list)
 
 
-class FilterBooksHandler(AnonymousBooksHandler):
-    fields = book_tag_categories + [
-        'href', 'title', 'url', 'cover', 'cover_thumb', 'cover_color', 'simple_thumb', 'has_audio', 'slug', 'key']
+class AnonFilterBooksHandler(AnonymousBooksHandler):
+    fields = book_list_fields + ['key']
 
     def parse_bool(self, s):
         if s in ('true', 'false'):
@@ -346,9 +384,10 @@ class FilterBooksHandler(AnonymousBooksHandler):
         is_audiobook = self.parse_bool(request.GET.get('audiobook'))
         preview = self.parse_bool(request.GET.get('preview'))
 
+        new_api = request.GET.get('new_api')
         after = request.GET.get('after')
         count = int(request.GET.get('count', 50))
-        books = Book.objects.distinct().order_by('slug')
+        books = self.order_books(Book.objects.distinct(), new_api)
         if is_lektura is not None:
             books = books.filter(has_audience=is_lektura)
         if is_audiobook is not None:
@@ -373,27 +412,29 @@ class FilterBooksHandler(AnonymousBooksHandler):
             books_title = books.filter(title__iregex='\m' + search_string)
             books_title = books_title.exclude(id__in=list(books_author.values_list('id', flat=True)))
             if after and (key_sep in after):
-                which, slug = after.split(key_sep, 1)
+                which, key = after.split(key_sep, 1)
                 if which == 'title':
-                    book_lists = [(books_title.filter(slug__gt=slug), 'title')]
+                    book_lists = [(self.books_after(books_title, key, new_api), 'title')]
                 else:  # which == 'author'
-                    book_lists = [(books_author.filter(slug__gt=slug), 'author'), (books_title, 'title')]
+                    book_lists = [(self.books_after(books_author, key, new_api), 'author'), (books_title, 'title')]
             else:
                 book_lists = [(books_author, 'author'), (books_title, 'title')]
         else:
             if after and key_sep in after:
-                which, slug = after.split(key_sep, 1)
-                books = books.filter(slug__gt=slug)
+                which, key = after.split(key_sep, 1)
+                books = self.books_after(books, key, new_api)
             book_lists = [(books, 'book')]
 
         filtered_books = []
         for book_list, label in book_lists:
-            book_list = book_list.only('slug', 'title', 'cover', 'cover_thumb')
+            book_list = book_list.only('slug', 'title', 'cover', 'cover_thumb', 'sort_key_author', 'sort_key')
             for category in book_tag_categories:
                 book_list = prefetch_relations(book_list, category)
             remaining_count = count - len(filtered_books)
-            new_books = [BookProxy(book, '%s%s%s' % (label, key_sep, book.slug))
-                         for book in book_list[:remaining_count]]
+            new_books = [
+                BookProxy(book, '%s%s%s' % (
+                    label, key_sep, book.slug if not new_api else self.full_sort_key(book)))
+                for book in book_list[:remaining_count]]
             filtered_books += new_books
             if len(filtered_books) == count:
                 break
@@ -401,6 +442,30 @@ class FilterBooksHandler(AnonymousBooksHandler):
         return QuerySetProxy(filtered_books)
 
 
+class FilterBooksHandler(BooksHandler):
+    anonymous = AnonFilterBooksHandler
+    fields = book_list_fields + ['key', 'liked']
+
+    # hack, because piston is stupid
+    @classmethod
+    def liked(cls, book):
+        return getattr(book, 'liked', None)
+
+    def read(self, request):
+        qsp = AnonFilterBooksHandler().read(request)
+        likes = set(Book.tagged.with_any(request.user.tag_set.all()).values_list('id', flat=True))
+        for book in qsp.list:
+            book.set('liked', book.id in likes)
+        return qsp
+
+
+class BookPreviewHandler(BookDetailHandler):
+    fields = BookDetailHandler.fields + ['slug']
+
+    def read(self, request):
+        return Book.objects.filter(preview=True)
+
+
 # add categorized tags fields for Book
 def _tags_getter(category):
     @classmethod
@@ -552,7 +617,6 @@ class TagsHandler(BaseHandler, TagDetails):
             return rc.NOT_FOUND
 
         after = request.GET.get('after')
-        before = request.GET.get('before')
         count = request.GET.get('count')
 
         tags = Tag.objects.filter(category=category_sng).exclude(items=None).order_by('slug')
@@ -566,14 +630,9 @@ class TagsHandler(BaseHandler, TagDetails):
 
         if after:
             tags = tags.filter(slug__gt=after)
-        if before:
-            tags = tags.filter(slug__lt=before)
 
         if count:
-            if before:
-                tags = list(reversed(tags.order_by('-slug')[:count]))
-            else:
-                tags = tags[:count]
+            tags = tags[:count]
 
         return tags
 
@@ -664,14 +723,14 @@ class PictureHandler(BaseHandler):
 
 class UserDataHandler(BaseHandler):
     model = BookUserData
-    fields = ('state', 'username')
+    fields = ('state', 'username', 'premium')
     allowed_methods = ('GET', 'POST')
 
     def read(self, request, slug=None):
         if not request.user.is_authenticated():
             return rc.FORBIDDEN
         if slug is None:
-            return {'username': request.user.username}
+            return {'username': request.user.username, 'premium': is_subscribed(request.user)}
         try:
             book = Book.objects.get(slug=slug)
         except Book.DoesNotExist:
@@ -698,8 +757,7 @@ class UserDataHandler(BaseHandler):
 
 
 class UserShelfHandler(BookDetailHandler):
-    fields = book_tag_categories + [
-        'href', 'title', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'slug', 'key']
+    fields = book_list_fields + ['liked']
 
     def parse_bool(self, s):
         if s in ('true', 'false'):
@@ -707,11 +765,18 @@ class UserShelfHandler(BookDetailHandler):
         else:
             return None
 
+    # hack, because piston is stupid
+    @classmethod
+    def liked(cls, book):
+        return getattr(book, 'liked', None)
+
     def read(self, request, state):
         if not request.user.is_authenticated():
             return rc.FORBIDDEN
+        likes = set(Book.tagged.with_any(request.user.tag_set.all()).values_list('id', flat=True))
         if state not in ('reading', 'complete', 'likes'):
             return rc.NOT_FOUND
+        new_api = request.GET.get('new_api')
         after = request.GET.get('after')
         count = int(request.GET.get('count', 50))
         if state == 'likes':
@@ -719,12 +784,16 @@ class UserShelfHandler(BookDetailHandler):
         else:
             ids = BookUserData.objects.filter(user=request.user, complete=state == 'complete')\
                 .values_list('book_id', flat=True)
-            books = Book.objects.filter(id__in=list(ids)).distinct().order_by('slug')
+            books = Book.objects.filter(id__in=list(ids)).distinct()
+            books = self.order_books(books, new_api)
         if after:
-            books = books.filter(slug__gt=after)
+            books = self.books_after(books, after, new_api)
         if count:
             books = books[:count]
-        return books
+        new_books = []
+        for book in books:
+            new_books.append(BookProxy(book).set('liked', book.id in likes))
+        return QuerySetProxy(new_books)
 
 
 class UserLikeHandler(BaseHandler):
@@ -740,13 +809,14 @@ class UserLikeHandler(BaseHandler):
             return rc.NOT_FOUND
         return {'likes': likes(request.user, book)}
 
-    def create(self, request, slug, action='like'):
+    def create(self, request, slug):
         if not request.user.is_authenticated():
             return rc.FORBIDDEN
         try:
             book = Book.objects.get(slug=slug)
         except Book.DoesNotExist:
             return rc.NOT_FOUND
+        action = request.GET.get('action', 'like')
         if action == 'like':
             book.like(request.user)
         elif action == 'unlike':
@@ -756,7 +826,8 @@ class UserLikeHandler(BaseHandler):
 
 class BlogEntryHandler(BaseHandler):
     model = Entry
-    fields = ('title', 'lead', 'body', 'place', 'time', 'image_url', 'gallery_urls', 'type', 'key')
+    fields = (
+        'title', 'lead', 'body', 'place', 'time', 'image_url', 'image_thumb', 'gallery_urls', 'type', 'key', 'url')
 
     def read(self, request):
         after = request.GET.get('after')
@@ -770,12 +841,21 @@ class BlogEntryHandler(BaseHandler):
 
     @classmethod
     def image_url(cls, entry):
-        return entry.image.url if entry.image else None
+        return (WL_BASE + entry.image.url) if entry.image else None
+
+    @classmethod
+    def image_thumb(cls, entry):
+        return MEDIA_BASE + default.backend.get_thumbnail(
+            entry.image, "193x193").url if entry.image else ''
 
     @classmethod
     def gallery_urls(cls, entry):
-        return [photo.url() for photo in entry.photo_set.all()]
+        return [WL_BASE + photo.url() for photo in entry.photo_set.all()]
 
     @classmethod
     def key(cls, entry):
         return entry.first_published_at
+
+    @classmethod
+    def url(cls, entry):
+        return WL_BASE + entry.get_absolute_url()
diff --git a/src/api/migrations/0004_bookuserdata_last_changed.py b/src/api/migrations/0004_bookuserdata_last_changed.py
new file mode 100644 (file)
index 0000000..f42c405
--- /dev/null
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import datetime
+from django.utils.timezone import utc
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('api', '0003_bookuserdata'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='bookuserdata',
+            name='last_changed',
+            field=models.DateTimeField(default=datetime.datetime(2018, 11, 28, 14, 41, 2, 673054, tzinfo=utc), auto_now=True),
+            preserve_default=False,
+        ),
+    ]
index cc71a06..2481010 100644 (file)
@@ -45,6 +45,7 @@ class BookUserData(models.Model):
     book = models.ForeignKey(Book)
     user = models.ForeignKey(User)
     complete = models.BooleanField(default=False)
+    last_changed = models.DateTimeField(auto_now=True)
 
     def get_state(self):
         return 'complete' if self.complete else 'reading'
index e395553..bb0c1f4 100644 (file)
@@ -16,7 +16,27 @@ from api.piston_patch import oauth_user_auth
 auth = OAuthAuthentication(realm="Wolne Lektury")
 
 
+class DjangoAuthentication(object):
+    """
+    Authentication handler that always returns
+    True, so no authentication is needed, nor
+    initiated (`challenge` is missing.)
+    """
+    def is_authenticated(self, request):
+        return request.user.is_authenticated()
+
+    def challenge(self):
+        from django.http import HttpResponse
+        resp = HttpResponse("Authorization Required")
+        resp.status_code = 401
+        return resp
+
+
 def auth_resource(handler):
+    from django.conf import settings
+    if settings.DEBUG:
+        django_auth = DjangoAuthentication()
+        return CsrfExemptResource(handler=handler, authentication=django_auth)
     return CsrfExemptResource(handler=handler, authentication=auth)
 
 
@@ -24,9 +44,11 @@ book_list_resource = auth_resource(handler=handlers.BooksHandler)
 ebook_list_resource = Resource(handler=handlers.EBooksHandler)
 # book_list_resource = Resource(handler=handlers.BooksHandler)
 book_resource = Resource(handler=handlers.BookDetailHandler)
-filter_book_resource = Resource(handler=handlers.FilterBooksHandler)
+filter_book_resource = auth_resource(handler=handlers.FilterBooksHandler)
 epub_resource = auth_resource(handler=handlers.EpubHandler)
 
+preview_resource = Resource(handler=handlers.BookPreviewHandler)
+
 reading_resource = auth_resource(handler=handlers.UserDataHandler)
 shelf_resource = auth_resource(handler=handlers.UserShelfHandler)
 
@@ -47,7 +69,7 @@ blog_resource = Resource(handler=handlers.BlogEntryHandler)
 
 
 tags_re = r'^(?P<tags>(?:(?:[a-z0-9-]+/){2}){0,6})'
-paginate_re = r'(?:before/(?P<before>[a-z0-9-]+)/)?(?:after/(?P<after>[a-z0-9-]+)/)?(?:count/(?P<count>[0-9]+)/)?$'
+paginate_re = r'(?:after/(?P<after>[a-z0-9-]+)/)?(?:count/(?P<count>[0-9]+)/)?$'
 
 
 @ssi_included
@@ -115,8 +137,10 @@ urlpatterns = [
         book_list_resource, {"daisy": True}, name='api_daisy_list'),
 
     url(r'^recommended/' + paginate_re, book_list_resource, {"recommended": True}, name='api_recommended_list'),
-    url(r'^newest/', book_list_resource, {"newest": True, "top_level": True, "count": 20}, name='api_newest_list'),
-    url(r'^filter-books/', filter_book_resource, name='api_filter_books'),
+    url(r'^newest/$', book_list_resource, {"newest": True, "top_level": True, "count": 20}, name='api_newest_list'),
+    url(r'^filter-books/$', filter_book_resource, name='api_filter_books'),
+
+    url(r'^preview/$', preview_resource, name='api_preview'),
 
     url(r'^pictures/$', picture_resource),
 
index 420ea63..ec010a5 100644 (file)
@@ -14,12 +14,17 @@ class Command(BaseCommand):
         parser.add_argument('--width', type=int)
         parser.add_argument('--height', type=int)
         parser.add_argument('--bleed', action='store_true')
+        parser.add_argument('--cover-class', type=str)
 
     def handle(self, *args, **options):
         slug = options['slug']
         width = options['width']
         height = options.get('height')
+        cover_class = options.get('cover_class')
         bleed = 20 if options['bleed'] else 0
         wldoc = Book.objects.get(slug=slug).wldocument()
-        cover = make_cover(wldoc.book_info, width=width, height=height, bleed=bleed)
+        kwargs = {}
+        if cover_class:
+            kwargs['cover_class'] = cover_class
+        cover = make_cover(wldoc.book_info, width=width, height=height, bleed=bleed, **kwargs)
         cover.save('%s.jpg' % slug)
index b71926e..dbbee78 100644 (file)
@@ -754,7 +754,10 @@ class Book(models.Model):
     def fragment_data(self):
         fragment = self.choose_fragment()
         if fragment:
-            return {'title': fragment.book.pretty_title(), 'html': fragment.get_short_text()}
+            return {
+                'title': fragment.book.pretty_title(),
+                'html': re.sub('</?blockquote[^>]*>', '', fragment.get_short_text()),
+            }
         else:
             return None
 
index 2145312..9878a70 100644 (file)
@@ -20,6 +20,7 @@ from django.core.files.uploadedfile import UploadedFile
 from django.http import HttpResponse
 from django.utils.encoding import force_unicode
 
+from paypal.rest import user_is_subscribed
 from reporting.utils import read_chunks
 
 # Use the system (hardware-based) random number generator if it exists.
@@ -357,4 +358,4 @@ def gallery_url(slug):
 
 
 def is_subscribed(user):
-    return user.is_authenticated()  # TEMPORARY
+    return user_is_subscribed(user)
index bfd4209..2d578e6 100644 (file)
@@ -2,18 +2,10 @@
 
 from hashlib import md5
 
-import requests
 from django.conf import settings
 from mailchimp3 import MailChimp
 from mailchimp3.mailchimpclient import MailChimpError
 
-INTERESTS = {settings.MAILCHIMP_GROUP_ID: True}
-
-
-def get_client():
-    headers = requests.utils.default_headers()
-    headers['User-Agent'] = '%s (%s)' % settings.MANAGERS[0]
-
 
 def subscriber_hash(email):
     return md5(email).hexdigest()
@@ -32,7 +24,7 @@ def remove_from_groups(email, client):
          data={'interests': interests})
 
 
-def subscribe(email):
+def subscribe(email, mailing_lists=None):
     client = MailChimp(mc_api=settings.MAILCHIMP_API_KEY, timeout=10.0)
     try:
         member = client.lists.members.get(settings.MAILCHIMP_LIST_ID, subscriber_hash(email))
@@ -41,12 +33,41 @@ def subscribe(email):
     else:
         if member['status'] != 'subscribed':
             remove_from_groups(email, client)
+    mailing_lists = mailing_lists or [settings.MAILCHIMP_DEFAULT_GROUP]
+    interests = {
+        settings.MAILCHIMP_GROUP_IDS[mailing_list]: True
+        for mailing_list in mailing_lists
+        if mailing_list in settings.MAILCHIMP_GROUP_IDS
+    }
     client.lists.members.create_or_update(
         settings.MAILCHIMP_LIST_ID, subscriber_hash(email),
         data={
             'email_address': email,
             'status_if_new': 'subscribed',
             'status': 'subscribed',
-            'interests': INTERESTS,
+            'interests': interests,
+        }
+    )
+
+
+def unsubscribe(email, mailing_lists=None):
+    client = MailChimp(mc_api=settings.MAILCHIMP_API_KEY, timeout=10.0)
+    try:
+        member = client.lists.members.get(settings.MAILCHIMP_LIST_ID, subscriber_hash(email))
+    except MailChimpError:
+        return
+    else:
+        if member['status'] != 'subscribed':
+            return
+    mailing_lists = mailing_lists or settings.MAILCHIMP_GROUP_IDS
+    interests = {
+        settings.MAILCHIMP_GROUP_IDS[mailing_list]: False
+        for mailing_list in mailing_lists
+        if mailing_list in settings.MAILCHIMP_GROUP_IDS
+    }
+    client.lists.members.update(
+        settings.MAILCHIMP_LIST_ID, subscriber_hash(email),
+        data={
+            'interests': interests,
         }
     )
index adffafa..0ab8201 100644 (file)
@@ -36,6 +36,17 @@ class Contact(models.Model):
         data = '%s%s%s%s%s' % (self.id, self.contact, serialized_body, self.ip, self.form_tag)
         return sha1(data).hexdigest()
 
+    def keys(self):
+        try:
+            from .views import contact_forms
+            orig_fields = contact_forms[self.form_tag]().fields
+        except KeyError:
+            orig_fields = {}
+        return list(orig_fields.keys())
+
+    def items(self):
+        return [(key, self.body[key]) for key in self.keys() if key in self.body]
+
 
 class Attachment(models.Model):
     contact = models.ForeignKey(Contact)
index b65ebd8..3a4297c 100644 (file)
@@ -2,7 +2,7 @@
 
 https://{{site_domain}}{% url 'admin:contact_contact_change' contact.pk %}
 
-{% for k, v in contact.body.items %}
+{% for k, v in contact.items %}
 {{ k }}:
 {{ v|pretty_print }}
 {% endfor %}
index eb7afa5..d473cf3 100644 (file)
@@ -1,8 +1,9 @@
 # -*- coding: utf-8 -*-
 from django.core.exceptions import ValidationError
 from django.core.validators import validate_email
-from django.forms import Form, BooleanField
+from django.forms import Form, BooleanField, MultipleChoiceField
 from django.forms.fields import EmailField
+from django.forms.widgets import CheckboxSelectMultiple
 from django.template.loader import render_to_string
 from django.utils.safestring import mark_safe
 from django.utils.translation import ugettext_lazy as _, ugettext
@@ -16,6 +17,8 @@ class NewsletterForm(Form):
     email_field = 'email'
     agree_newsletter = BooleanField(
         required=False, initial=False, label=_(u'I want to receive Wolne Lektury\'s newsletter.'))
+    mailing = False
+    mailing_field = 'agree_newsletter'
 
     data_processing_part1 = u'''\
 Administratorem danych osobowych jest Fundacja Nowoczesna Polska (ul. Marszałkowska 84/92 lok. 125, 00-514 Warszawa).
@@ -35,7 +38,7 @@ Więcej informacji w <a href="">polityce prywatności.</a>'''
             super(NewsletterForm, self).save(*args, **kwargs)
         except AttributeError:
             pass
-        if not self.cleaned_data.get('agree_newsletter'):
+        if not (self.mailing or self.cleaned_data.get(self.mailing_field)):
             return
         email = self.cleaned_data[self.email_field]
         try:
@@ -47,15 +50,21 @@ Więcej informacji w <a href="">polityce prywatności.</a>'''
             # send_noreply_mail(
             #     ugettext(u'Confirm your subscription to Wolne Lektury newsletter'),
             #     render_to_string('newsletter/subscribe_email.html', {'subscription': subscription}), [email])
-            mailing.subscribe(email)
+            mailing.subscribe(email, mailing_lists=self.cleaned_data.get('mailing_lists'))
 
 
 class SubscribeForm(NewsletterForm):
+    mailing = True
+    agree_newsletter = None
+
     email = EmailField(label=_('email address'))
+    mailing_lists = MultipleChoiceField(
+        widget=CheckboxSelectMultiple,
+        choices=(('general', _(u'general newsletter')), ('contest', _(u'about the contest'))),
+        label=_(u'mailing list'))
 
     def __init__(self, *args, **kwargs):
         super(SubscribeForm, self).__init__(*args, **kwargs)
-        self.fields['agree_newsletter'].required = True
 
 
 class UnsubscribeForm(Form):
@@ -73,6 +82,7 @@ class UnsubscribeForm(Form):
         subscription = self.cleaned_data['subscription']
         subscription.active = False
         subscription.save()
+        mailing.unsubscribe(subscription.email)
 
         context = {'subscription': subscription}
         # refactor to send_noreply_mail
index 06f3fb7..71cbdf3 100644 (file)
Binary files a/src/newsletter/locale/de/LC_MESSAGES/django.mo and b/src/newsletter/locale/de/LC_MESSAGES/django.mo differ
index 482fe19..9b804bf 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 17:12+0200\n"
+"POT-Creation-Date: 2018-09-28 11:23+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,19 +18,31 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: forms.py:17
+#: forms.py:19
 msgid "I want to receive Wolne Lektury's newsletter."
 msgstr ""
 
-#: forms.py:46 forms.py:54 models.py:10
+#: forms.py:60 forms.py:71 models.py:10
 msgid "email address"
 msgstr ""
 
-#: forms.py:61
+#: forms.py:63
+msgid "general newsletter"
+msgstr ""
+
+#: forms.py:63
+msgid "about the contest"
+msgstr ""
+
+#: forms.py:64
+msgid "mailing list"
+msgstr ""
+
+#: forms.py:78
 msgid "Email address not found."
 msgstr ""
 
-#: forms.py:72
+#: forms.py:89
 msgid "Unsubscribe from Wolne Lektury's newsletter."
 msgstr ""
 
@@ -54,7 +66,7 @@ msgstr ""
 msgid "Your subscription to Wolne Lektury newsletter is confirmed. Thank you!"
 msgstr ""
 
-#: templates/newsletter/subscribe_form.html:15
+#: templates/newsletter/subscribe_form.html:16
 msgid "Subscribe"
 msgstr ""
 
index f364c60..6c5906d 100644 (file)
Binary files a/src/newsletter/locale/en/LC_MESSAGES/django.mo and b/src/newsletter/locale/en/LC_MESSAGES/django.mo differ
index d9a4b2d..8d23cff 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 17:12+0200\n"
+"POT-Creation-Date: 2018-09-28 11:23+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,19 +17,31 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: forms.py:17
+#: forms.py:19
 msgid "I want to receive Wolne Lektury's newsletter."
 msgstr ""
 
-#: forms.py:46 forms.py:54 models.py:10
+#: forms.py:60 forms.py:71 models.py:10
 msgid "email address"
 msgstr ""
 
-#: forms.py:61
+#: forms.py:63
+msgid "general newsletter"
+msgstr ""
+
+#: forms.py:63
+msgid "about the contest"
+msgstr ""
+
+#: forms.py:64
+msgid "mailing list"
+msgstr ""
+
+#: forms.py:78
 msgid "Email address not found."
 msgstr ""
 
-#: forms.py:72
+#: forms.py:89
 msgid "Unsubscribe from Wolne Lektury's newsletter."
 msgstr ""
 
@@ -53,7 +65,7 @@ msgstr ""
 msgid "Your subscription to Wolne Lektury newsletter is confirmed. Thank you!"
 msgstr ""
 
-#: templates/newsletter/subscribe_form.html:15
+#: templates/newsletter/subscribe_form.html:16
 msgid "Subscribe"
 msgstr ""
 
index 06f3fb7..71cbdf3 100644 (file)
Binary files a/src/newsletter/locale/es/LC_MESSAGES/django.mo and b/src/newsletter/locale/es/LC_MESSAGES/django.mo differ
index 482fe19..9b804bf 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 17:12+0200\n"
+"POT-Creation-Date: 2018-09-28 11:23+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,19 +18,31 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: forms.py:17
+#: forms.py:19
 msgid "I want to receive Wolne Lektury's newsletter."
 msgstr ""
 
-#: forms.py:46 forms.py:54 models.py:10
+#: forms.py:60 forms.py:71 models.py:10
 msgid "email address"
 msgstr ""
 
-#: forms.py:61
+#: forms.py:63
+msgid "general newsletter"
+msgstr ""
+
+#: forms.py:63
+msgid "about the contest"
+msgstr ""
+
+#: forms.py:64
+msgid "mailing list"
+msgstr ""
+
+#: forms.py:78
 msgid "Email address not found."
 msgstr ""
 
-#: forms.py:72
+#: forms.py:89
 msgid "Unsubscribe from Wolne Lektury's newsletter."
 msgstr ""
 
@@ -54,7 +66,7 @@ msgstr ""
 msgid "Your subscription to Wolne Lektury newsletter is confirmed. Thank you!"
 msgstr ""
 
-#: templates/newsletter/subscribe_form.html:15
+#: templates/newsletter/subscribe_form.html:16
 msgid "Subscribe"
 msgstr ""
 
index 1090d01..2c90dd0 100644 (file)
Binary files a/src/newsletter/locale/fr/LC_MESSAGES/django.mo and b/src/newsletter/locale/fr/LC_MESSAGES/django.mo differ
index 271e7a9..26887f3 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 17:12+0200\n"
+"POT-Creation-Date: 2018-09-28 11:23+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,19 +18,31 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 
-#: forms.py:17
+#: forms.py:19
 msgid "I want to receive Wolne Lektury's newsletter."
 msgstr ""
 
-#: forms.py:46 forms.py:54 models.py:10
+#: forms.py:60 forms.py:71 models.py:10
 msgid "email address"
 msgstr ""
 
-#: forms.py:61
+#: forms.py:63
+msgid "general newsletter"
+msgstr ""
+
+#: forms.py:63
+msgid "about the contest"
+msgstr ""
+
+#: forms.py:64
+msgid "mailing list"
+msgstr ""
+
+#: forms.py:78
 msgid "Email address not found."
 msgstr ""
 
-#: forms.py:72
+#: forms.py:89
 msgid "Unsubscribe from Wolne Lektury's newsletter."
 msgstr ""
 
@@ -54,7 +66,7 @@ msgstr ""
 msgid "Your subscription to Wolne Lektury newsletter is confirmed. Thank you!"
 msgstr ""
 
-#: templates/newsletter/subscribe_form.html:15
+#: templates/newsletter/subscribe_form.html:16
 msgid "Subscribe"
 msgstr ""
 
index 06f3fb7..71cbdf3 100644 (file)
Binary files a/src/newsletter/locale/it/LC_MESSAGES/django.mo and b/src/newsletter/locale/it/LC_MESSAGES/django.mo differ
index 482fe19..9b804bf 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 17:12+0200\n"
+"POT-Creation-Date: 2018-09-28 11:23+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,19 +18,31 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: forms.py:17
+#: forms.py:19
 msgid "I want to receive Wolne Lektury's newsletter."
 msgstr ""
 
-#: forms.py:46 forms.py:54 models.py:10
+#: forms.py:60 forms.py:71 models.py:10
 msgid "email address"
 msgstr ""
 
-#: forms.py:61
+#: forms.py:63
+msgid "general newsletter"
+msgstr ""
+
+#: forms.py:63
+msgid "about the contest"
+msgstr ""
+
+#: forms.py:64
+msgid "mailing list"
+msgstr ""
+
+#: forms.py:78
 msgid "Email address not found."
 msgstr ""
 
-#: forms.py:72
+#: forms.py:89
 msgid "Unsubscribe from Wolne Lektury's newsletter."
 msgstr ""
 
@@ -54,7 +66,7 @@ msgstr ""
 msgid "Your subscription to Wolne Lektury newsletter is confirmed. Thank you!"
 msgstr ""
 
-#: templates/newsletter/subscribe_form.html:15
+#: templates/newsletter/subscribe_form.html:16
 msgid "Subscribe"
 msgstr ""
 
index 914b4b5..50c6184 100644 (file)
Binary files a/src/newsletter/locale/lt/LC_MESSAGES/django.mo and b/src/newsletter/locale/lt/LC_MESSAGES/django.mo differ
index 478deff..b34be27 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 17:12+0200\n"
+"POT-Creation-Date: 2018-09-28 11:23+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -19,19 +19,31 @@ msgstr ""
 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n"
 "%100<10 || n%100>=20) ? 1 : 2);\n"
 
-#: forms.py:17
+#: forms.py:19
 msgid "I want to receive Wolne Lektury's newsletter."
 msgstr ""
 
-#: forms.py:46 forms.py:54 models.py:10
+#: forms.py:60 forms.py:71 models.py:10
 msgid "email address"
 msgstr ""
 
-#: forms.py:61
+#: forms.py:63
+msgid "general newsletter"
+msgstr ""
+
+#: forms.py:63
+msgid "about the contest"
+msgstr ""
+
+#: forms.py:64
+msgid "mailing list"
+msgstr ""
+
+#: forms.py:78
 msgid "Email address not found."
 msgstr ""
 
-#: forms.py:72
+#: forms.py:89
 msgid "Unsubscribe from Wolne Lektury's newsletter."
 msgstr ""
 
@@ -55,7 +67,7 @@ msgstr ""
 msgid "Your subscription to Wolne Lektury newsletter is confirmed. Thank you!"
 msgstr ""
 
-#: templates/newsletter/subscribe_form.html:15
+#: templates/newsletter/subscribe_form.html:16
 msgid "Subscribe"
 msgstr ""
 
index c5f0322..0abba0a 100644 (file)
Binary files a/src/newsletter/locale/pl/LC_MESSAGES/django.mo and b/src/newsletter/locale/pl/LC_MESSAGES/django.mo differ
index 95ea219..87bf45b 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 17:12+0200\n"
+"POT-Creation-Date: 2018-09-28 11:23+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: Jan Szejko <jan.szejko@nowoczesnapolska.org.pl>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,19 +18,31 @@ msgstr ""
 "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
 "|| n%100>=20) ? 1 : 2);\n"
 
-#: forms.py:17
+#: forms.py:19
 msgid "I want to receive Wolne Lektury's newsletter."
 msgstr "Chcę otrzymywać newsletter Wolnych Lektur"
 
-#: forms.py:46 forms.py:54 models.py:10
+#: forms.py:60 forms.py:71 models.py:10
 msgid "email address"
 msgstr "adres email"
 
-#: forms.py:61
+#: forms.py:63
+msgid "general newsletter"
+msgstr "ogólny newsletter"
+
+#: forms.py:63
+msgid "about the contest"
+msgstr "informacje o konkursie"
+
+#: forms.py:64
+msgid "mailing list"
+msgstr "lista mailowa"
+
+#: forms.py:78
 msgid "Email address not found."
 msgstr "Nie znaleziono adresu email."
 
-#: forms.py:72
+#: forms.py:89
 msgid "Unsubscribe from Wolne Lektury's newsletter."
 msgstr "Wypisuję się z newslettera Wolnych Lektur"
 
@@ -54,7 +66,7 @@ msgstr "Ekstrakt"
 msgid "Your subscription to Wolne Lektury newsletter is confirmed. Thank you!"
 msgstr "Potwierdzono subskrypcję newslettera Wolnych Lektur. Dziękujemy!"
 
-#: templates/newsletter/subscribe_form.html:15
+#: templates/newsletter/subscribe_form.html:16
 msgid "Subscribe"
 msgstr "Zapisz się"
 
@@ -79,7 +91,6 @@ msgid "Subscribe To Newsletter"
 msgstr "Zapisz się na newsletter"
 
 #: views.py:28
-#| msgid "Subscribe"
 msgid "Subscribed"
 msgstr "Zapisano do newslettera"
 
index a609786..d2d31a4 100644 (file)
Binary files a/src/newsletter/locale/ru/LC_MESSAGES/django.mo and b/src/newsletter/locale/ru/LC_MESSAGES/django.mo differ
index bbd427f..8c408eb 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 17:12+0200\n"
+"POT-Creation-Date: 2018-09-28 11:23+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -19,19 +19,31 @@ msgstr ""
 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
 
-#: forms.py:17
+#: forms.py:19
 msgid "I want to receive Wolne Lektury's newsletter."
 msgstr ""
 
-#: forms.py:46 forms.py:54 models.py:10
+#: forms.py:60 forms.py:71 models.py:10
 msgid "email address"
 msgstr ""
 
-#: forms.py:61
+#: forms.py:63
+msgid "general newsletter"
+msgstr ""
+
+#: forms.py:63
+msgid "about the contest"
+msgstr ""
+
+#: forms.py:64
+msgid "mailing list"
+msgstr ""
+
+#: forms.py:78
 msgid "Email address not found."
 msgstr ""
 
-#: forms.py:72
+#: forms.py:89
 msgid "Unsubscribe from Wolne Lektury's newsletter."
 msgstr ""
 
@@ -55,7 +67,7 @@ msgstr ""
 msgid "Your subscription to Wolne Lektury newsletter is confirmed. Thank you!"
 msgstr ""
 
-#: templates/newsletter/subscribe_form.html:15
+#: templates/newsletter/subscribe_form.html:16
 msgid "Subscribe"
 msgstr ""
 
index a609786..d2d31a4 100644 (file)
Binary files a/src/newsletter/locale/uk/LC_MESSAGES/django.mo and b/src/newsletter/locale/uk/LC_MESSAGES/django.mo differ
index bbd427f..8c408eb 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-04-04 17:12+0200\n"
+"POT-Creation-Date: 2018-09-28 11:23+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -19,19 +19,31 @@ msgstr ""
 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
 
-#: forms.py:17
+#: forms.py:19
 msgid "I want to receive Wolne Lektury's newsletter."
 msgstr ""
 
-#: forms.py:46 forms.py:54 models.py:10
+#: forms.py:60 forms.py:71 models.py:10
 msgid "email address"
 msgstr ""
 
-#: forms.py:61
+#: forms.py:63
+msgid "general newsletter"
+msgstr ""
+
+#: forms.py:63
+msgid "about the contest"
+msgstr ""
+
+#: forms.py:64
+msgid "mailing list"
+msgstr ""
+
+#: forms.py:78
 msgid "Email address not found."
 msgstr ""
 
-#: forms.py:72
+#: forms.py:89
 msgid "Unsubscribe from Wolne Lektury's newsletter."
 msgstr ""
 
@@ -55,7 +67,7 @@ msgstr ""
 msgid "Your subscription to Wolne Lektury newsletter is confirmed. Thank you!"
 msgstr ""
 
-#: templates/newsletter/subscribe_form.html:15
+#: templates/newsletter/subscribe_form.html:16
 msgid "Subscribe"
 msgstr ""
 
index 203e54c..c0379f0 100644 (file)
@@ -11,7 +11,7 @@
     {% render_honeypot_field %}
     <ol>
       <li>{{ form.email|pretty_field }}</li>
-      <li>{{ form.agree_newsletter|pretty_checkbox }}</li>
+      <li>{{ form.mailing_lists|pretty_field }}</li>
       <li><span class="helptext">{{ form.data_processing }}</span></li>
       <li><input type="submit" value="{% trans "Subscribe" %}"/></li>
     </ol>
index 9d12111..b90ea8e 100644 (file)
@@ -64,7 +64,10 @@ class TagManager(models.Manager):
         # Add new tags
         tags_to_add = [tag for tag in updated_tags if tag not in current_tags]
         for tag in tags_to_add:
-            self.intermediary_table_model.objects.create(tag=tag, content_object=obj)
+            existing = self.intermediary_table_model.objects.filter(
+                content_type__pk=content_type.pk, object_id=obj.pk, tag=tag)
+            if not existing:
+                self.intermediary_table_model.objects.create(tag=tag, content_object=obj)
 
         tags_updated.send(sender=type(obj), instance=obj, affected_tags=tags_to_add + tags_for_removal)
 
index 849810d..4cc7660 100644 (file)
@@ -7,4 +7,4 @@ from django.utils.translation import ugettext_lazy as _
 
 
 class PaypalSubscriptionForm(forms.Form):
-    amount = forms.IntegerField(min_value=10, max_value=30000, initial=20, label=_('amount'))
+    amount = forms.IntegerField(min_value=5, max_value=30000, initial=20, label=_('amount in PLN'))
diff --git a/src/paypal/locale/de/LC_MESSAGES/django.mo b/src/paypal/locale/de/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..68082f1
Binary files /dev/null and b/src/paypal/locale/de/LC_MESSAGES/django.mo differ
diff --git a/src/paypal/locale/de/LC_MESSAGES/django.po b/src/paypal/locale/de/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..b1136e4
--- /dev/null
@@ -0,0 +1,63 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-08-30 15:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: forms.py:10
+msgid "amount in PLN"
+msgstr ""
+
+#: templates/paypal/cancel.html:5
+msgid "Zrezygnowano z płatności :("
+msgstr ""
+
+#: templates/paypal/error.html:1
+msgid "PayPal Error"
+msgstr ""
+
+#: templates/paypal/error.html:5
+msgid "Learn more"
+msgstr ""
+
+#: templates/paypal/form.html:6 templates/paypal/form.html.py:9
+msgid "Wolne Lektury Friend Club"
+msgstr ""
+
+#: templates/paypal/form.html:17
+msgid "Subscribe with PayPal"
+msgstr ""
+
+#: templates/paypal/form.html:20
+msgid "You must be logged in to subscribe."
+msgstr ""
+
+#: templates/paypal/return.html:8
+msgid "Dziękujemy, że jesteś z nami i pomagasz nam rozwijać Wolne Lektury!"
+msgstr ""
+
+#: templates/paypal/return.html:9
+msgid "Pamiętaj, że zawsze możesz się z nami skontaktować:"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Do przeczytania!"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Zespół Wolnych Lektur"
+msgstr ""
diff --git a/src/paypal/locale/en/LC_MESSAGES/django.mo b/src/paypal/locale/en/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..9df4926
Binary files /dev/null and b/src/paypal/locale/en/LC_MESSAGES/django.mo differ
diff --git a/src/paypal/locale/en/LC_MESSAGES/django.po b/src/paypal/locale/en/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..1f7a225
--- /dev/null
@@ -0,0 +1,62 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-08-30 15:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:10
+msgid "amount in PLN"
+msgstr ""
+
+#: templates/paypal/cancel.html:5
+msgid "Zrezygnowano z płatności :("
+msgstr ""
+
+#: templates/paypal/error.html:1
+msgid "PayPal Error"
+msgstr ""
+
+#: templates/paypal/error.html:5
+msgid "Learn more"
+msgstr ""
+
+#: templates/paypal/form.html:6 templates/paypal/form.html.py:9
+msgid "Wolne Lektury Friend Club"
+msgstr ""
+
+#: templates/paypal/form.html:17
+msgid "Subscribe with PayPal"
+msgstr ""
+
+#: templates/paypal/form.html:20
+msgid "You must be logged in to subscribe."
+msgstr ""
+
+#: templates/paypal/return.html:8
+msgid "Dziękujemy, że jesteś z nami i pomagasz nam rozwijać Wolne Lektury!"
+msgstr ""
+
+#: templates/paypal/return.html:9
+msgid "Pamiętaj, że zawsze możesz się z nami skontaktować:"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Do przeczytania!"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Zespół Wolnych Lektur"
+msgstr ""
diff --git a/src/paypal/locale/es/LC_MESSAGES/django.mo b/src/paypal/locale/es/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..68082f1
Binary files /dev/null and b/src/paypal/locale/es/LC_MESSAGES/django.mo differ
diff --git a/src/paypal/locale/es/LC_MESSAGES/django.po b/src/paypal/locale/es/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..b1136e4
--- /dev/null
@@ -0,0 +1,63 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-08-30 15:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: forms.py:10
+msgid "amount in PLN"
+msgstr ""
+
+#: templates/paypal/cancel.html:5
+msgid "Zrezygnowano z płatności :("
+msgstr ""
+
+#: templates/paypal/error.html:1
+msgid "PayPal Error"
+msgstr ""
+
+#: templates/paypal/error.html:5
+msgid "Learn more"
+msgstr ""
+
+#: templates/paypal/form.html:6 templates/paypal/form.html.py:9
+msgid "Wolne Lektury Friend Club"
+msgstr ""
+
+#: templates/paypal/form.html:17
+msgid "Subscribe with PayPal"
+msgstr ""
+
+#: templates/paypal/form.html:20
+msgid "You must be logged in to subscribe."
+msgstr ""
+
+#: templates/paypal/return.html:8
+msgid "Dziękujemy, że jesteś z nami i pomagasz nam rozwijać Wolne Lektury!"
+msgstr ""
+
+#: templates/paypal/return.html:9
+msgid "Pamiętaj, że zawsze możesz się z nami skontaktować:"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Do przeczytania!"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Zespół Wolnych Lektur"
+msgstr ""
diff --git a/src/paypal/locale/fr/LC_MESSAGES/django.mo b/src/paypal/locale/fr/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..c2032a2
Binary files /dev/null and b/src/paypal/locale/fr/LC_MESSAGES/django.mo differ
diff --git a/src/paypal/locale/fr/LC_MESSAGES/django.po b/src/paypal/locale/fr/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..63184dd
--- /dev/null
@@ -0,0 +1,63 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-08-30 15:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: forms.py:10
+msgid "amount in PLN"
+msgstr ""
+
+#: templates/paypal/cancel.html:5
+msgid "Zrezygnowano z płatności :("
+msgstr ""
+
+#: templates/paypal/error.html:1
+msgid "PayPal Error"
+msgstr ""
+
+#: templates/paypal/error.html:5
+msgid "Learn more"
+msgstr ""
+
+#: templates/paypal/form.html:6 templates/paypal/form.html.py:9
+msgid "Wolne Lektury Friend Club"
+msgstr ""
+
+#: templates/paypal/form.html:17
+msgid "Subscribe with PayPal"
+msgstr ""
+
+#: templates/paypal/form.html:20
+msgid "You must be logged in to subscribe."
+msgstr ""
+
+#: templates/paypal/return.html:8
+msgid "Dziękujemy, że jesteś z nami i pomagasz nam rozwijać Wolne Lektury!"
+msgstr ""
+
+#: templates/paypal/return.html:9
+msgid "Pamiętaj, że zawsze możesz się z nami skontaktować:"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Do przeczytania!"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Zespół Wolnych Lektur"
+msgstr ""
diff --git a/src/paypal/locale/it/LC_MESSAGES/django.mo b/src/paypal/locale/it/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..68082f1
Binary files /dev/null and b/src/paypal/locale/it/LC_MESSAGES/django.mo differ
diff --git a/src/paypal/locale/it/LC_MESSAGES/django.po b/src/paypal/locale/it/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..b1136e4
--- /dev/null
@@ -0,0 +1,63 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-08-30 15:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: forms.py:10
+msgid "amount in PLN"
+msgstr ""
+
+#: templates/paypal/cancel.html:5
+msgid "Zrezygnowano z płatności :("
+msgstr ""
+
+#: templates/paypal/error.html:1
+msgid "PayPal Error"
+msgstr ""
+
+#: templates/paypal/error.html:5
+msgid "Learn more"
+msgstr ""
+
+#: templates/paypal/form.html:6 templates/paypal/form.html.py:9
+msgid "Wolne Lektury Friend Club"
+msgstr ""
+
+#: templates/paypal/form.html:17
+msgid "Subscribe with PayPal"
+msgstr ""
+
+#: templates/paypal/form.html:20
+msgid "You must be logged in to subscribe."
+msgstr ""
+
+#: templates/paypal/return.html:8
+msgid "Dziękujemy, że jesteś z nami i pomagasz nam rozwijać Wolne Lektury!"
+msgstr ""
+
+#: templates/paypal/return.html:9
+msgid "Pamiętaj, że zawsze możesz się z nami skontaktować:"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Do przeczytania!"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Zespół Wolnych Lektur"
+msgstr ""
diff --git a/src/paypal/locale/lt/LC_MESSAGES/django.mo b/src/paypal/locale/lt/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..d81c2f9
Binary files /dev/null and b/src/paypal/locale/lt/LC_MESSAGES/django.mo differ
diff --git a/src/paypal/locale/lt/LC_MESSAGES/django.po b/src/paypal/locale/lt/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..b908555
--- /dev/null
@@ -0,0 +1,64 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-08-30 15:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n"
+"%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: forms.py:10
+msgid "amount in PLN"
+msgstr ""
+
+#: templates/paypal/cancel.html:5
+msgid "Zrezygnowano z płatności :("
+msgstr ""
+
+#: templates/paypal/error.html:1
+msgid "PayPal Error"
+msgstr ""
+
+#: templates/paypal/error.html:5
+msgid "Learn more"
+msgstr ""
+
+#: templates/paypal/form.html:6 templates/paypal/form.html.py:9
+msgid "Wolne Lektury Friend Club"
+msgstr ""
+
+#: templates/paypal/form.html:17
+msgid "Subscribe with PayPal"
+msgstr ""
+
+#: templates/paypal/form.html:20
+msgid "You must be logged in to subscribe."
+msgstr ""
+
+#: templates/paypal/return.html:8
+msgid "Dziękujemy, że jesteś z nami i pomagasz nam rozwijać Wolne Lektury!"
+msgstr ""
+
+#: templates/paypal/return.html:9
+msgid "Pamiętaj, że zawsze możesz się z nami skontaktować:"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Do przeczytania!"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Zespół Wolnych Lektur"
+msgstr ""
diff --git a/src/paypal/locale/pl/LC_MESSAGES/django.mo b/src/paypal/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..a32f524
Binary files /dev/null and b/src/paypal/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/src/paypal/locale/pl/LC_MESSAGES/django.po b/src/paypal/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..7bf6a88
--- /dev/null
@@ -0,0 +1,64 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-08-30 15:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+
+#: forms.py:10
+msgid "amount in PLN"
+msgstr "kwota w złotych"
+
+#: templates/paypal/cancel.html:5
+msgid "Zrezygnowano z płatności :("
+msgstr ""
+
+#: templates/paypal/error.html:1
+msgid "PayPal Error"
+msgstr "Błąd PayPala"
+
+#: templates/paypal/error.html:5
+msgid "Learn more"
+msgstr "Dowiedz się więcej"
+
+#: templates/paypal/form.html:6 templates/paypal/form.html.py:9
+msgid "Wolne Lektury Friend Club"
+msgstr "Klub Przyjaciół Wolnych Lektur"
+
+#: templates/paypal/form.html:17
+msgid "Subscribe with PayPal"
+msgstr "Subskrypcja przez PayPal"
+
+#: templates/paypal/form.html:20
+msgid "You must be logged in to subscribe."
+msgstr "Musisz się zalogować, aby ustawić subskrypcję."
+
+#: templates/paypal/return.html:8
+msgid "Dziękujemy, że jesteś z nami i pomagasz nam rozwijać Wolne Lektury!"
+msgstr ""
+
+#: templates/paypal/return.html:9
+msgid "Pamiętaj, że zawsze możesz się z nami skontaktować:"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Do przeczytania!"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Zespół Wolnych Lektur"
+msgstr ""
diff --git a/src/paypal/locale/ru/LC_MESSAGES/django.mo b/src/paypal/locale/ru/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..6937917
Binary files /dev/null and b/src/paypal/locale/ru/LC_MESSAGES/django.mo differ
diff --git a/src/paypal/locale/ru/LC_MESSAGES/django.po b/src/paypal/locale/ru/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..c0d0d14
--- /dev/null
@@ -0,0 +1,64 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-08-30 15:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: forms.py:10
+msgid "amount in PLN"
+msgstr ""
+
+#: templates/paypal/cancel.html:5
+msgid "Zrezygnowano z płatności :("
+msgstr ""
+
+#: templates/paypal/error.html:1
+msgid "PayPal Error"
+msgstr ""
+
+#: templates/paypal/error.html:5
+msgid "Learn more"
+msgstr ""
+
+#: templates/paypal/form.html:6 templates/paypal/form.html.py:9
+msgid "Wolne Lektury Friend Club"
+msgstr ""
+
+#: templates/paypal/form.html:17
+msgid "Subscribe with PayPal"
+msgstr ""
+
+#: templates/paypal/form.html:20
+msgid "You must be logged in to subscribe."
+msgstr ""
+
+#: templates/paypal/return.html:8
+msgid "Dziękujemy, że jesteś z nami i pomagasz nam rozwijać Wolne Lektury!"
+msgstr ""
+
+#: templates/paypal/return.html:9
+msgid "Pamiętaj, że zawsze możesz się z nami skontaktować:"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Do przeczytania!"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Zespół Wolnych Lektur"
+msgstr ""
diff --git a/src/paypal/locale/uk/LC_MESSAGES/django.mo b/src/paypal/locale/uk/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..6937917
Binary files /dev/null and b/src/paypal/locale/uk/LC_MESSAGES/django.mo differ
diff --git a/src/paypal/locale/uk/LC_MESSAGES/django.po b/src/paypal/locale/uk/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..c0d0d14
--- /dev/null
@@ -0,0 +1,64 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-08-30 15:57+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: forms.py:10
+msgid "amount in PLN"
+msgstr ""
+
+#: templates/paypal/cancel.html:5
+msgid "Zrezygnowano z płatności :("
+msgstr ""
+
+#: templates/paypal/error.html:1
+msgid "PayPal Error"
+msgstr ""
+
+#: templates/paypal/error.html:5
+msgid "Learn more"
+msgstr ""
+
+#: templates/paypal/form.html:6 templates/paypal/form.html.py:9
+msgid "Wolne Lektury Friend Club"
+msgstr ""
+
+#: templates/paypal/form.html:17
+msgid "Subscribe with PayPal"
+msgstr ""
+
+#: templates/paypal/form.html:20
+msgid "You must be logged in to subscribe."
+msgstr ""
+
+#: templates/paypal/return.html:8
+msgid "Dziękujemy, że jesteś z nami i pomagasz nam rozwijać Wolne Lektury!"
+msgstr ""
+
+#: templates/paypal/return.html:9
+msgid "Pamiętaj, że zawsze możesz się z nami skontaktować:"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Do przeczytania!"
+msgstr ""
+
+#: templates/paypal/return.html:11
+msgid "Zespół Wolnych Lektur"
+msgstr ""
index 527e804..80aa357 100644 (file)
@@ -18,3 +18,11 @@ class BillingAgreement(models.Model):
     plan = models.ForeignKey(BillingPlan)
     active = models.BooleanField(max_length=32)
     token = models.CharField(max_length=32)
+
+    def get_agreement(self):
+        from .rest import get_agreement
+        return get_agreement(self.agreement_id)
+
+    def check_agreement(self):
+        from .rest import check_agreement
+        return check_agreement(self.agreement_id)
index 1e0811b..3590c6f 100644 (file)
@@ -11,7 +11,7 @@ from django.core.urlresolvers import reverse
 from django.utils import timezone
 from paypalrestsdk import BillingPlan, BillingAgreement, ResourceNotFound
 from django.conf import settings
-from .models import BillingPlan as BillingPlanModel
+from .models import BillingPlan as BillingPlanModel, BillingAgreement as BillingAgreementModel
 
 paypalrestsdk.configure(settings.PAYPAL_CONFIG)
 
@@ -114,5 +114,10 @@ def check_agreement(agreement_id):
         return a.state == 'Active'
 
 
+def user_is_subscribed(user):
+    agreements = BillingAgreementModel.objects.filter(user=user)
+    return any(agreement.check_agreement() for agreement in agreements)
+
+
 def execute_agreement(token):
     return BillingAgreement.execute(token)
index 557284b..6214e90 100644 (file)
@@ -1,3 +1,4 @@
+{% load i18n %}
 <h1>{% trans "PayPal Error" %}: {{ error.message }}</h1>
 {% for detail in error.details %}
   <p>{{ detail.field }}: {{ detail.issue }}</p>
index de8382f..9f4c76b 100644 (file)
@@ -1,18 +1,25 @@
 {% extends "base/base.html" %}
 {% load i18n %}
+{% load chunks %}
+{% load static from staticfiles %}
 
-{% block title %}{% trans "Subscription" %}{% endblock %}
+{% block title %}{% trans "Wolne Lektury Friend Club" %}{% endblock %}
 
 {% block body %}
+  <div class="mobile-margins">
+  <h1>{% trans "Wolne Lektury Friend Club" %}</h1>
+  <p>Poniżej możesz ustawić comiesięczną płatność <strong>(co najmniej 5 zł)</strong>, aby dołączyć do Przyjaciół Wolnych Lektur.</p>
   {# https://www.facebook.com/sharer/sharer.php?u=https%3A//wolnelektury.pl{% url 'paypal_form' %} #}
   {% if user.is_authenticated %}
     <form method="post">
       {% csrf_token %}
-      {{ form.as_p }}
+      {{ form.amount.label_tag }}{{ form.amount }}
       {# paypal submit button #}
-      <button type="submit">{% trans "Subscribe with PayPal" %}</button>
+      <input type="image" src="{% static 'img/btn_subscribe_LG_pl.gif' %}" alt="{% trans "Subscribe with PayPal" %}" style="margin-left: 2em; vertical-align: bottom;">
     </form>
   {% else %}
     {% trans "You must be logged in to subscribe." %}
   {% endif %}
+  {% chunk "klub_info" %}
+  </div>
 {% endblock %}
\ No newline at end of file
index 468870b..38f636d 100644 (file)
@@ -7,8 +7,8 @@ from . import views
 
 urlpatterns = (
     url(r'^form/$', views.paypal_form, name='paypal_form'),
-    url(r'^app-form/$', views.paypal_form, kwargs={'app': True}, name='paypal_api_form'),
+    url(r'^app-form/$', views.paypal_form, kwargs={'app': True}, name='paypal_app_form'),
     url(r'^return/$', views.paypal_return, name='paypal_return'),
-    url(r'^app-return/$', views.paypal_return, kwargs={'app': True}, name='paypal_api_return'),
+    url(r'^app-return/$', views.paypal_return, kwargs={'app': True}, name='paypal_app_return'),
     url(r'^cancel/$', views.paypal_cancel, name='paypal_cancel'),
 )
diff --git a/src/push/__init__.py b/src/push/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/push/admin.py b/src/push/admin.py
new file mode 100644 (file)
index 0000000..339e2e7
--- /dev/null
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django.contrib import admin
+
+from .models import Notification
+
+
+admin.site.register(Notification)
diff --git a/src/push/forms.py b/src/push/forms.py
new file mode 100644 (file)
index 0000000..f3c832b
--- /dev/null
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django import forms
+from django.contrib.sites.models import Site
+
+from push.models import Notification
+from push.utils import send_fcm_push
+
+
+class NotificationForm(forms.ModelForm):
+
+    class Meta:
+        model = Notification
+        exclude = ('timestamp', 'message_id')
+
+    def save(self, commit=True):
+        notification = super(NotificationForm, self).save(commit=commit)
+        wl_base = u'https://' + Site.objects.get_current().domain
+        if notification.image:
+            image_url = wl_base + notification.image.url
+        else:
+            image_url = None
+        notification.message_id = send_fcm_push(notification.title, notification.body, image_url)
+        if commit:
+            notification.save()
diff --git a/src/push/migrations/0001_initial.py b/src/push/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..86a33dc
--- /dev/null
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Notification',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('timestamp', models.DateTimeField(auto_now_add=True)),
+                ('title', models.CharField(max_length=256)),
+                ('body', models.CharField(max_length=2048)),
+                ('image_url', models.URLField()),
+                ('message_id', models.CharField(max_length=2048)),
+            ],
+        ),
+    ]
diff --git a/src/push/migrations/0002_auto_20180830_1627.py b/src/push/migrations/0002_auto_20180830_1627.py
new file mode 100644 (file)
index 0000000..bb22b65
--- /dev/null
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('push', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='notification',
+            options={'ordering': ['-timestamp']},
+        ),
+        migrations.RemoveField(
+            model_name='notification',
+            name='image_url',
+        ),
+        migrations.AddField(
+            model_name='notification',
+            name='image',
+            field=models.FileField(upload_to=b'push/img', verbose_name='image', blank=True),
+        ),
+        migrations.AlterField(
+            model_name='notification',
+            name='body',
+            field=models.CharField(max_length=2048, verbose_name='content'),
+        ),
+        migrations.AlterField(
+            model_name='notification',
+            name='title',
+            field=models.CharField(max_length=256, verbose_name='title'),
+        ),
+    ]
diff --git a/src/push/migrations/0003_auto_20180831_1135.py b/src/push/migrations/0003_auto_20180831_1135.py
new file mode 100644 (file)
index 0000000..40da3fe
--- /dev/null
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('push', '0002_auto_20180830_1627'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='notification',
+            name='image',
+            field=models.ImageField(upload_to=b'push/img', verbose_name='image', blank=True),
+        ),
+    ]
diff --git a/src/push/migrations/__init__.py b/src/push/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/push/models.py b/src/push/models.py
new file mode 100644 (file)
index 0000000..79e5551
--- /dev/null
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+
+class Notification(models.Model):
+    timestamp = models.DateTimeField(auto_now_add=True)
+    title = models.CharField(max_length=256, verbose_name=_(u'title'))
+    body = models.CharField(max_length=2048, verbose_name=_(u'content'))
+    image = models.ImageField(verbose_name=_(u'image'), blank=True, upload_to='push/img')
+    message_id = models.CharField(max_length=2048)
+
+    class Meta:
+        ordering = ['-timestamp']
+
+    def __unicode__(self):
+        return '%s: %s' % (self.timestamp, self.title)
diff --git a/src/push/templates/push/notification_form.html b/src/push/templates/push/notification_form.html
new file mode 100644 (file)
index 0000000..e926c92
--- /dev/null
@@ -0,0 +1,17 @@
+{% extends "base/base.html" %}
+{% load i18n %}
+
+{% block titleextra %}{% trans "Notifications" %}{% endblock %}
+
+
+{% block body %}
+  <h1>Wyślij powiadomienie</h1>
+
+  <form method="post" class="submit-form" action="" enctype="multipart/form-data">
+    {% csrf_token %}
+    <table>
+      {{ form.as_table }}
+      <tr><td></td><td><button type="submit">Wyślij</button></td></tr>
+    </table>
+  </form>
+{% endblock %}
\ No newline at end of file
diff --git a/src/push/templates/push/notification_sent.html b/src/push/templates/push/notification_sent.html
new file mode 100644 (file)
index 0000000..44c29c3
--- /dev/null
@@ -0,0 +1,10 @@
+{% extends "base/base.html" %}
+{% load i18n %}
+
+{% block titleextra %}{% trans "Notifications" %}{% endblock %}
+
+
+{% block body %}
+  <h1>Wysłano powiadomienie</h1>
+  <p>Gratulacje!</p>
+{% endblock %}
\ No newline at end of file
diff --git a/src/push/urls.py b/src/push/urls.py
new file mode 100644 (file)
index 0000000..70a8487
--- /dev/null
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django.conf.urls import patterns, url
+
+from push import views
+
+urlpatterns = (
+    url(r'^wyslij/$', views.notification_form, name='notification_form'),
+    url(r'^wyslane/$', views.notification_sent, name='notification_sent'),
+)
diff --git a/src/push/utils.py b/src/push/utils.py
new file mode 100644 (file)
index 0000000..cd67f9c
--- /dev/null
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+import firebase_admin
+from firebase_admin import credentials, messaging
+from django.conf import settings
+
+cred = credentials.Certificate(settings.FCM_PRIVATE_KEY_PATH)
+firebase_admin.initialize_app(cred)
+
+TOPIC = 'wolnelektury'
+
+
+def send_fcm_push(title, body, image_url=None):
+    # See documentation on defining a message payload.
+    data = {}
+    # data = {
+    #     'title': title,
+    #     'body': body,
+    # }
+    if image_url:
+        data['imageUrl'] = image_url
+    message = messaging.Message(
+        notification=messaging.Notification(
+            title=title,
+            body=body,
+        ),
+        data=data,
+        topic=TOPIC,
+    )
+    message_id = messaging.send(message)
+    return message_id
diff --git a/src/push/views.py b/src/push/views.py
new file mode 100644 (file)
index 0000000..bbcf89d
--- /dev/null
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django.contrib.auth.decorators import permission_required
+from django.core.urlresolvers import reverse
+from django.http.response import HttpResponseRedirect
+from django.shortcuts import render
+
+from push.forms import NotificationForm
+
+
+@permission_required('push.change_notification')
+def notification_form(request):
+    if request.method == 'POST':
+        form = NotificationForm(data=request.POST, files=request.FILES or None)
+        if form.is_valid():
+            form.save()
+            return HttpResponseRedirect(reverse('notification_sent'))
+    else:
+        form = NotificationForm()
+    return render(request, 'push/notification_form.html', {'form': form})
+
+
+def notification_sent(request):
+    return render(request, 'push/notification_sent.html')
\ No newline at end of file
index 8aae74b..5a59422 100644 (file)
@@ -6,6 +6,6 @@ from django.conf.urls import patterns, url
 
 urlpatterns = patterns(
     'search.views',
-    url(r'^$', 'main', name='search'),
+    url(r'^$', 'main', name='wlsearch'),
     url(r'^hint/$', 'hint', name='search_hint'),
 )
index f280dec..25ff0c0 100644 (file)
@@ -70,6 +70,102 @@ class KonkursForm(ContactForm):
               u'rozpowszechnianie ich wizerunków.')
 
 
+class CoJaCzytamForm(ContactForm):
+    form_tag = 'cojaczytam'
+    form_title = u"#cojaczytam?"
+    admin_list = ['opiekun_nazwisko', 'contact', 'nazwa_kampanii']
+    # ends_on = (2018, 11, 16)
+    disabled_template = 'contact/disabled_contact_form.html'
+    submit_label = u'Wyślij'
+
+    opiekun_nazwisko = forms.CharField(label=u'Imię i nazwisko Opiekuna/ki', max_length=128)
+    contact = forms.EmailField(label=u'Adres e-mail Opiekuna/ki', max_length=128)
+    opiekun_tel = forms.CharField(label=u'Numer telefonu Opiekuna/ki', max_length=32)
+    nazwa_dkk = forms.CharField(label=u'Nazwa szkoły/biblioteki publicznej', max_length=128)
+    adres_dkk = forms.CharField(label=u'Adres szkoły/biblioteki publicznej', max_length=128)
+
+    nazwa_kampanii = forms.CharField(label=u'Nazwa kampanii', max_length=255)
+
+    wiek = forms.ChoiceField(label=u'Grupa wiekowa', choices=(
+        ('9-14', u'uczniowie kl. IV-VIII szkół podstawowych w wieku 9-14 lat,'),
+        ('15-19', u'uczniowie gimnazjum oraz wszystkich typów szkół ponadpodstawowych w wieku 15-19.'),
+    ), widget=forms.RadioSelect)
+
+    uczestnik1_header = HeaderField(label=u'Dane\xa0Uczestników (3 do 5)')
+    uczestnik1_imie = forms.CharField(label=u'Imię', max_length=128)
+    uczestnik1_nazwisko = forms.CharField(label=u'Nazwisko', max_length=128)
+    uczestnik1_email = forms.EmailField(label=u'Adres e-mail', max_length=128)
+    uczestnik2_header = HeaderField(label=u'')
+    uczestnik2_imie = forms.CharField(label=u'Imię', max_length=128)
+    uczestnik2_nazwisko = forms.CharField(label=u'Nazwisko', max_length=128)
+    uczestnik2_email = forms.EmailField(label=u'Adres e-mail', max_length=128)
+    uczestnik3_header = HeaderField(label=u'')
+    uczestnik3_imie = forms.CharField(label=u'Imię', max_length=128)
+    uczestnik3_nazwisko = forms.CharField(label=u'Nazwisko', max_length=128)
+    uczestnik3_email = forms.EmailField(label=u'Adres e-mail', max_length=128)
+    uczestnik4_header = HeaderField(label=u'')
+    uczestnik4_imie = forms.CharField(label=u'Imię', max_length=128, required=False)
+    uczestnik4_nazwisko = forms.CharField(label=u'Nazwisko', max_length=128, required=False)
+    uczestnik4_email = forms.EmailField(label=u'Adres e-mail', max_length=128, required=False)
+    uczestnik5_header = HeaderField(label=u'')
+    uczestnik5_imie = forms.CharField(label=u'Imię', max_length=128, required=False)
+    uczestnik5_nazwisko = forms.CharField(label=u'Nazwisko', max_length=128, required=False)
+    uczestnik5_email = forms.EmailField(label=u'Adres e-mail', max_length=128, required=False)
+
+    ankieta_header = HeaderField(label=u'')
+    opis_kampanii = forms.CharField(
+        label=u'Krótki opis realizacji oraz przebiegu kampanii', max_length=255, widget=forms.Textarea)
+    co_sie_udalo = forms.CharField(label=u'Co udało Wam się zrealizować?', max_length=1024, widget=forms.Textarea)
+    co_sie_nie_udalo = forms.CharField(
+        label=u'Czy jest coś, co chcieliście zrealizować, a się nie udało? Jeśli tak, to dlaczego?', max_length=1024,
+        widget=forms.Textarea)
+    wnioski = forms.CharField(
+        label=u'Jakie wnioski na przyszłość wyciągnęliście z tego, czego się nie udało zrealizować?', max_length=1024,
+        widget=forms.Textarea)
+    zasieg = forms.CharField(
+        label=u'Do ilu odbiorców udało Wam się dotrzeć z Waszą kompanią? Podaj liczbę, może być szacunkowa.',
+        max_length=1024, widget=forms.Textarea)
+    grupy_odbiorcow = forms.CharField(
+        label=u'Do jakich grup odbiorców dotarliście (np. uczniowie, nauczyciele, rodzice, seniorzy, inni)?',
+        max_length=1024, widget=forms.Textarea)
+    plik = forms.FileField(
+        label=u'Plik .zip ze stworzonymi materiałami (np. zdjęcia, dokumenty tekstowe)')
+    materialy = forms.CharField(
+        label=u'Adresy stworzonych materiałów online (jeśli dotyczy)', max_length=1024, widget=forms.Textarea,
+        required=False)
+
+    agree_header = HeaderField(label=u'Oświadczenia')
+    agree_terms = forms.BooleanField(
+        label='Regulamin',
+        help_text=mark_safe_lazy(
+            u'Znam i akceptuję <a href="/media/chunks/attachment/Regulamin_konkursu_cojaczytam_edycja_2018.pdf">'
+            u'Regulamin Konkursu</a>.'),
+    )
+    agree_data = forms.BooleanField(
+        label='Przetwarzanie danych osobowych',
+        help_text=u'Oświadczam, że wyrażam zgodę na przetwarzanie danych osobowych zawartych w niniejszym formularzu '
+              u'zgłoszeniowym przez Fundację Nowoczesna Polska (administratora danych) z siedzibą w Warszawie (00-514) '
+              u'przy ul. Marszałkowskiej 84/92 lok. 125 na potrzeby organizacji Konkursu. Jednocześnie oświadczam, '
+              u'że zostałam/em poinformowana/y o tym, że mam prawo wglądu w treść swoich danych i możliwość ich '
+              u'poprawiania oraz że ich podanie jest dobrowolne, ale niezbędne do dokonania zgłoszenia.')
+    agree_license = forms.BooleanField(
+        label='Licencja',
+        help_text=mark_safe_lazy(
+            u'Wyrażam zgodę oraz potwierdzam, że uczestnicy (lub ich przedstawiciele ustawowi – gdy dotyczy) '
+            u'wyrazili zgodę na korzystanie ze stworzonych materiałów zgodnie z postanowieniami '
+            u'<a href="http://freedomdefined.org/Definition/Pl">wolnej licencji</a>, takiej jak '
+            u'<a href="https://creativecommons.org/licenses/by-sa/3.0/pl/">Creative Commons Uznanie autorstwa – '
+            u'Na tych samych warunkach 3.0 PL</a>. Licencja pozwala każdemu na swobodne, nieodpłatne korzystanie '
+            u'z utworu '
+            u'w oryginale oraz w postaci opracowań do wszelkich celów wymagając poszanowania autorstwa i innych praw '
+            u'osobistych oraz tego, aby ewentualne opracowania utworu były także udostępniane na tej samej licencji.'))
+    agree_wizerunek = forms.BooleanField(
+        label='Rozpowszechnianie wizerunku',
+        help_text=u'Wyrażam zgodę oraz potwierdzam, że uczestnicy (lub ich przedstawiciele ustawowi – gdy dotyczy) '
+                  u'wyrazili zgodę na fotografowanie oraz nagrywanie, a następnie rozpowszechnianie ich '
+                  u'wizerunków w celach promocyjnych.')
+
+
 class WorkshopsForm(ContactForm):
     form_tag = 'warsztaty'
     form_title = u"Wolne Lektury Fest"
@@ -99,3 +195,39 @@ class WorkshopsForm(ContactForm):
                   u'24.11.2017 roku i następnie rozpowszechnianie mojego wizerunku w celach promocyjnych.')
     agree_gala = forms.BooleanField(
         label=u'Wezmę udział w uroczystej gali o godz. 19.00.', required=False)
+
+
+class WLFest2018Form(ContactForm):
+    form_tag = 'wlfest2018'
+    form_title = u"Wolne Lektury Fest"
+    nazwisko = forms.CharField(label=u'Imię i nazwisko uczestnika', max_length=128)
+    instytucja = forms.CharField(label=u'Instytucja/organizacja', max_length=128, required=False)
+    contact = forms.EmailField(label=u'Adres e-mail', max_length=128)
+    tel = forms.CharField(label=u'Numer telefonu', max_length=32)
+    warsztaty = forms.MultipleChoiceField(choices=(
+        ('kim-sa-odbiorcy', u'Kim są odbiorcy zdigitalizowanych zasobów kultury w Polsce? (9:30-11:30)'),
+        ('business-model-canvas', u'Business Model Canvas dla kultury (9:30-11:30)'),
+        ('jak-byc-glam', u'Jak być GLAM? Współpraca pomiędzy instytucjami kultury a Wikipedią (12:00-14:00)'),
+        ('wirtualne-muzea', u'Jak twórczo i zgodnie z prawem wykorzystywać zasoby dziedzictwa kulturowego '
+                            u'na przykładzie portalu „Wirtualne Muzea Małopolski” (12:00-14:00)'),
+        ('jak-legalnie-tworzyc', u'Jak legalnie tworzyć i korzystać z cudzej twórczości (15:00-17:00)'),
+        ('aplikacje-w-dzialaniach', u'Aplikacje w działaniach kulturalnych (15:00-17:00)')),
+        widget=forms.CheckboxSelectMultiple,
+    )
+    agree_header = HeaderField(label=mark_safe_lazy(u'<strong>Oświadczenia</strong>'))
+    agree_data = forms.BooleanField(
+        label='Przetwarzanie danych osobowych',
+        help_text=u'Administratorem danych osobowych przetwarzanych w związku z organizacją wydarzenia '
+                  u'„WOLNE LEKTURY FEST” jest Fundacja Nowoczesna Polska '
+                  u'(ul. Marszałkowska 84/92 lok. 125, 00-514 Warszawa). Podanie danych osobowych jest konieczne '
+                  u'do dokonania rejestracji na wydarzenie. Dane są przetwarzane w zakresie niezbędnym '
+                  u'do przeprowadzenia wydarzenia, a także w celach prowadzenia statystyk, '
+                  u'ewaluacji i sprawozdawczości. Osobom, których dane są zbierane, przysługuje prawo dostępu '
+                  u'do treści swoich danych oraz ich poprawiania. Więcej informacji w polityce prywatności '
+                  u'(https://nowoczesnapolska.org.pl/prywatnosc/).')
+    agree_wizerunek = forms.BooleanField(
+        label='Rozpowszechnianie wizerunku',
+        help_text=u'Wyrażam zgodę na fotografowanie i nagrywanie podczas warsztatów „WOLNE LEKTURY FEST” '
+                  u'28.11.2018 roku i następnie rozpowszechnianie mojego wizerunku w celach promocyjnych.')
+    agree_gala = forms.BooleanField(
+        label=u'Wezmę udział w spotkaniu z Julią Fiedorczuk o godz. 17:30.', required=False)
index b57ca55..78b556d 100644 (file)
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 # Django settings for wolnelektury project.
+from .apps import *
 from .basic import *
 from .auth import *
 from .cache import *
@@ -10,106 +11,6 @@ from .locale import *
 from .static import *
 from .paths import *
 
-
-MIDDLEWARE_CLASSES = [
-    'django.middleware.csrf.CsrfViewMiddleware',
-    'ssify.middleware.SsiMiddleware',
-    'django.middleware.cache.UpdateCacheMiddleware',
-    'ssify.middleware.PrepareForCacheMiddleware',
-    'django.middleware.common.CommonMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django.contrib.admindocs.middleware.XViewMiddleware',
-    'fnp_django_pagination.middleware.PaginationMiddleware',
-    'ssify.middleware.LocaleMiddleware',
-    'maintenancemode.middleware.MaintenanceModeMiddleware',
-    'django.middleware.common.CommonMiddleware',
-    'django.contrib.messages.middleware.MessageMiddleware',
-    'fnpdjango.middleware.SetRemoteAddrFromXRealIP',
-    'django.middleware.cache.FetchFromCacheMiddleware',
-]
-
-ROOT_URLCONF = 'wolnelektury.urls'
-
-# These are the ones we should test.
-INSTALLED_APPS_OUR = [
-    'wolnelektury',
-    # our
-    'ajaxable',
-    'api',
-    'catalogue',
-    'chunks',
-    'dictionary',
-    'infopages',
-    'lesmianator',
-    'newtagging',
-    'opds',
-    'pdcounter',
-    'reporting',
-    'sponsors',
-    'stats',
-    'suggest',
-    'picture',
-    'social',
-    'waiter',
-    'search',
-    'oai',
-    'funding',
-    'polls',
-    'libraries',
-    'newsletter',
-    'contact',
-    'isbn',
-    'paypal',
-]
-
-GETPAID_BACKENDS = (
-    'getpaid.backends.payu',
-)
-
-INSTALLED_APPS_CONTRIB = [
-    # Should be before django.contrib.admin
-    'modeltranslation',
-
-    # external
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.sites',
-    'django.contrib.admin',
-    'django.contrib.admindocs',
-    'django.contrib.staticfiles',
-    'fnp_django_pagination',
-    'pipeline',
-    'piston',
-    'piwik',
-    'sorl.thumbnail',
-    'kombu.transport.django',
-    'honeypot',
-    'fnpdjango',
-    'getpaid',
-    'getpaid.backends.payu',
-    'ssify',
-    'django_extensions',
-    'raven.contrib.django.raven_compat',
-
-    'migdal',
-    'django_comments',
-    'django_comments_xtd',
-
-    # allauth stuff
-    'uni_form',
-    'allauth',
-    'allauth.account',
-    'allauth.socialaccount',
-    'allauth.socialaccount.providers.openid',
-    'allauth.socialaccount.providers.facebook',
-    'allauth.socialaccount.providers.google',
-    # 'allauth.socialaccount.providers.twitter',
-    ]
-
-INSTALLED_APPS = INSTALLED_APPS_OUR + INSTALLED_APPS_CONTRIB
-
 # Load localsettings, if they exist
 try:
     from wolnelektury.localsettings import *
diff --git a/src/wolnelektury/settings/apps.py b/src/wolnelektury/settings/apps.py
new file mode 100644 (file)
index 0000000..87286d5
--- /dev/null
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+# These are the ones we should test.
+INSTALLED_APPS_OUR = [
+    'wolnelektury',
+    # our
+    'ajaxable',
+    'api',
+    'catalogue',
+    'chunks',
+    'dictionary',
+    'infopages',
+    'lesmianator',
+    'newtagging',
+    'opds',
+    'pdcounter',
+    'reporting',
+    'sponsors',
+    'stats',
+    'suggest',
+    'picture',
+    'social',
+    'waiter',
+    'search',
+    'oai',
+    'funding',
+    'polls',
+    'libraries',
+    'newsletter',
+    'contact',
+    'isbn',
+    'paypal',
+    'push',
+]
+
+INSTALLED_APPS_CONTRIB = [
+    # Should be before django.contrib.admin
+    'modeltranslation',
+
+    # external
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.admin',
+    'django.contrib.admindocs',
+    'django.contrib.staticfiles',
+    'fnp_django_pagination',
+    'pipeline',
+    'piston',
+    'piwik',
+    'sorl.thumbnail',
+    'kombu.transport.django',
+    'honeypot',
+    'fnpdjango',
+    'getpaid',
+    'getpaid.backends.payu',
+    'ssify',
+    'django_extensions',
+    'raven.contrib.django.raven_compat',
+
+    'migdal',
+    'django_comments',
+    'django_comments_xtd',
+    'django_gravatar',
+
+    # allauth stuff
+    'uni_form',
+    'allauth',
+    'allauth.account',
+    'allauth.socialaccount',
+    'allauth.socialaccount.providers.openid',
+    'allauth.socialaccount.providers.facebook',
+    'allauth.socialaccount.providers.google',
+    # 'allauth.socialaccount.providers.twitter',
+]
+
+INSTALLED_APPS = INSTALLED_APPS_OUR + INSTALLED_APPS_CONTRIB
index 32b48f2..7990f25 100644 (file)
@@ -58,3 +58,23 @@ TEMPLATES = [{
         ),
     },
 }]
+
+MIDDLEWARE_CLASSES = [
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'ssify.middleware.SsiMiddleware',
+    'django.middleware.cache.UpdateCacheMiddleware',
+    'ssify.middleware.PrepareForCacheMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.admindocs.middleware.XViewMiddleware',
+    'fnp_django_pagination.middleware.PaginationMiddleware',
+    'ssify.middleware.LocaleMiddleware',
+    'maintenancemode.middleware.MaintenanceModeMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'fnpdjango.middleware.SetRemoteAddrFromXRealIP',
+    'django.middleware.cache.FetchFromCacheMiddleware',
+]
+
+ROOT_URLCONF = 'wolnelektury.urls'
index 70eca9e..de64990 100644 (file)
@@ -3,6 +3,8 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from fnpdjango.utils.text.textilepl import textile_pl
+from migdal import EntryType
+from django.utils.translation import ugettext_lazy as _
 
 HONEYPOT_FIELD_NAME = 'miut'
 PAGINATION_INVALID_PAGE_RAISES_404 = True
@@ -18,6 +20,10 @@ MIGRATION_MODULES = {
 
 GETPAID_ORDER_DESCRIPTION = "{% load funding_tags %}{{ order|sanitize_payment_title }}"
 
+GETPAID_BACKENDS = (
+    'getpaid.backends.payu',
+)
+
 PIWIK_URL = ''
 PIWIK_SITE_ID = 0
 PIWIK_TOKEN = ''
@@ -31,3 +37,10 @@ PAYPAL_CONFIG = {
 MARKUP_FIELD_TYPES = (
     ('textile_pl', textile_pl),
 )
+
+MIGDAL_TYPES = (
+    EntryType('news', _('news'), commentable=False, on_main=True, promotable=True),
+    EntryType('publications', _('publications'), commentable=False),
+    EntryType('info', _('info'), commentable=False),
+    EntryType('event', _('events'), commentable=False),
+)
diff --git a/src/wolnelektury/static/img/btn_subscribe_LG_pl.gif b/src/wolnelektury/static/img/btn_subscribe_LG_pl.gif
new file mode 100644 (file)
index 0000000..c628e17
Binary files /dev/null and b/src/wolnelektury/static/img/btn_subscribe_LG_pl.gif differ
index 270d227..45e383c 100755 (executable)
@@ -18,7 +18,7 @@ body {
   background: #f7f7f7;
   color: black;
 
-  @include size(font-size, 13px);
+  @include size(font-size, 15px);
 }
 
 a {
@@ -41,12 +41,12 @@ h1 {
 }
 
 h2 {
-  @include size(font-size, 20px);
+  @include size(font-size, 23px);
   font-weight: normal;
 }
 
 h3 {
-  @include size(font-size, 15px);
+  @include size(font-size, 17px);
   font-weight: normal;
 }
 
@@ -83,15 +83,21 @@ ul.plain {
 
 .left-column, .right-column {
   @include size(max-width, 600px);
+  @include size(padding-left, 1em);
+  @include size(padding-right, 1em);
 }
 
 @media screen and (min-width: 62.5em) {
-  .left-column {
+  .left-column, .right-column {
     @include size(width, 470px);
+    padding-left: 0;
+    padding-right: 0;
+  }
+
+  .left-column {
     float: left;
   }
   .right-column {
-    @include size(width, 470px);
     float: right;
   }
 }
@@ -214,3 +220,7 @@ ul.plain {
     @include size(margin-bottom, 10px);
   }
 }
+
+@media screen and (max-width: 62.5em) {
+  .mobile-margins {margin-left: 1em; margin-right: 1em;}
+}
index 749b6b5..3ff768f 100644 (file)
                   </li>
                 </ul>
               </nav>
-              <form id="search-area" action="{% url 'search' %}">
+              <form id="search-area" action="{% url 'wlsearch' %}">
                 <div id="search-field">
                   <label for="search">{{ search_form.q.label }}</label>
                   {{ search_form.q }}
diff --git a/src/wolnelektury/templates/contact/cojaczytam/form.html b/src/wolnelektury/templates/contact/cojaczytam/form.html
new file mode 100644 (file)
index 0000000..bc00e0c
--- /dev/null
@@ -0,0 +1,6 @@
+{% extends "contact/form.html" %}
+
+{% block body %}
+  {{ block.super }}
+  <p><a href="{{ MEDIA_URL }}chunks/attachment/Zgoda_dla_rodzic%C3%B3w.doc">Formularz zgody dla rodziców i opiekunów</a></p>
+{% endblock %}
\ No newline at end of file
index 0cbaced..403cc1f 100644 (file)
@@ -7,6 +7,7 @@ from django.conf import settings
 from django.contrib import admin
 from django.views.generic import RedirectView
 import django.views.static
+from migdal.urls import urlpatterns as migdal_urlpatterns
 import catalogue.views
 import picture.views
 from . import views
@@ -51,6 +52,7 @@ urlpatterns += [
     url(r'^formularz/', include('contact.urls')),
     url(r'^isbn/', include('isbn.urls')),
     url(r'^paypal/', include('paypal.urls')),
+    url(r'^powiadomienie/', include('push.urls')),
 
     # Admin panel
     url(r'^admin/catalogue/book/import$', catalogue.views.import_book, name='import_book'),
@@ -93,3 +95,5 @@ urlpatterns += [
     url(r'^error-test/$', views.exception_test),
     # url(r'^post-test/$', views.post_test),
 ]
+
+urlpatterns += migdal_urlpatterns