This commit is contained in:
khashashin 2017-09-05 14:04:28 +02:00
commit d5c13742ce
17 changed files with 277 additions and 44 deletions

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from datetime import datetime from datetime import datetime
from guess_language import guess_language
def parse(obj, raw, stream): def parse(obj, raw, stream):
""" """
@ -47,14 +48,18 @@ def parse(obj, raw, stream):
else: else:
obj.content = '' obj.content = ''
# Detect language
obj.lang = guess_language(obj.content) or ''
# Collect tags # Collect tags
tags = [] tags = []
for tag in obj.raw['tags']: if 'tags' in obj.raw:
if 'label' in tag: for tag in obj.raw['tags']:
label = tag['label'].replace(',','-') if 'label' in tag:
label = label.strip().lower() label = tag['label'].replace(',','-')
if len(label) > 3 and not label in tags: label = label.strip().lower()
tags.append(label) if len(label) > 3 and not label in tags:
obj.tags = ','.join(tags) tags.append(label)
obj.tags = ','.join(tags)
return obj return obj

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-09-04 20:33
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('feedler', '0002_feedpage'),
]
operations = [
migrations.AddField(
model_name='entry',
name='lang',
field=models.CharField(blank=True, default='', max_length=2),
),
]

View file

@ -61,5 +61,7 @@ def handle_save_settings(sender, instance, *args, **kwargs):
except Entry.DoesNotExist: except Entry.DoesNotExist:
logger.info("Adding entry '%s'" % eid) logger.info("Adding entry '%s'" % eid)
entry = Entry() entry = Entry()
# Parse the Feedly object
entry = feedparser.parse(entry, raw_entry, stream) entry = feedparser.parse(entry, raw_entry, stream)
# Persist resulting object
entry.save() entry.save()

View file

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.db import models from django.db import models
from django.utils import translation
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from wagtail.wagtailcore.models import Page, Orderable from wagtail.wagtailcore.models import Page, Orderable
from wagtail.wagtailadmin.edit_handlers import FieldPanel from wagtail.wagtailadmin.edit_handlers import FieldPanel
@ -13,6 +15,14 @@ class Stream(models.Model):
def __str__(self): def __str__(self):
return self.title return self.title
LANGUAGE_CHOICES = (
('de', 'Deutsch'),
('fr', 'Français'),
('it', 'Italiano'),
('en', 'English'),
('', ' * * * '),
)
class Entry(models.Model): class Entry(models.Model):
"""Implementation of the Entry from the feedly API as generic Django model """Implementation of the Entry from the feedly API as generic Django model
""" """
@ -25,7 +35,7 @@ class Entry(models.Model):
author = models.CharField(max_length=255, blank=True) author = models.CharField(max_length=255, blank=True)
link = models.URLField() link = models.URLField()
visual = models.URLField(blank=True) visual = models.URLField(blank=True)
lang = models.CharField(max_length=2, blank=True, default='', choices=LANGUAGE_CHOICES)
content = models.TextField() content = models.TextField()
tags = models.TextField(blank=True) tags = models.TextField(blank=True)
@ -53,14 +63,28 @@ class FeedPage(Page):
entries = Entry.objects.filter(stream=self.stream) entries = Entry.objects.filter(stream=self.stream)
else: else:
entries = Entry.objects.all() entries = Entry.objects.all()
# Filter out by chosen language
curlang = translation.get_language()
if curlang in ['de']:
entries = entries.exclude(lang='fr')
elif curlang in ['fr']:
entries = entries.exclude(lang='de')
# Order by most recent date first # Order by most recent date first
entries = entries.order_by('-published') return entries.order_by('-published')[:72]
return entries[:10]
def get_context(self, request): def get_context(self, request):
# Update template context # Update template context
context = super(FeedPage, self).get_context(request) context = super(FeedPage, self).get_context(request)
context['feedentries'] = self.feedentries
# Wrap with pagination
paginator = Paginator(self.feedentries, 9)
page = request.GET.get('page')
try:
feedentries = paginator.page(page)
except (PageNotAnInteger, EmptyPage):
feedentries = paginator.page(1)
context['feedentries'] = feedentries
return context return context
class Meta: class Meta:

View file

@ -19,12 +19,32 @@
</div> </div>
</section> </section>
<!-- Page body --> <!-- Page body -->
<section id="news" class="feedpage-body"> <section id="news" class="feedpage-body">
<div class="container"> <div class="container">
<!-- Pagination -->
<center>
<ul class="pagination">
{% if feedentries.has_previous %}
<li><a href="?page={{ feedentries.previous_page_number }}" title="Previous">
<span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span></a></li>
{% endif %}
{% for page_num in feedentries.paginator.page_range %}
<li {% if page_num == feedentries.number %}class="active"{% endif %}>
<a href="?page={{ page_num }}">{{ page_num }}</a></li>
{% endfor %}
{% if feedentries.has_next %}
<li><a href="?page={{ feedentries.next_page_number }}" title="Next">
<span class="glyphicon glyphicon-arrow-right" aria-hidden="true"></span></a></li>
{% endif %}
</ul>
</center>
<div class="row"> <div class="row">
{% for entry in feedentries %} {% for entry in feedentries %}
<div class="col-md-4 col-sm-6 col-xs-12"> <div class="col-md-4 col-sm-6 col-xs-12 news-entry">
{% if entry.visual %} {% if entry.visual %}
<div class="panel panel-default"> <div class="panel panel-default">
<img src="{{ entry.visual }}"> <img src="{{ entry.visual }}">
@ -34,14 +54,13 @@
<div class="panel-body"> <div class="panel-body">
<h3><span>{{ entry.title|striptags|truncatewords_html:10 }}</span></h3> <h3><span>{{ entry.title|striptags|truncatewords_html:10 }}</span></h3>
<p> <p>
<em><small><span>{{ entry.author }}</span></small></em><br><br> {{ entry.content|striptags|truncatewords_html:25 }}
{{ entry.content|striptags|truncatewords_html:25 }} <em><span>{{ entry.author }}</span></em>
</p> </p>
</div> </div>
<a href="{{ entry.link }}" target="_blank" class="fill"></a> <a href="{{ entry.link }}" target="_blank" class="fill"></a>
</div> </div>
</div> </div>
<!-- {{ entry.raw }} -->
{% empty %} {% empty %}
<!-- No news today --> <!-- No news today -->
{% endfor %} {% endfor %}

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-09-04 20:33
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('home', '0019_auto_20170703_1244'),
]
operations = [
migrations.AlterField(
model_name='contactformfield',
name='field_type',
field=models.CharField(choices=[('singleline', 'Single line text'), ('multiline', 'Multi-line text'), ('email', 'Email'), ('number', 'Number'), ('url', 'URL'), ('checkbox', 'Checkbox'), ('checkboxes', 'Checkboxes'), ('dropdown', 'Drop down'), ('multiselect', 'Multiple select'), ('radio', 'Radio buttons'), ('date', 'Date'), ('datetime', 'Date/time')], max_length=16, verbose_name='field type'),
),
]

View file

@ -16,6 +16,8 @@ from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
from wagtail.wagtailsearch import index from wagtail.wagtailsearch import index
from puput.models import EntryPage, BlogPage from puput.models import EntryPage, BlogPage
from feedler.models import Entry
from itertools import chain
from ..util import TranslatedField from ..util import TranslatedField
@ -193,11 +195,6 @@ class HomePage(Page):
'infos_fr', 'infos_fr',
) )
# news_home_de = models.ForeignKey(
# 'puput.EntryPage',
# null=True, blank=True, on_delete=models.SET_NULL,
# )
content_panels = Page.content_panels + [ content_panels = Page.content_panels + [
MultiFieldPanel([ MultiFieldPanel([
FieldPanel('intro_de', classname="full"), FieldPanel('intro_de', classname="full"),
@ -227,16 +224,29 @@ class HomePage(Page):
if not curlang in ['de', 'fr']: curlang = 'de' # Default language if not curlang in ['de', 'fr']: curlang = 'de' # Default language
parent = BlogPage.objects.filter(slug='news-%s' % curlang) parent = BlogPage.objects.filter(slug='news-%s' % curlang)
if not parent: return [] if not parent: return []
entries = EntryPage.objects.live().descendant_of(parent[0]) posts = EntryPage.objects.live().descendant_of(parent[0])
# Order by most recent date first # Order by most recent date first
entries = entries.order_by('-date') posts = posts.order_by('-date')
return entries[:6] return posts[:3]
@property
def newsentries(self):
# Get the last few news entries
entries = Entry.objects.all().order_by('-published')
# Filter out by current language
curlang = translation.get_language()
if curlang in ['de']:
entries = entries.exclude(lang='fr')
elif curlang in ['fr']:
entries = entries.exclude(lang='de')
return entries[:3]
def get_context(self, request): def get_context(self, request):
# Update template context # Update template context
context = super(HomePage, self).get_context(request) context = super(HomePage, self).get_context(request)
context['featured'] = self.featured context['featured'] = self.featured
context['blogentries'] = self.blogentries context['blogentries'] = self.blogentries
context['newsentries'] = self.newsentries
return context return context
parent_page_types = ['wagtailcore.Page'] parent_page_types = ['wagtailcore.Page']

View file

@ -13,6 +13,8 @@
<!-- Banner (articles) --> <!-- Banner (articles) -->
{% include 'banner.html' %} {% include 'banner.html' %}
<div class="home_page">
<!-- News --> <!-- News -->
{% include 'news.html' %} {% include 'news.html' %}
@ -28,4 +30,6 @@
<!-- Infoblocks --> <!-- Infoblocks -->
{% include 'infos.html' %} {% include 'infos.html' %}
</div>
{% endblock %} {% endblock %}

View file

@ -1,10 +1,12 @@
{% load wagtailcore_tags wagtailimages_tags puput_tags %} {% load wagtailcore_tags wagtailimages_tags puput_tags %}
<!-- Front page news -->
<section id="news"> <section id="news">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
{% for entry in blogentries %} {% for entry in blogentries %}
<div class="col-md-4 col-sm-6 col-xs-12"> <!-- Blog post {{ entry.id }} -->
<div class="col-md-4 col-sm-6 col-xs-12 blog-entry">
<div class="panel panel-default"> <div class="panel panel-default">
{% if entry.header_image %} {% if entry.header_image %}
{% image entry.header_image fill-360x270 %} {% image entry.header_image fill-360x270 %}
@ -18,11 +20,35 @@
{{ entry.body|striptags|truncatewords_html:40 }} {{ entry.body|striptags|truncatewords_html:40 }}
{% endif %} {% endif %}
</p> </p>
<a href="{% pageurl entry %}" class="btn btn-default btn-xs">Mehr erfahren</a> <a href="{% pageurl entry %}" class="btn btn-default btn-xs"> 🡆 </a>
</div> </div>
<a href="{% pageurl entry %}" class="fill"></a> <a href="{% pageurl entry %}" class="fill"></a>
</div> </div>
</div> </div>
{% empty %}
<!-- No blogs today -->
{% endfor %}
<!-- <h4 class="partner-news"><a href="/aktuelles/">Partner news</a></h4> -->
{% for entry in newsentries %}
<!-- News entry {{ entry.id }} -->
<div class="col-md-4 col-sm-6 col-xs-12 news-entry">
{% if entry.visual %}
<div class="panel panel-default">
<img src="{{ entry.visual }}">
{% else %}
<div class="panel panel-fulltext">
{% endif %}
<div class="panel-body">
<h3><span>{{ entry.title|striptags|truncatewords_html:10 }}</span></h3>
<p>
{{ entry.content|striptags|truncatewords_html:25 }}
<em><span>{{ entry.author }}</span></em>
</p>
</div>
<a href="/aktuelles/" class="fill"></a>
<!-- <a href="{{ entry.link }}" target="_blank" class="fill"></a> -->
</div>
</div>
{% empty %} {% empty %}
<!-- No news today --> <!-- No news today -->
{% endfor %} {% endfor %}

View file

@ -127,10 +127,15 @@ AUTH_PASSWORD_VALIDATORS = [
}, },
] ]
PASSWORD_REQUIRED_TEMPLATE = 'password.html'
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/ # https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGES = (
('de', u'Deutsch'),
('fr', u'Français'),
)
LANGUAGE_CODE = 'de' # default language LANGUAGE_CODE = 'de' # default language
TIME_ZONE = 'Europe/Zurich' TIME_ZONE = 'Europe/Zurich'

View file

@ -80,8 +80,11 @@
} }
} }
/* Home page banner background */
body.template-frontpage {
background: $gray-lighter;
.home_page section:nth-child(even) { background: white; }
}
/* Page header */ /* Page header */
$banner-height: 700px; $banner-height: 700px;

View file

@ -46,6 +46,12 @@
margin: 10px 0 0 15px; margin: 10px 0 0 15px;
text-align: center; text-align: center;
} }
em {
display: block;
font-size: 95%;
margin: 0.5em 0;
font-weight: 500;
}
} }
// expand link over the thumbnail // expand link over the thumbnail
@ -75,6 +81,29 @@
transform: rotateY(0); transform: rotateY(0);
} }
} }
.news-entry {
// height: 8em;
.panel {
background: none;
box-shadow: none;
border: none;
}
.panel-body {
// height: 50%;
background-color: #ffffff;
border-radius: 4px;
border-top: 3px solid $brand-primary;
// border: 2px solid rgba(38, 67, 169, 0.8);
*,a { color: black !important; }
}
}
.partner-news {
text-align: center;
margin-top: 2em;
margin-bottom: 1em;
}
} }
// News detail article // News detail article

