Merge branch 'preview'
authorJan Szejko <janek37@gmail.com>
Tue, 5 Jun 2018 14:46:14 +0000 (16:46 +0200)
committerJan Szejko <janek37@gmail.com>
Tue, 5 Jun 2018 15:11:50 +0000 (17:11 +0200)
# Conflicts:
# src/api/handlers.py
# src/catalogue/models/book.py
# src/catalogue/templates/catalogue/book_short.html

15 files changed:
src/ajaxable/templatetags/ajaxable_tags.py
src/ajaxable/utils.py
src/api/handlers.py
src/catalogue/migrations/0024_book_audio_length.py [new file with mode: 0644]
src/catalogue/migrations/0025_merge.py [new file with mode: 0644]
src/catalogue/models/book.py
src/catalogue/signals.py
src/catalogue/templates/catalogue/book_short.html
src/newsletter/forms.py
src/wolnelektury/forms.py
src/wolnelektury/management/commands/clean_social_accounts.py [new file with mode: 0644]
src/wolnelektury/static/scss/main/book_box.scss
src/wolnelektury/templates/socialaccount/signup.html [new file with mode: 0644]
src/wolnelektury/urls.py
src/wolnelektury/views.py

index ec90e47..31897d1 100644 (file)
@@ -23,7 +23,7 @@ def placeholdized_ul(form):
 @register.filter
 def pretty_field(field, template=None):
     if template is None:
-        template = '''
+        template = u'''
             <li>
               <span class="error">%(errors)s</span>
               <label class="nohide"><span class="label">%(label)s: </span>%(input)s</label>
@@ -32,7 +32,7 @@ def pretty_field(field, template=None):
     return mark_safe(template % {
         'errors': field.errors,
         'input': field,
-        'label': force_unicode(field.label),
+        'label': ('*' if field.field.required else '') + force_unicode(field.label),
         'helptext': force_unicode(field.help_text),
     })
 
index 8052e6b..82de847 100755 (executable)
@@ -48,7 +48,7 @@ def require_login(request):
 
 def placeholdized(form):
     for field in form.fields.values():
-        field.widget.attrs['placeholder'] = field.label
+        field.widget.attrs['placeholder'] = field.label + ('*' if field.required else '')
     return form
 
 
index c899976..3e4e093 100644 (file)
@@ -164,7 +164,8 @@ class BookDetailHandler(BaseHandler, BookDetails):
     """
     allowed_methods = ['GET']
     fields = ['title', 'parent', 'children'] + Book.formats + [
-        'media', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'simple_cover', 'fragment_data', 'preview'] + [
+        'media', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'simple_cover', 'fragment_data', 'audio_length',
+        'preview'] + [
             category_plural[c] for c in book_tag_categories]
 
     @piwik_track
@@ -183,7 +184,7 @@ class AnonymousBooksHandler(AnonymousBaseHandler, BookDetails):
     """
     allowed_methods = ('GET',)
     model = Book
-    fields = book_tag_categories + ['href', 'title', 'url', 'cover', 'cover_thumb', 'slug', 'simple_thumb']
+    fields = book_tag_categories + ['href', 'title', 'url', 'cover', 'cover_thumb', 'slug', 'simple_thumb', 'has_audio']
 
     @classmethod
     def genres(cls, book):
@@ -322,7 +323,7 @@ class QuerySetProxy(models.QuerySet):
 
 class FilterBooksHandler(AnonymousBooksHandler):
     fields = book_tag_categories + [
-        'href', 'title', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'slug', 'key']
+        'href', 'title', 'url', 'cover', 'cover_thumb', 'simple_thumb', 'has_audio', 'slug', 'key']
 
     def parse_bool(self, s):
         if s in ('true', 'false'):
@@ -433,6 +434,7 @@ def add_file_getters():
     for book_format in Book.formats:
         setattr(BookDetails, book_format, _file_getter(book_format))
 
+
 add_file_getters()
 
 
@@ -624,7 +626,7 @@ class FragmentsHandler(BaseHandler, FragmentDetails):
 
         """
         try:
-            tags, ancestors = read_tags(tags, allowed=self.categories)
+            tags, ancestors = read_tags(tags, request, allowed=self.categories)
         except ValueError:
             return rc.NOT_FOUND
         fragments = Fragment.tagged.with_all(tags).select_related('book')
@@ -702,7 +704,8 @@ class UserShelfHandler(BookDetailHandler):
             return rc.NOT_FOUND
         after = request.GET.get('after')
         count = int(request.GET.get('count', 50))
-        ids = BookUserData.objects.filter(user=request.user, complete=state == 'complete').values_list('book_id', flat=True)
+        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')
         if after:
             books = books.filter(slug__gt=after)
diff --git a/src/catalogue/migrations/0024_book_audio_length.py b/src/catalogue/migrations/0024_book_audio_length.py
new file mode 100644 (file)
index 0000000..89e38af
--- /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 = [
+        ('catalogue', '0023_book_abstract'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='book',
+            name='audio_length',
+            field=models.CharField(max_length=8, verbose_name='audio length', blank=True),
+        ),
+    ]
diff --git a/src/catalogue/migrations/0025_merge.py b/src/catalogue/migrations/0025_merge.py
new file mode 100644 (file)
index 0000000..66c8c8f
--- /dev/null
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('catalogue', '0024_book_audio_length'),
+        ('catalogue', '0024_auto_20180510_1407'),
+    ]
+
+    operations = [
+    ]
index 027d272..004c27e 100644 (file)
@@ -72,6 +72,7 @@ class Book(models.Model):
     wiki_link = models.CharField(blank=True, max_length=240)
     print_on_demand = models.BooleanField(_('print on demand'), default=False)
     recommended = models.BooleanField(_('recommended'), default=False)
+    audio_length = models.CharField(_('audio length'), blank=True, max_length=8)
     preview = models.BooleanField(_('preview'), default=False)
     preview_until = models.DateField(_('preview until'), blank=True, null=True)
 
@@ -211,6 +212,32 @@ class Book(models.Model):
     def is_foreign(self):
         return self.language_code() != settings.LANGUAGE_CODE
 
+    def set_audio_length(self):
+        length = self.get_audio_length()
+        if length > 0:
+            self.audio_length = self.format_audio_length(length)
+            self.save()
+
+    @staticmethod
+    def format_audio_length(seconds):
+        if seconds < 60*60:
+            minutes = seconds // 60
+            seconds = seconds % 60
+            return '%d:%02d' % (minutes, seconds)
+        else:
+            hours = seconds // 3600
+            minutes = seconds % 3600 // 60
+            seconds = seconds % 60
+            return '%d:%02d:%02d' % (hours, minutes, seconds)
+
+    def get_audio_length(self):
+        from mutagen.mp3 import MP3
+        total = 0
+        for media in self.get_mp3():
+            audio = MP3(media.file.path)
+            total += audio.info.length
+        return int(total)
+
     def has_media(self, type_):
         if type_ in Book.formats:
             return bool(getattr(self, "%s_file" % type_))
@@ -277,19 +304,18 @@ class Book(models.Model):
     has_description.short_description = _('description')
     has_description.boolean = True
 
-    # ugly ugly ugly
     def has_mp3_file(self):
-        return bool(self.has_media("mp3"))
+        return self.has_media("mp3")
     has_mp3_file.short_description = 'MP3'
     has_mp3_file.boolean = True
 
     def has_ogg_file(self):
-        return bool(self.has_media("ogg"))
+        return self.has_media("ogg")
     has_ogg_file.short_description = 'OGG'
     has_ogg_file.boolean = True
 
     def has_daisy_file(self):
-        return bool(self.has_media("daisy"))
+        return self.has_media("daisy")
     has_daisy_file.short_description = 'DAISY'
     has_daisy_file.boolean = True
 
@@ -762,6 +788,7 @@ def add_file_fields():
             default=''
         ).contribute_to_class(Book, field_name)
 
+
 add_file_fields()
 
 
index 3adfedb..f3cacaa 100644 (file)
@@ -19,6 +19,7 @@ from .models import BookMedia, Book, Collection, Fragment, Tag
 
 @receiver([post_save, post_delete], sender=BookMedia)
 def bookmedia_save(sender, instance, **kwargs):
+    instance.book.set_audio_length()
     instance.book.save()
 
 
index 727ffb4..675e761 100644 (file)
       {% else %}
         <p class="book-box-tools">{% trans "For now this work is only available for our subscribers." %}</p>
       {% endif %}
-      <div class="clearboth"></div>
-      {% if book.abstract %}
-        <div class="abstract more">
-          {{ book.abstract|safe }}
-        </div>
-      {% endif %}
       {% block book-box-extra-info %}{% endblock %}
-      {% block box-append %}
-      {% endblock %}
+      {% block box-append %}{% endblock %}
     </div>
+    {% if book.abstract %}
+      <div class="abstract more">
+        {{ book.abstract|safe }}
+      </div>
+    {% endif %}
     {% endwith %}
 
     {% block right-column %}
index 323fe3f..eb7afa5 100644 (file)
@@ -29,10 +29,10 @@ Więcej informacji w <a href="">polityce prywatności.</a>'''
     def data_processing(self):
         return mark_safe('%s %s %s' % (self.data_processing_part1, self.data_processing_part2, self.data_processing_part3))
 
-    def save(self):
+    def save(self, *args, **kwargs):
         try:
             # multiple inheritance mode
-            super(NewsletterForm, self).save()
+            super(NewsletterForm, self).save(*args, **kwargs)
         except AttributeError:
             pass
         if not self.cleaned_data.get('agree_newsletter'):
index 053178a..0eb1e97 100644 (file)
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from allauth.socialaccount.forms import SignupForm
 from django.contrib.auth.forms import UserCreationForm
 from django.contrib.auth.models import User
 
@@ -19,3 +20,13 @@ także w celu przesyłania newslettera Wolnych Lektur.'''
     def save(self, commit=True):
         super(RegistrationForm, self).save(commit=commit)
         NewsletterForm.save(self)
+
+
+class SocialSignupForm(NewsletterForm, SignupForm):
+    data_processing_part2 = u'''\
+Dane są przetwarzane w zakresie niezbędnym do prowadzenia serwisu, a także w celach prowadzenia statystyk, \
+ewaluacji i sprawozdawczości. W przypadku wyrażenia dodatkowej zgody adres e-mail zostanie wykorzystany \
+także w celu przesyłania newslettera Wolnych Lektur.'''
+
+    def save(self, *args, **kwargs):
+        super(SocialSignupForm, self).save(*args, **kwargs)
diff --git a/src/wolnelektury/management/commands/clean_social_accounts.py b/src/wolnelektury/management/commands/clean_social_accounts.py
new file mode 100644 (file)
index 0000000..263ee2c
--- /dev/null
@@ -0,0 +1,21 @@
+# -*- 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 allauth.socialaccount.models import SocialAccount
+from django.core.management.base import BaseCommand
+
+
+KEPT_FIELDS = {
+    'facebook': ['link', 'name', 'id', 'locale', 'timezone', 'updated_time', 'verified'],
+    'google': ['name', 'picture', 'locale', 'id', 'verified_email', 'link'],
+}
+
+
+class Command(BaseCommand):
+    def handle(self, *args, **options):
+        for provider, kept_fields in KEPT_FIELDS.iteritems():
+            for sa in SocialAccount.objects.filter(provider=provider):
+                trimmed_data = {k: v for k, v in sa.extra_data.iteritems() if k in kept_fields}
+                sa.extra_data = trimmed_data
+                sa.save()
index dd2a77f..ddb4b72 100755 (executable)
     @media screen and (min-width: 1024px) {
       display: inline-block;
       @include size(width, 590px);
+      @include size(min-height, 196px);
     }
   }
 
       @media screen and (min-width: 62.5em) {
         float: left;
         @include size(width, 536px);
+        @include size(min-height, 196px);
       }
     }
 
   overflow: hidden;
   position: relative;
 
+  @media screen and (min-width: 62.5em) {
+    @include size(width, 536px);
+  }
+
   p.paragraph {
     margin-bottom: 0;
     margin-top: 1.2em;
diff --git a/src/wolnelektury/templates/socialaccount/signup.html b/src/wolnelektury/templates/socialaccount/signup.html
new file mode 100644 (file)
index 0000000..ef4d5fc
--- /dev/null
@@ -0,0 +1,28 @@
+{% extends "socialaccount/base.html" %}
+
+{% load i18n %}
+{% load ajaxable_tags %}
+
+{% block head_title %}{% trans "Signup" %}{% endblock %}
+
+{% block content %}
+    <h1>{% trans "Sign Up" %}</h1>
+
+<p>{% blocktrans with provider_name=account.get_provider.name site_name=site.name %}You are about to use your {{provider_name}} account to login to
+{{site_name}}. As a final step, please complete the following form:{% endblocktrans %}</p>
+
+<form class="signup cuteform" id="signup_form" method="post" action="{% url 'socialaccount_signup' %}">
+  {% csrf_token %}
+  <ul>
+    {{ form.username|pretty_field }}
+    {{ form.email|pretty_field }}
+    {{ form.agree_newsletter|pretty_checkbox }}
+  </ul>
+  {% if redirect_field_value %}
+  <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
+  {% endif %}
+  <p><span class="helptext">{{ form.data_processing }}</span></p>
+  <button type="submit">{% trans "Sign Up" %} &raquo;</button>
+</form>
+
+{% endblock %}
index ee807fd..bf95306 100644 (file)
@@ -25,6 +25,7 @@ urlpatterns = [
     url(r'^uzytkownik/signup/$', views.RegisterFormView(), name='register'),
     url(r'^uzytkownik/logout/$', views.logout_then_redirect, name='logout'),
     url(r'^uzytkownik/zaloguj-utworz/$', views.LoginRegisterFormView(), name='login_register'),
+    url(r'^uzytkownik/social/signup/$', views.SocialSignupView.as_view(), name='socialaccount_signup'),
 
     # Includes.
     url(r'^latests_blog_posts.html$', views.latest_blog_posts, name='latest_blog_posts'),
index 2115151..a5db0a8 100644 (file)
@@ -4,11 +4,12 @@
 #
 from datetime import date, datetime
 import feedparser
+from allauth.socialaccount.views import SignupView
 
 from django.conf import settings
 from django.contrib import auth
 from django.contrib.auth.decorators import login_required
-from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
+from django.contrib.auth.forms import AuthenticationForm
 from django.core.cache import cache
 from django.http import HttpResponse, HttpResponseRedirect
 from django.shortcuts import render
@@ -22,7 +23,7 @@ from catalogue.models import Book, Collection, Tag, Fragment
 from ssify import ssi_included
 
 from social.utils import get_or_choose_cite
-from wolnelektury.forms import RegistrationForm
+from wolnelektury.forms import RegistrationForm, SocialSignupForm
 
 
 def main_page(request):
@@ -186,6 +187,10 @@ def widget(request):
     return render(request, 'widget.html')
 
 
+class SocialSignupView(SignupView):
+    form_class = SocialSignupForm
+
+
 def exception_test(request):
     msg = request.GET.get('msg')
     if msg: