Annoy!
authorRadek Czajka <rczajka@rczajka.pl>
Fri, 13 Dec 2019 12:31:28 +0000 (13:31 +0100)
committerRadek Czajka <rczajka@rczajka.pl>
Fri, 13 Dec 2019 12:31:28 +0000 (13:31 +0100)
37 files changed:
src/annoy/__init__.py [new file with mode: 0644]
src/annoy/admin.py [new file with mode: 0644]
src/annoy/apps.py [new file with mode: 0644]
src/annoy/migrations/0001_initial.py [new file with mode: 0644]
src/annoy/migrations/0002_auto_20191211_1320.py [new file with mode: 0644]
src/annoy/migrations/0003_auto_20191211_1511.py [new file with mode: 0644]
src/annoy/migrations/0004_auto_20191211_1512.py [new file with mode: 0644]
src/annoy/migrations/0005_auto_20191211_1617.py [new file with mode: 0644]
src/annoy/migrations/0006_auto_20191213_1328.py [new file with mode: 0644]
src/annoy/migrations/__init__.py [new file with mode: 0644]
src/annoy/models.py [new file with mode: 0644]
src/annoy/places.py [new file with mode: 0644]
src/annoy/static/annoy/banner.js [new file with mode: 0644]
src/annoy/static/annoy/banner.scss [new file with mode: 0644]
src/annoy/static/annoy/book_text.js [new file with mode: 0644]
src/annoy/static/annoy/book_text.scss [new file with mode: 0644]
src/annoy/templates/annoy/banner.html [new file with mode: 0644]
src/annoy/templates/annoy/banners.html [new file with mode: 0644]
src/annoy/templatetags/annoy.py [new file with mode: 0644]
src/annoy/tests.py [new file with mode: 0644]
src/annoy/translation.py [new file with mode: 0644]
src/annoy/utils.py [new file with mode: 0644]
src/annoy/views.py [new file with mode: 0644]
src/catalogue/templates/catalogue/book_detail.html
src/catalogue/templates/catalogue/book_fragments.html
src/catalogue/templates/catalogue/book_text.html
src/catalogue/templates/catalogue/viewer_base.html
src/catalogue/views.py
src/club/urls.py
src/wolnelektury/settings/apps.py
src/wolnelektury/settings/static.py
src/wolnelektury/static/css/annoy.css [deleted file]
src/wolnelektury/static/js/annoy.js [deleted file]
src/wolnelektury/static/scss/main/cite.scss
src/wolnelektury/static/scss/main/fragment.scss
src/wolnelektury/templates/annoy.html [deleted file]
src/wolnelektury/templates/base/superbase.html

diff --git a/src/annoy/__init__.py b/src/annoy/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/annoy/admin.py b/src/annoy/admin.py
new file mode 100644 (file)
index 0000000..7a82d68
--- /dev/null
@@ -0,0 +1,24 @@
+from django.contrib import admin
+from django import forms
+from modeltranslation.admin import TranslationAdmin
+from . import models
+
+
+class BannerAdmin(TranslationAdmin):
+    list_display = ['place', 'text', 'priority', 'since', 'until', 'show_members', 'staff_preview']
+
+    
+admin.site.register(models.Banner, BannerAdmin)
+
+
+class DynamicTextInsertTextInline(admin.TabularInline):
+    model = models.DynamicTextInsertText
+    fields = ['text', 'image', 'background_color', 'text_color']
+
+
+class DynamicTextInsertAdmin(admin.ModelAdmin):
+    list_display = ['paragraphs']
+    inlines = [DynamicTextInsertTextInline]
+
+
+admin.site.register(models.DynamicTextInsert, DynamicTextInsertAdmin)
diff --git a/src/annoy/apps.py b/src/annoy/apps.py
new file mode 100644 (file)
index 0000000..213a4e9
--- /dev/null
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class AnnoyConfig(AppConfig):
+    name = 'annoy'
diff --git a/src/annoy/migrations/0001_initial.py b/src/annoy/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..d5b091e
--- /dev/null
@@ -0,0 +1,64 @@
+# Generated by Django 2.2.6 on 2019-12-11 11:44
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Banner',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('place', models.SlugField(choices=[('top', 'Top of all pages.'), ('book-page', 'Book page'), ('book-text-intermission', 'Book text intermission'), ('fragment-list', 'Next to list of book fragments.')])),
+                ('text', models.TextField()),
+                ('text_de', models.TextField(null=True)),
+                ('text_en', models.TextField(null=True)),
+                ('text_es', models.TextField(null=True)),
+                ('text_fr', models.TextField(null=True)),
+                ('text_it', models.TextField(null=True)),
+                ('text_lt', models.TextField(null=True)),
+                ('text_pl', models.TextField(null=True)),
+                ('text_ru', models.TextField(null=True)),
+                ('text_uk', models.TextField(null=True)),
+                ('url', models.CharField(max_length=1024)),
+                ('priority', models.PositiveSmallIntegerField(default=0)),
+                ('since', models.DateTimeField(blank=True, null=True)),
+                ('until', models.DateTimeField(blank=True, null=True)),
+                ('show_members', models.BooleanField(default=False)),
+            ],
+            options={
+                'verbose_name': 'banner',
+                'verbose_name_plural': 'banners',
+                'ordering': ('place', '-priority'),
+            },
+        ),
+        migrations.CreateModel(
+            name='DynamicTextInsert',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('paragraphs', models.IntegerField(verbose_name='pararaphs')),
+                ('text', models.TextField(verbose_name='text')),
+                ('text_de', models.TextField(null=True, verbose_name='text')),
+                ('text_en', models.TextField(null=True, verbose_name='text')),
+                ('text_es', models.TextField(null=True, verbose_name='text')),
+                ('text_fr', models.TextField(null=True, verbose_name='text')),
+                ('text_it', models.TextField(null=True, verbose_name='text')),
+                ('text_lt', models.TextField(null=True, verbose_name='text')),
+                ('text_pl', models.TextField(null=True, verbose_name='text')),
+                ('text_ru', models.TextField(null=True, verbose_name='text')),
+                ('text_uk', models.TextField(null=True, verbose_name='text')),
+                ('url', models.CharField(max_length=1024)),
+            ],
+            options={
+                'verbose_name': 'dynamic insert',
+                'verbose_name_plural': 'dynamic inserts',
+                'ordering': ('paragraphs',),
+            },
+        ),
+    ]
diff --git a/src/annoy/migrations/0002_auto_20191211_1320.py b/src/annoy/migrations/0002_auto_20191211_1320.py
new file mode 100644 (file)
index 0000000..8d23a1f
--- /dev/null
@@ -0,0 +1,23 @@
+# Generated by Django 2.2.6 on 2019-12-11 12:20
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('annoy', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='banner',
+            name='open_label',
+            field=models.CharField(blank=True, max_length=255),
+        ),
+        migrations.AlterField(
+            model_name='banner',
+            name='place',
+            field=models.SlugField(choices=[('top', 'Top of all pages.'), ('book-page', 'Book page'), ('book-text-intermission', 'Book text intermission'), ('book-fragment-list', 'Next to list of book fragments.')]),
+        ),
+    ]
diff --git a/src/annoy/migrations/0003_auto_20191211_1511.py b/src/annoy/migrations/0003_auto_20191211_1511.py
new file mode 100644 (file)
index 0000000..daa3f97
--- /dev/null
@@ -0,0 +1,18 @@
+# Generated by Django 2.2.6 on 2019-12-11 14:11
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('annoy', '0002_auto_20191211_1320'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='banner',
+            name='place',
+            field=models.SlugField(choices=[('top', 'Top of all pages.'), ('book-page', 'Book page'), ('book-text-intermission', 'Book text intermission'), ('book-fragment-list', 'Next to list of book fragments.'), ('blackout', 'Blackout')]),
+        ),
+    ]
diff --git a/src/annoy/migrations/0004_auto_20191211_1512.py b/src/annoy/migrations/0004_auto_20191211_1512.py
new file mode 100644 (file)
index 0000000..0e57edf
--- /dev/null
@@ -0,0 +1,23 @@
+# Generated by Django 2.2.6 on 2019-12-11 14:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('annoy', '0003_auto_20191211_1511'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='banner',
+            name='action_label',
+            field=models.CharField(blank=True, max_length=255),
+        ),
+        migrations.AddField(
+            model_name='banner',
+            name='close_label',
+            field=models.CharField(blank=True, max_length=255),
+        ),
+    ]
diff --git a/src/annoy/migrations/0005_auto_20191211_1617.py b/src/annoy/migrations/0005_auto_20191211_1617.py
new file mode 100644 (file)
index 0000000..5fa9b95
--- /dev/null
@@ -0,0 +1,70 @@
+# Generated by Django 2.2.6 on 2019-12-11 15:17
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('annoy', '0004_auto_20191211_1512'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='dynamictextinsert',
+            name='text',
+        ),
+        migrations.RemoveField(
+            model_name='dynamictextinsert',
+            name='text_de',
+        ),
+        migrations.RemoveField(
+            model_name='dynamictextinsert',
+            name='text_en',
+        ),
+        migrations.RemoveField(
+            model_name='dynamictextinsert',
+            name='text_es',
+        ),
+        migrations.RemoveField(
+            model_name='dynamictextinsert',
+            name='text_fr',
+        ),
+        migrations.RemoveField(
+            model_name='dynamictextinsert',
+            name='text_it',
+        ),
+        migrations.RemoveField(
+            model_name='dynamictextinsert',
+            name='text_lt',
+        ),
+        migrations.RemoveField(
+            model_name='dynamictextinsert',
+            name='text_pl',
+        ),
+        migrations.RemoveField(
+            model_name='dynamictextinsert',
+            name='text_ru',
+        ),
+        migrations.RemoveField(
+            model_name='dynamictextinsert',
+            name='text_uk',
+        ),
+        migrations.AlterField(
+            model_name='banner',
+            name='action_label',
+            field=models.CharField(blank=True, help_text='', max_length=255),
+        ),
+        migrations.CreateModel(
+            name='DynamicTextInsertText',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('background_color', models.CharField(blank=True, max_length=10)),
+                ('text_color', models.CharField(blank=True, max_length=10)),
+                ('text', models.TextField(verbose_name='text')),
+                ('image', models.FileField(blank=True, upload_to='')),
+                ('insert', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='annoy.DynamicTextInsert')),
+            ],
+        ),
+    ]
diff --git a/src/annoy/migrations/0006_auto_20191213_1328.py b/src/annoy/migrations/0006_auto_20191213_1328.py
new file mode 100644 (file)
index 0000000..acafafe
--- /dev/null
@@ -0,0 +1,23 @@
+# Generated by Django 2.2.6 on 2019-12-13 12:28
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('annoy', '0005_auto_20191211_1617'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='banner',
+            name='staff_preview',
+            field=models.BooleanField(default=False),
+        ),
+        migrations.AlterField(
+            model_name='banner',
+            name='action_label',
+            field=models.CharField(blank=True, help_text='', max_length=255),
+        ),
+    ]
diff --git a/src/annoy/migrations/__init__.py b/src/annoy/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/annoy/models.py b/src/annoy/models.py
new file mode 100644 (file)
index 0000000..26b0136
--- /dev/null
@@ -0,0 +1,82 @@
+from django.apps import apps
+from django.conf import settings
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+from django.utils.timezone import now
+from .places import PLACES, PLACE_CHOICES
+
+
+class Banner(models.Model):
+    place = models.SlugField(choices=PLACE_CHOICES)
+    action_label = models.CharField(
+        max_length=255, blank=True,
+        help_text=_('')
+    )
+    open_label = models.CharField(max_length=255, blank=True)
+    close_label = models.CharField(max_length=255, blank=True)
+    text = models.TextField()
+    url = models.CharField(max_length=1024)
+    priority = models.PositiveSmallIntegerField(default=0)
+    since = models.DateTimeField(null=True, blank=True)
+    until = models.DateTimeField(null=True, blank=True)
+    show_members = models.BooleanField(default=False)
+    staff_preview = models.BooleanField(default=False)
+
+    class Meta:
+        verbose_name = _('banner')
+        verbose_name_plural = _('banners')
+        ordering = ('place', '-priority',)
+
+    def __str__(self):
+        return self.text
+
+    @classmethod
+    def choice(cls, place, request):
+        Membership = apps.get_model('club', 'Membership')
+
+        if hasattr(request, 'annoy_banner_exempt'):
+            return cls.objects.none()
+        
+        if settings.DEBUG:
+            assert place in PLACES, "Banner place `{}` must be defined in annoy.places.".format(place)
+
+        n = now()
+        banners = cls.objects.filter(
+            place=place
+        ).exclude(
+            since__gt=n
+        ).exclude(
+            until__lt=n
+        ).order_by('-priority', '?')
+
+        if not request.user.is_staff:
+            banners = banners.filter(staff_preview=False)
+
+        if request:
+            if Membership.is_active_for(request.user):
+                banners = banners.filter(show_members=True)
+        return banners
+        
+        
+class DynamicTextInsert(models.Model):
+    paragraphs = models.IntegerField(_('pararaphs'))
+    url = models.CharField(max_length=1024)
+
+    class Meta:
+        verbose_name = _('dynamic insert')
+        verbose_name_plural = _('dynamic inserts')
+        ordering = ('paragraphs', )
+
+    def __str__(self):
+        return str(self.paragraphs)
+
+    def choose(self):
+        return self.dynamictextinserttext_set.order_by('?').first()
+
+
+class DynamicTextInsertText(models.Model):
+    insert = models.ForeignKey(DynamicTextInsert, models.CASCADE)
+    background_color = models.CharField(max_length=10, blank=True)
+    text_color = models.CharField(max_length=10, blank=True)
+    text = models.TextField(_('text'))
+    image = models.FileField(blank=True)
diff --git a/src/annoy/places.py b/src/annoy/places.py
new file mode 100644 (file)
index 0000000..93b22e4
--- /dev/null
@@ -0,0 +1,16 @@
+from django.utils.translation import ugettext_lazy as _
+
+PLACE_DEFINITIONS = [
+    ('top', _('Top of all pages.'), True),
+    ('book-page', _('Book page'), False),
+    ('book-text-intermission', _('Book text intermission'), False),
+    ('book-fragment-list', _('Next to list of book fragments.'), False),
+    ('blackout', _('Blackout'), True),
+]
+
+PLACE_CHOICES = [p[:2] for p in PLACE_DEFINITIONS]
+
+PLACES = {
+    p[0]: p[2]
+    for p in PLACE_DEFINITIONS
+}
diff --git a/src/annoy/static/annoy/banner.js b/src/annoy/static/annoy/banner.js
new file mode 100644 (file)
index 0000000..ee45613
--- /dev/null
@@ -0,0 +1,32 @@
+(function($) {
+    $(function() {
+
+        $(".annoy-banner-on").each(function() {
+            var $on = $(this);
+            var tag = 'annoyed' + $on.attr('data-target');
+            var $target = $($on.attr('data-target'));
+            var $off = $('.annoy-banner-off', $target);
+
+            $on.click(function(e) {
+                e.preventDefault();
+                $target.slideDown('fast');
+                $on.hide();
+                if (Modernizr.localstorage) localStorage.removeItem(tag);
+            });
+
+            $off.click(function() {
+                $target.slideUp('fast');
+                $on.show();
+                if (Modernizr.localstorage) localStorage[tag] = true;
+            });
+
+            if (Modernizr.localstorage) {
+                if (!localStorage[tag]) {
+                    $on.hide();
+                    $target.show();
+                }
+            }
+        });
+
+    });
+})(jQuery);
diff --git a/src/annoy/static/annoy/banner.scss b/src/annoy/static/annoy/banner.scss
new file mode 100644 (file)
index 0000000..33fd481
--- /dev/null
@@ -0,0 +1,213 @@
+.annoy-banner {
+    background: orange;
+
+    p {
+        text-align: center;
+    }
+
+    a {
+        color: black;
+        display: block;
+        padding: 1em 2em;
+    }
+}
+
+
+.annoy-banner-off {
+    cursor: pointer;
+}
+
+.annoy-banner_top {
+    display: none;
+
+    p {
+        margin: 0;
+        font-size: 2em;
+    }
+    
+    .annoy-banner-off {
+        padding: .5em 1em .5em;
+        width: 1em;
+        text-align:center;
+        font-family: Arial, sans-serif;
+        display: block;
+        
+        border-radius: 0 0 0 1em;
+        position: absolute;
+        top: 0;
+        right: 0;
+        color: black;
+        font-size: 13px;
+    }
+}
+
+.annoy-banner-on_top {
+    font-size: 13px;
+    line-height: 1.15em;
+
+    background: orange;
+    z-index: 99;
+    font-family: Arial, sans-serif;
+    display: block;
+    padding: .1em 1em;
+    text-align:center;
+    border-radius: 0 0 0 1em;
+    position: absolute;
+    top: 0;
+    right: 0;
+    color: black;
+}
+
+
+.annoy-banner_book-page {
+    margin-top: 30px;
+    font-size: 2em;
+
+    p {
+        margin: 0;
+    }
+}
+
+
+.annoy-banner_book-fragment-list {
+    margin-right: 2em;
+}
+
+
+
+.annoy-banner_blackout {
+    display: none;
+    
+    position: fixed;
+    z-index: 100000;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    background: black;
+    color: white;
+    font-size: 16pt;
+    line-height: 26pt;
+    padding: 0;
+
+    .annoy-banner-inner {
+        padding: 0 40% 0 15%;
+        height: 100%;
+        overflow-y: scroll;
+        padding-top: 20vh;
+        padding-bottom: 2em;
+        width: 101vw;
+        box-sizing: border-box;
+    }
+
+    p {
+        text-align: left;
+    }
+    
+    a {
+        color: white;
+        position: absolute;
+        right: 10%;
+        width: 25%;
+        border-radius: 24px;
+        text-align: center;
+        box-sizing: border-box;
+        padding: 1em;
+
+        &.annoy-banner-off {
+            bottom: 20vh;
+            background: #666;
+            border-radius: auto 100%;
+            font-size: .75em;
+            padding: .5em;
+            width: 20%;
+            margin-right: 2.5%;
+        }
+
+        &.action {
+            bottom: 33vh;
+            background: #018189;
+            color: white;
+            border-radius: auto 100%;
+            padding: 1.5em 1em;
+        }
+    }    
+
+    @media screen and (max-height: 1000px) {
+        .annoy-banner-inner {
+            padding-top: 5vh;
+        }
+    }
+    
+    @media screen and (max-width: 1280px) {
+        .annoy-banner-inner {
+            padding-left: 5%;
+            padding-right: 50%;
+            padding-left: 5%;
+            padding-right: 50%;
+        }
+
+        a {
+            right: 5%;
+            width: 40%;
+
+            &.annoy-banner-off {
+                width: 40%;
+                margin-right: 0%;
+            }
+        }
+        
+    }
+    @media screen and (max-width: 1024px) {
+        font-size: 14pt;
+        line-height: 21pt;        
+    }
+    @media screen and (max-height: 820px) {
+        .annoy-banner-inner {
+            padding-top: 5vh;
+        }
+    }
+    @media screen and (max-width: 820px) and (max-height: 820px) {
+        a.action {
+            bottom: 20vh;
+        }
+        a.annoy-banner-off {
+            bottom: 5vh;
+        }
+    }
+    @media screen and (max-height: 400px) {
+        a.action {
+            bottom: 30vh;
+        }
+    }
+
+    
+}
+.annoy-banner-on_blackout {
+    position: fixed;
+    z-index: 9999;
+    bottom: 0;
+    right: 0;
+    background: black;
+    color: white;
+    border-radius: 1em 0 0 0;
+    padding: .1em 1em;
+}
+
+
+
+.dynamic-insert.with-image {
+    min-height: 120px;
+    padding: 0;
+
+    a {
+        position: relative;
+        .text {
+        }
+        img {
+            position: absolute;
+            top: 10px;
+            right: 10px;
+        }
+    }
+}
diff --git a/src/annoy/static/annoy/book_text.js b/src/annoy/static/annoy/book_text.js
new file mode 100644 (file)
index 0000000..267d6e1
--- /dev/null
@@ -0,0 +1,51 @@
+(function($) {
+    $(function() {
+
+
+        var fold = $(window).scrollTop() + $(window).height();
+
+        var inserts = [];
+        $("#annoy-stubs .dynamic-insert").each(function() {inserts.push($(this));});
+
+        var $intermissions = $("#annoy-stubs .annoy-banner_book-text-intermission");
+        if ($intermissions.length) {
+            var which = 0;
+            $("#book-text a + h2").each(function(i, e) {
+                console.log(i);
+                if (i) {
+                    $($intermissions[which]).clone().insertBefore($(this)).show();
+                    which = (which + 1) % $intermissions.length;
+                }
+            });
+
+            if ($("#footnotes").length) {
+                $($intermissions[which]).clone().insertBefore($("#footnotes")).show();
+            } else {
+                $($intermissions[which]).clone().appendTo($("#book-text")).show();
+            }
+        };
+
+        if (inserts) {
+            var underFold = false;
+            var counter = 0;
+            $(".paragraph, .stanza").each(function() {
+                var p = $(this);
+                if (!underFold) {
+                    if (p.offset().top > fold) {
+                        underFold = true;
+                    }
+                }
+                if (underFold) {
+                    if (inserts[0].attr('data-paragraphs') == counter) {
+                        insert = inserts.shift();
+                        insert.insertBefore(p);
+                    }
+                    counter += 1;
+                }
+                return inserts.length > 0;
+            });
+        };
+
+        
+    });
+})(jQuery);
diff --git a/src/annoy/static/annoy/book_text.scss b/src/annoy/static/annoy/book_text.scss
new file mode 100644 (file)
index 0000000..37c83ef
--- /dev/null
@@ -0,0 +1,15 @@
+.dynamic-insert, .intermission {
+    margin: 2em 0;
+    padding: 2em;
+    background: orange;
+
+    a {
+        display: block;
+        color: black;
+    }
+}
+
+
+#annoy-stubs {
+    display: none;
+}
diff --git a/src/annoy/templates/annoy/banner.html b/src/annoy/templates/annoy/banner.html
new file mode 100644 (file)
index 0000000..3f54eb2
--- /dev/null
@@ -0,0 +1,28 @@
+{% if banner %}
+  {% if closable %}
+    <a class='annoy-banner-on annoy-banner-on_{{ banner.place }}'
+       href="{{ banner.url }}"
+       data-target="#annoy-banner-{{ banner.id }}"
+    >
+      {{ banner.open_label }}
+    </a>
+  {% endif %}
+  <div class="annoy-banner annoy-banner_{{ banner.place }}" id="annoy-banner-{{ banner.id }}">
+    <div class="annoy-banner-inner">
+      {% if banner.action_label %}
+        {{ banner.text|safe|linebreaks }}
+        <a class="action" href="{{ banner.url }}">
+          {{ banner.action_label }}
+        </a>
+      {% else %}
+        <a href="{{ banner.url }}">
+          {{ banner.text|safe|linebreaks }}
+        </a>
+      {% endif %}
+      {% if closable %}
+        <a class='annoy-banner-off annoy-banner-off_{{ banner.place }}'>{{ banner.close_label|default:"x" }}</a>
+      {% endif %}
+    </div>
+  </div>
+
+{% endif %}
diff --git a/src/annoy/templates/annoy/banners.html b/src/annoy/templates/annoy/banners.html
new file mode 100644 (file)
index 0000000..7de539e
--- /dev/null
@@ -0,0 +1,3 @@
+{% for banner in banners %}
+  {% include "annoy/banner.html" %}
+{% endfor %}
diff --git a/src/annoy/templatetags/annoy.py b/src/annoy/templatetags/annoy.py
new file mode 100644 (file)
index 0000000..21f1bb8
--- /dev/null
@@ -0,0 +1,23 @@
+from django import template
+from ..models import Banner
+from ..places import PLACES
+
+
+register = template.Library()
+
+
+@register.inclusion_tag('annoy/banner.html', takes_context=True)
+def annoy_banner(context, place):
+    banners = Banner.choice(place, request=context['request'])
+    return {
+        'banner': banners.first(),
+        'closable': PLACES.get(place, False),
+    }
+
+
+@register.inclusion_tag('annoy/banners.html', takes_context=True)
+def annoy_banners(context, place):
+    return {
+        'banners': Banner.choice(place, request=context['request']),
+        'closable': PLACES.get(place, False),
+    }
diff --git a/src/annoy/tests.py b/src/annoy/tests.py
new file mode 100644 (file)
index 0000000..7ce503c
--- /dev/null
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/src/annoy/translation.py b/src/annoy/translation.py
new file mode 100644 (file)
index 0000000..eb17ada
--- /dev/null
@@ -0,0 +1,15 @@
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from modeltranslation.translator import translator, TranslationOptions
+from . import models
+
+
+class BannerTranslationOptions(TranslationOptions):
+    fields = ('text',)
+
+
+
+translator.register(models.Banner, BannerTranslationOptions)
+
+
diff --git a/src/annoy/utils.py b/src/annoy/utils.py
new file mode 100644 (file)
index 0000000..07a1a63
--- /dev/null
@@ -0,0 +1,9 @@
+from functools import wraps
+
+
+def banner_exempt(view):
+    @wraps(view)
+    def wrapped(request, *args, **kwargs):
+        request.annoy_banner_exempt = True
+        return view(request, *args, **kwargs)
+    return wrapped
diff --git a/src/annoy/views.py b/src/annoy/views.py
new file mode 100644 (file)
index 0000000..91ea44a
--- /dev/null
@@ -0,0 +1,3 @@
+from django.shortcuts import render
+
+# Create your views here.
index de1b45a..6e875bd 100644 (file)
@@ -2,6 +2,7 @@
 {% load i18n %}
 {% load catalogue_tags %}
 {% load build_absolute_uri from fnp_common %}
+{% load annoy_banner from annoy %}
 {% load cache %}
 
 {% block titleextra %}{{ book.pretty_title }}{% endblock %}
@@ -27,6 +28,8 @@
       </section>
     {% endif %}
 
+    {% annoy_banner 'book-page' %}
+
     <section class="see-also">
       <h2>{% trans "See also" %}:</h2>
       {% related_books book taken=book.other_versions|length %}
index 7696c14..4bcb30e 100644 (file)
@@ -1,6 +1,7 @@
 {% extends "base/base.html" %}
 {% load i18n %}
 {% load work_list from catalogue_tags %}
+{% load annoy_banner from annoy %}
 
 {% block titleextra %}{% trans "Theme" %} {{ theme }} {% trans "in work " %} {{ book }}{% endblock %}
 
@@ -12,6 +13,9 @@
     <a href="{{ theme.get_absolute_url }}">{{ theme }}</a>
     <br/>{% trans "in work " %}
     <a href="{{ book.get_absolute_url }}">{{ book }}</a></h1>
+
+  {% annoy_banner 'book-fragment-list' %}
+
   </div>
 
   <div class="right-column">
index a1c0b23..c08f76c 100644 (file)
@@ -1,7 +1,9 @@
 {% extends "catalogue/viewer_base.html" %}
 {% load i18n %}
 {% load catalogue_tags %}
+{% load chunks %}
 {% load thumbnail %}
+{% load annoy_banners from annoy %}
 
 
 {% block title %}{{ book.pretty_title }}{% endblock %}
   <div class="box" id="book-short">
     {% include 'catalogue/book_short.html' %}
   </div>
+
+  <div id="annoy-stubs">
+    {% annoy_banners 'book-text-intermission' %}
+
+    {% for insert in inserts %}
+      {% with text=insert.choose %}
+        <div class="dynamic-insert{% if text.image %} with-image{% endif %}" style="{% if text.text_color %}color: {{ text.text_color }};{% endif %}{% if text.background_color %}{{ text.background_color }}{% endif %}" data-paragraphs="{{ insert.paragraphs }}">
+          <a href="{% url 'club' %}">
+            <div class="text">
+              {{ text.text }}
+            </div>
+            {% if text.image %}
+              <img src="{% thumbnail text.image '120x120' as thumb %}{{ thumb.url }}{% empty %}{{ text.image.url }}{% endthumbnail %}">
+            {% endif %}
+          </a>
+        </div>
+      {% endwith %}
+    {% endfor %}
+  </div>
 {% endblock footer %}
index a975d67..6df862c 100644 (file)
@@ -3,6 +3,7 @@
 {% load static from static %}
 {% load pipeline %}
 {% load piwik_tags %}
+{% load annoy_banner from annoy %}
 <html class="no-js">
   <head>
     <meta charset="utf-8">
@@ -13,7 +14,7 @@
     {% block extrahead %}{% endblock %}
   </head>
   <body id="{% block body-id %}reader{% endblock %}">
-    {% include "annoy.html" %}
+    {% annoy_banner 'top' %}
     <nav id="no-menu"><ul>
       <li><a href="#" id="menu-toggle-on"></a></li>
       {% block no-menu-extra %}{% endblock %}
index 007ebe4..cfed6ca 100644 (file)
@@ -19,6 +19,7 @@ from django.views.decorators.cache import never_cache
 
 from ajaxable.utils import AjaxableFormView
 from club.models import Membership
+from annoy.models import DynamicTextInsert
 from pdcounter import views as pdcounter_views
 from picture.models import Picture, PictureArea
 from catalogue import constants
@@ -314,9 +315,11 @@ def book_text(request, slug):
         raise Http404
     with book.html_file.open('r') as f:
         book_text = f.read()
+
     return render(request, 'catalogue/book_text.html', {
         'book': book,
         'book_text': book_text,
+        'inserts': DynamicTextInsert.objects.all()
     })
 
 
index 3e0391b..a2dfbb9 100644 (file)
@@ -2,22 +2,23 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.conf.urls import url
+from annoy.utils import banner_exempt
 from . import views
 
 
 urlpatterns = [
-    url(r'^$', views.JoinView.as_view(), name='club_join'),
-    url(r'^info/$', views.ClubView.as_view(), name='club'),
+    url(r'^$', banner_exempt(views.JoinView.as_view()), name='club_join'),
+    url(r'^info/$', banner_exempt(views.ClubView.as_view()), name='club'),
 
-    url(r'^plan/(?P<key>[-a-z0-9]+)/$', views.ScheduleView.as_view(), name='club_schedule'),
-    url(r'^plan/(?P<key>[-a-z0-9]+)/dziekujemy/$', views.ScheduleThanksView.as_view(), name='club_thanks'),
+    url(r'^plan/(?P<key>[-a-z0-9]+)/$', banner_exempt(views.ScheduleView.as_view()), name='club_schedule'),
+    url(r'^plan/(?P<key>[-a-z0-9]+)/dziekujemy/$', banner_exempt(views.ScheduleThanksView.as_view()), name='club_thanks'),
 
     url(r'^przylacz/(?P<key>[-a-z0-9]+)/$', views.claim, name='club_claim'),
     url(r'^anuluj/(?P<key>[-a-z0-9]+)/$', views.cancel, name='club_cancel'),
     url(r'^testowa-platnosc/(?P<key>[-a-z0-9]+)/$', views.DummyPaymentView.as_view(), name='club_dummy_payment'),
 
-    url(r'platnosc/payu/cykl/(?P<key>.+)/', views.PayURecPayment.as_view(), name='club_payu_rec_payment'),
-    url(r'platnosc/payu/(?P<key>.+)/', views.PayUPayment.as_view(), name='club_payu_payment'),
+    url(r'platnosc/payu/cykl/(?P<key>.+)/', banner_exempt(views.PayURecPayment.as_view()), name='club_payu_rec_payment'),
+    url(r'platnosc/payu/(?P<key>.+)/', banner_exempt(views.PayUPayment.as_view()), name='club_payu_payment'),
 
     url(r'notify/(?P<pk>\d+)/', views.PayUNotifyView.as_view(), name='club_payu_notify'),
 
index 81756be..85272b9 100644 (file)
@@ -6,6 +6,7 @@ INSTALLED_APPS_OUR = [
     'wolnelektury',
     # our
     'ajaxable',
+    'annoy',
     'api',
     'catalogue',
     'chunks',
index f98d7ee..eb6c851 100644 (file)
@@ -30,7 +30,7 @@ PIPELINE = {
 
                 'sponsors/css/sponsors.css',
 
-                'css/annoy.css',
+                'annoy/banner.scss',
 
                 'css/ui-lightness/jquery-ui-1.8.16.custom.css',
 
@@ -50,7 +50,8 @@ PIPELINE = {
             'source_filenames': [
                 'scss/book_text.scss',
                 'css/new.book.css',
-                'css/annoy.css',
+                'annoy/banner.scss',
+                'annoy/book_text.scss',
 
                 'css/master.picture.css',
             ],
@@ -116,7 +117,7 @@ PIPELINE = {
                 'funding/funding.js',
                 'club/form.js',
 
-                'js/annoy.js',
+                'annoy/banner.js',
                 ),
             'output_filename': 'js/base.min.js',
         },
@@ -153,12 +154,13 @@ PIPELINE = {
                 'js/book_text/toc.js',
                 'js/locale.js',
                 'js/dialogs.js',
+                'annoy/book_text.js',
 
                 'js/contrib/jquery.highlightfade.js',
                 'js/contrib/raphael-min.js',
                 'player/openplayer.js',
                 'js/contrib/progressSpin.min.js',
-                'js/annoy.js',
+                'annoy/banner.js',
             ],
             'output_filename': 'js/book_text.js',
         },
diff --git a/src/wolnelektury/static/css/annoy.css b/src/wolnelektury/static/css/annoy.css
deleted file mode 100755 (executable)
index 6519605..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#annoy-on {
-    font-size: 13px;
-    line-height: 1.15em;
-
-    background: orange;
-    z-index: 99;
-    font-family: Arial, sans-serif;
-    display: block;
-    padding: .1em 1em;
-    text-align:center;
-    border-radius: 0 0 0 1em;
-    position: absolute;
-    top: 0;
-    right: 0;
-    color: black;
-}
-
-#annoy {
-    padding: 1em 5em 1em 0;
-    display: none;
-    background: orange;
-    position: relative;
-    z-index: 99;
-}
-#annoy p {
-    margin: 0;
-}
-#annoy a {
-    color: black;
-    display: block;
-    font-size: 2em;
-    text-align: center;
-}
-
-#annoy .banner {
-    display: block;
-    margin: auto;
-    width: 975px;
-    background: none;
-}
-
-#annoy .banner img {
-    display: block;
-}
-
-#annoy a#annoy-off {
-    padding: .5em 1em .5em;
-    width: 1em;
-    text-align:center;
-    font-family: Arial, sans-serif;
-    display: block;
-
-    border-radius: 0 0 0 1em;
-    position: absolute;
-    top: 0;
-    right: 0;
-    color: black;
-    font-size: 13px;
-}
-#annoy a#annoy-off:hover {
-    cursor: pointer;
-}
-
-@media screen and (min-width: 1024px) {
-    #annoy-content {
-        width: 975px;
-        margin: auto;
-    }
-}
diff --git a/src/wolnelektury/static/js/annoy.js b/src/wolnelektury/static/js/annoy.js
deleted file mode 100644 (file)
index f2a00f6..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-(function($) {
-    $(function() {
-
-var tag = "annoyed2019wspieraj";
-
-$("#annoy-on").click(function(e) {
-    e.preventDefault();
-    $("#annoy").slideDown('fast');
-    $(this).hide();
-    if (Modernizr.localstorage) localStorage.removeItem(tag);
-});
-
-$("#annoy-off").click(function() {
-    $("#annoy").slideUp('fast');
-    $("#annoy-on").show();
-    if (Modernizr.localstorage) localStorage[tag] = true;
-});
-
-
-if (Modernizr.localstorage) {
-    if (!localStorage[tag]) {
-        $("#annoy-on").hide();
-        $("#annoy").show();
-    }
-}
-
-
-
-    });
-})(jQuery);
index 81a9ab8..fbcc45e 100755 (executable)
 
 
 .book-wide-box .cite {
+    display: block;
+    color: black;
     background-color: #f7f7f7;
     vertical-align: middle;
     @include min-screen(1000px) {
index 4c765f6..bbd6b67 100755 (executable)
@@ -9,7 +9,11 @@
     .toggle {
         @include size(font-size, 11px);
         display: block;
-        @include size(padding, 5px 0);
+        @include size(padding, 5px 16px);
+    }
+    .source {
+        padding: 16px;
+        margin: 0;
     }
 }
 
diff --git a/src/wolnelektury/templates/annoy.html b/src/wolnelektury/templates/annoy.html
deleted file mode 100644 (file)
index 89c596d..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-{% load static %}
-<a id='annoy-on' href="/towarzystwo/">Wesprzyj</a>
-<div id='annoy'>
-  <div id='annoy-content'>
-
-    <!--a href="http://nowoczesnapolska.org.pl/pomoz-nam/1-procent/">
-      <img src='{% static "img/procent.png" %}' alt="Logo akcji 1%" style="float:left;margin: 0 2em" />
-    </a>
-    <p>
-      Czy wiesz, że możesz pomóc nam rozwijać Wolne Lektury, przekazując 1% swojego podatku?
-      To bardzo proste - wystarczy, że w zeznaniu podatkowym podasz nasz numer
-      <strong>KRS 0000070056</strong>.
-    </p>
-
-    <p><a href="http://nowoczesnapolska.org.pl/pomoz-nam/wesprzyj-nas/">Dowiedz się więcej</a></p-->
-    <p>
-    <a href="/towarzystwo/">
-       Wspieraj Wolne Lektury
-    </a>
-    </p>
-
-  </div>
-  <a id='annoy-off'>x</a>
-  <div style="clear:both;"></div>
-</div>
index 3945716..8bdd431 100644 (file)
@@ -8,6 +8,7 @@
     {% load cache %}
     {% load chunk from chunks %}
     {% load sponsor_page from sponsors %}
+    {% load annoy_banner from annoy %}
     {% get_current_language as LANGUAGE_CODE %}
     <head>
       <meta charset="utf-8">
@@ -35,7 +36,8 @@
       {% block bodycontent %}
 
         {% block annoy %}
-          {% include "annoy.html" %}
+          {% annoy_banner 'blackout' %}
+          {% annoy_banner 'top' %}
           {% if not funding_no_show_current %}
             {% cache 120 funding_top_bar LANGUAGE_CODE %}
               {% funding_top_bar %}