View file

@ -18,11 +18,11 @@ $(document).ready(function() {
nextArrow: '<span class="arrow right glyphicon glyphicon-chevron-right" aria-hidden="true">Next</span>', nextArrow: '<span class="arrow right glyphicon glyphicon-chevron-right" aria-hidden="true">Next</span>',
}); });
// Formatting of live news // Pastel colors on live news
$('.feedpage-body .panel').each(function() { // $('.feedpage-body .panel').each(function() {
var hue = Math.floor(Math.random() * 360); // var hue = Math.floor(Math.random() * 360);
var pastel = 'hsl(' + hue + ', 100%, 87.5%)'; // var pastel = 'hsl(' + hue + ', 100%, 87.5%)';
$(this).css('border-top', '3px solid ' + pastel); // $(this).css('border-top', '3px solid ' + pastel);
}); // });
}); });

View file

@ -1,11 +1,35 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}404 - Page not found{% endblock %} {% block title %}404 - Seite nicht gefunden{% endblock %}
{% block body_class %}template-404{% endblock %} {% block body_class %}template-404{% endblock %}
{% block content %} {% block content %}
<h1>Page not found</h1> <section>
<div class="container">
<h2>Sorry, this page could not be found.</h2> <h1>Nicht gefunden/Introuvable</h1>
<center>
<!-- Search form -->
<form action="/search/" method="get">
<input type="text" name="query" value="" id="search-form-404" class="form-control">
<button type="submit" title="Search">
<span class="glyphicon glyphicon-search" aria-hidden="false"></span>
</button>
</form>
<em><p>
<br>Diese Seite konnte nicht gefunden werden. Möchten Sie eine Suche starten?
<br>Désolé, cette page est introuvable. Voulez-vous faire une recherche sur le site?
<br>Sorry, this page could not be found. Would you like to do a search of the site?
</p></em>
</center>
</div>
</section>
<script>
document.getElementById('search-form-404').value = document.location.pathname.split('/')[2];
</script>
{% endblock %} {% endblock %}

View file

@ -10,5 +10,7 @@
<h1>Internal server error</h1> <h1>Internal server error</h1>
<h2>Sorry, there seems to be an error. Please try again soon.</h2> <h2>Sorry, there seems to be an error. Please try again soon.</h2>
<p>In case of persistent issues, write to <b>info@datalets.ch</b></p>
</body> </body>
</html> </html>

View file

@ -0,0 +1,39 @@
{% extends "base.html" %}
{% block title %}Password Required{% endblock %}
{% block body_class %}password-required{% endblock %}
{% block content %}
<section>
<div class="container">
<h1>Password requis/erforderlich</h1>
<center>
<form action="{{ action_url }}" method="POST">
{% csrf_token %}
{{ form.non_field_errors }}
<div>
{{ form.password.errors }}
{{ form.password }}
</div>
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
<input type="submit" value="Continue/Fortfahren" />
</form>
<em><p>
<br>Sie benötigen ein Passwort, um auf diese Seite zuzugreifen.
<br>Vous avez besoin d'un mot de passe pour accéder à cette page.
<br>You need a password to access this page.
</p></em>
</center>
</div>
</section>
{% endblock %}

View file

@ -1,15 +1,16 @@
# Updated: 30.5.2017 # Updated: 4.9.2017
# Core # Core
wagtail==1.11 wagtail==1.12.1
Django==1.11.3 Django==1.11.4
# Database # Database
psycopg2==2.7.1 psycopg2==2.7.3.1
dj-database-url==0.4.2 dj-database-url==0.4.2
# Addons # Content
puput==0.8 puput==0.9
guess-language-spirit==0.5.3
# Search # Search
elasticsearch>=2.0.0,<3.0.0 elasticsearch>=2.0.0,<3.0.0
@ -20,7 +21,7 @@ django-redis==4.8.0
# Frontend # Frontend
django-libsass==0.7 django-libsass==0.7
libsass==0.13.2 libsass==0.13.2
Pillow==4.2.0 Pillow==4.2.1
# Development tools # Development tools
stellar==0.4.3 stellar==0.4.3
@ -29,4 +30,4 @@ stellar==0.4.3
gunicorn==19.7.1 gunicorn==19.7.1
whitenoise==3.3.0 whitenoise==3.3.0
ConcurrentLogHandler==0.9.1 ConcurrentLogHandler==0.9.1
django-anymail==0.10 django-anymail==0.11.1