From: Jan Szejko Date: Tue, 5 Jun 2018 14:46:14 +0000 (+0200) Subject: Merge branch 'preview' X-Git-Url: https://git.mdrn.pl/wolnelektury.git/commitdiff_plain/73b6a1639ba8f9f5fc7615c136364fdba73e5bb2?hp=0543785bb9e74989036d99eb25cf23c3ef82cdf7 Merge branch 'preview' # Conflicts: # src/api/handlers.py # src/catalogue/models/book.py # src/catalogue/templates/catalogue/book_short.html --- diff --git a/src/ajaxable/templatetags/ajaxable_tags.py b/src/ajaxable/templatetags/ajaxable_tags.py index ec90e4794..31897d1b0 100644 --- a/src/ajaxable/templatetags/ajaxable_tags.py +++ b/src/ajaxable/templatetags/ajaxable_tags.py @@ -23,7 +23,7 @@ def placeholdized_ul(form): @register.filter def pretty_field(field, template=None): if template is None: - template = ''' + template = u'''
  • %(errors)s @@ -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), }) diff --git a/src/ajaxable/utils.py b/src/ajaxable/utils.py index 8052e6ba6..82de84771 100755 --- a/src/ajaxable/utils.py +++ b/src/ajaxable/utils.py @@ -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 diff --git a/src/api/handlers.py b/src/api/handlers.py index c89997617..3e4e0931a 100644 --- a/src/api/handlers.py +++ b/src/api/handlers.py @@ -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 index 000000000..89e38affa --- /dev/null +++ b/src/catalogue/migrations/0024_book_audio_length.py @@ -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 index 000000000..66c8c8f4d --- /dev/null +++ b/src/catalogue/migrations/0025_merge.py @@ -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 = [ + ] diff --git a/src/catalogue/models/book.py b/src/catalogue/models/book.py index 027d272a3..004c27e89 100644 --- a/src/catalogue/models/book.py +++ b/src/catalogue/models/book.py @@ -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() diff --git a/src/catalogue/signals.py b/src/catalogue/signals.py index 3adfedbb0..f3cacaaf7 100644 --- a/src/catalogue/signals.py +++ b/src/catalogue/signals.py @@ -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() diff --git a/src/catalogue/templates/catalogue/book_short.html b/src/catalogue/templates/catalogue/book_short.html index 727ffb474..675e761c4 100644 --- a/src/catalogue/templates/catalogue/book_short.html +++ b/src/catalogue/templates/catalogue/book_short.html @@ -142,16 +142,14 @@ {% else %}

    {% trans "For now this work is only available for our subscribers." %}

    {% endif %} -
    - {% if book.abstract %} -
    - {{ book.abstract|safe }} -
    - {% endif %} {% block book-box-extra-info %}{% endblock %} - {% block box-append %} - {% endblock %} + {% block box-append %}{% endblock %} + {% if book.abstract %} +
    + {{ book.abstract|safe }} +
    + {% endif %} {% endwith %} {% block right-column %} diff --git a/src/newsletter/forms.py b/src/newsletter/forms.py index 323fe3f5f..eb7afa5a8 100644 --- a/src/newsletter/forms.py +++ b/src/newsletter/forms.py @@ -29,10 +29,10 @@ Więcej informacji w polityce prywatności.''' 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'): diff --git a/src/wolnelektury/forms.py b/src/wolnelektury/forms.py index 053178aa1..0eb1e973b 100644 --- a/src/wolnelektury/forms.py +++ b/src/wolnelektury/forms.py @@ -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 index 000000000..263ee2c0d --- /dev/null +++ b/src/wolnelektury/management/commands/clean_social_accounts.py @@ -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() diff --git a/src/wolnelektury/static/scss/main/book_box.scss b/src/wolnelektury/static/scss/main/book_box.scss index dd2a77f49..ddb4b7218 100755 --- a/src/wolnelektury/static/scss/main/book_box.scss +++ b/src/wolnelektury/static/scss/main/book_box.scss @@ -136,6 +136,7 @@ @media screen and (min-width: 1024px) { display: inline-block; @include size(width, 590px); + @include size(min-height, 196px); } } @@ -175,6 +176,7 @@ @media screen and (min-width: 62.5em) { float: left; @include size(width, 536px); + @include size(min-height, 196px); } } @@ -525,6 +527,10 @@ 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 index 000000000..ef4d5fc45 --- /dev/null +++ b/src/wolnelektury/templates/socialaccount/signup.html @@ -0,0 +1,28 @@ +{% extends "socialaccount/base.html" %} + +{% load i18n %} +{% load ajaxable_tags %} + +{% block head_title %}{% trans "Signup" %}{% endblock %} + +{% block content %} +

    {% trans "Sign Up" %}

    + +

    {% 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 %}

    + + + +{% endblock %} diff --git a/src/wolnelektury/urls.py b/src/wolnelektury/urls.py index ee807fd2d..bf95306d9 100644 --- a/src/wolnelektury/urls.py +++ b/src/wolnelektury/urls.py @@ -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'), diff --git a/src/wolnelektury/views.py b/src/wolnelektury/views.py index 21151510d..a5db0a83f 100644 --- a/src/wolnelektury/views.py +++ b/src/wolnelektury/views.py @@ -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: