commit
021f7823f5
28 changed files with 543 additions and 31 deletions
20
feedler/__init__.py
Normal file
20
feedler/__init__.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
__author__ = 'Oleg Lavrovsky'
|
||||
__email__ = 'oleg@datalets.ch'
|
||||
__version__ = '0.1'
|
||||
|
||||
FEEDLER_APPS = (
|
||||
# Wagtail apps
|
||||
'wagtail.wagtailcore',
|
||||
'wagtail.wagtailadmin',
|
||||
'wagtail.contrib.modeladmin',
|
||||
'wagtail.contrib.wagtailroutablepage',
|
||||
'wagtail.api.v2',
|
||||
|
||||
# Third-party apps
|
||||
'rest_framework',
|
||||
|
||||
# My apps
|
||||
'feedler',
|
||||
)
|
7
feedler/api.py
Normal file
7
feedler/api.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from wagtail.api.v2.router import WagtailAPIRouter
|
||||
from .endpoints import EntriesAPIEndpoint
|
||||
|
||||
# Create the router. "wagtailapi" is the URL namespace
|
||||
api_router = WagtailAPIRouter('wagtailapi')
|
||||
|
||||
api_router.register_endpoint('entries', EntriesAPIEndpoint)
|
5
feedler/apps.py
Normal file
5
feedler/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class FeedlerConfig(AppConfig):
|
||||
name = 'feedler'
|
26
feedler/endpoints.py
Normal file
26
feedler/endpoints.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from wagtail.contrib.wagtailapi.endpoints import BaseAPIEndpoint
|
||||
from wagtail.contrib.wagtailapi.serializers import BaseSerializer
|
||||
from wagtail.contrib.wagtailapi.filters import FieldsFilter, OrderingFilter, SearchFilter
|
||||
from wagtail.contrib.wagtailapi.pagination import WagtailPagination
|
||||
|
||||
from .models import Entry
|
||||
|
||||
class EntrySerializer(BaseSerializer):
|
||||
pass
|
||||
|
||||
class EntriesAPIEndpoint(BaseAPIEndpoint):
|
||||
base_serializer_class = EntrySerializer
|
||||
filter_backends = [FieldsFilter, OrderingFilter, SearchFilter]
|
||||
extra_api_fields = [
|
||||
'title',
|
||||
'author',
|
||||
'link',
|
||||
'visual',
|
||||
'content',
|
||||
'tags',
|
||||
'published',
|
||||
]
|
||||
name = 'entries'
|
||||
model = Entry
|
60
feedler/feedparser.py
Normal file
60
feedler/feedparser.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
def parse(obj, raw, stream):
|
||||
"""
|
||||
Parse raw JSON implementation from the Feedly API
|
||||
"""
|
||||
obj.raw = raw
|
||||
obj.stream = stream
|
||||
obj.entry_id = raw['id']
|
||||
|
||||
# Date stamp handling
|
||||
ts = raw['published'] / 1000
|
||||
obj.published = datetime.fromtimestamp(ts)
|
||||
|
||||
# Authorship and title
|
||||
obj.title = raw['title']
|
||||
if 'author' in raw['origin']:
|
||||
obj.author = raw['author']
|
||||
elif 'title' in raw['origin']:
|
||||
obj.author = raw['origin']['title']
|
||||
|
||||
# Parse links and references
|
||||
if len(raw['alternate']) > 0:
|
||||
obj.link = raw['alternate'][0]['href']
|
||||
if 'thumbnail' in raw and len(raw['thumbnail']) > 0:
|
||||
if 'url' in raw['thumbnail'][0]:
|
||||
obj.visual = raw['thumbnail'][0]['url']
|
||||
elif 'enclosure' in raw and len(raw['enclosure']) > 0:
|
||||
if 'href' in raw['enclosure'][0]:
|
||||
obj.visual = raw['enclosure'][0]['href']
|
||||
elif 'visual' in raw and 'url' in raw['visual']:
|
||||
obj.visual = raw['visual']['url']
|
||||
if obj.visual.lower().strip() == 'none':
|
||||
obj.visual = ''
|
||||
|
||||
# Collect text in nested JSON content
|
||||
if 'content' in obj.raw:
|
||||
obj.content = obj.raw['content']
|
||||
else:
|
||||
if 'summary' in obj.raw:
|
||||
if 'content' in obj.raw['summary']:
|
||||
obj.content = obj.raw['summary']['content']
|
||||
else:
|
||||
obj.content = obj.raw['summary']
|
||||
else:
|
||||
obj.content = ''
|
||||
|
||||
# Collect tags
|
||||
tags = []
|
||||
for tag in obj.raw['tags']:
|
||||
if 'label' in tag:
|
||||
label = tag['label'].replace(',','-')
|
||||
label = label.strip().lower()
|
||||
if len(label) > 3 and not label in tags:
|
||||
tags.append(label)
|
||||
obj.tags = ','.join(tags)
|
||||
|
||||
return obj
|
71
feedler/migrations/0001_initial.py
Normal file
71
feedler/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.3 on 2017-07-03 13:46
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('wagtailcore', '__latest__'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Entry',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('raw', models.TextField(blank=True, editable=False)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
('published', models.DateTimeField(auto_now_add=True)),
|
||||
('entry_id', models.CharField(blank=True, editable=False, max_length=255, unique=True)),
|
||||
('title', models.CharField(max_length=255)),
|
||||
('author', models.CharField(blank=True, max_length=255)),
|
||||
('link', models.URLField()),
|
||||
('visual', models.URLField(blank=True)),
|
||||
('content', models.TextField()),
|
||||
('tags', models.TextField(blank=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'Entries',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='FeedlySettings',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('feedly_auth', models.TextField(blank=True, help_text='Your developer authorization key')),
|
||||
('feedly_pages', models.IntegerField(blank=True, choices=[(1, '2'), (2, '5'), (3, '10'), (4, '50')], help_text='How many pages to fetch?', null=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Feedly',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Stream',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=255)),
|
||||
('ident', models.CharField(max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feedlysettings',
|
||||
name='feedly_stream',
|
||||
field=models.ManyToManyField(to='feedler.Stream'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feedlysettings',
|
||||
name='site',
|
||||
field=models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='entry',
|
||||
name='stream',
|
||||
field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='feedler.Stream', verbose_name='Original stream'),
|
||||
),
|
||||
]
|
29
feedler/migrations/0002_feedpage.py
Normal file
29
feedler/migrations/0002_feedpage.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.3 on 2017-07-03 15:21
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import wagtail.wagtailcore.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('feedler', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='FeedPage',
|
||||
fields=[
|
||||
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
|
||||
('intro', wagtail.wagtailcore.fields.RichTextField(blank=True, default='')),
|
||||
('stream', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='feedler.Stream', verbose_name='Filter to stream (optional)')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Feeds',
|
||||
},
|
||||
bases=('wagtailcore.page',),
|
||||
),
|
||||
]
|
0
feedler/migrations/__init__.py
Normal file
0
feedler/migrations/__init__.py
Normal file
2
feedler/models/__init__.py
Normal file
2
feedler/models/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
from .models import *
|
||||
from .admin import *
|
65
feedler/models/admin.py
Normal file
65
feedler/models/admin.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import requests, json, codecs
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from django.db import models
|
||||
from django.db.models.signals import pre_save
|
||||
from django.dispatch import receiver
|
||||
from django.core.mail import send_mail
|
||||
|
||||
from wagtail.contrib.settings.models import BaseSetting, register_setting
|
||||
|
||||
from .models import Entry, Stream
|
||||
import feedler.feedparser as feedparser
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger('feedler')
|
||||
|
||||
# Feedly integration module
|
||||
|
||||
@register_setting
|
||||
class FeedlySettings(BaseSetting):
|
||||
feedly_auth = models.TextField(
|
||||
help_text='Your developer authorization key', blank=True)
|
||||
feedly_pages = models.IntegerField(
|
||||
choices=(
|
||||
(1, '2'),
|
||||
(2, '5'),
|
||||
(3, '10'),
|
||||
(4, '50'),
|
||||
), blank=True, null=True,
|
||||
help_text='How many pages to fetch?'
|
||||
)
|
||||
feedly_stream = models.ManyToManyField(Stream)
|
||||
class Meta:
|
||||
verbose_name = 'Feedly'
|
||||
|
||||
API_BASEURL = 'https://cloud.feedly.com/v3/streams/contents?streamId='
|
||||
|
||||
@receiver(pre_save, sender=FeedlySettings)
|
||||
def handle_save_settings(sender, instance, *args, **kwargs):
|
||||
if instance.feedly_auth:
|
||||
streams = instance.feedly_stream.all()
|
||||
for stream in streams:
|
||||
# Start a request to download the feed
|
||||
logger.info("Processing stream %s" % stream.title)
|
||||
url = API_BASEURL + stream.ident
|
||||
headers = {
|
||||
'Authorization': 'OAuth ' + instance.feedly_auth
|
||||
}
|
||||
contents = requests.get(url, headers=headers).json()
|
||||
if 'errorMessage' in contents:
|
||||
raise PermissionError(contents['errorMessage'])
|
||||
for raw_entry in contents['items']:
|
||||
eid = raw_entry['id']
|
||||
# Create or update data
|
||||
try:
|
||||
entry = Entry.objects.get(entry_id=eid)
|
||||
logger.info("Updating entry '%s'" % eid)
|
||||
except Entry.DoesNotExist:
|
||||
logger.info("Adding entry '%s'" % eid)
|
||||
entry = Entry()
|
||||
entry = feedparser.parse(entry, raw_entry, stream)
|
||||
entry.save()
|
67
feedler/models/models.py
Normal file
67
feedler/models/models.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from django.db import models
|
||||
|
||||
from wagtail.wagtailcore.models import Page, Orderable
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel
|
||||
from wagtail.wagtailcore.fields import RichTextField
|
||||
|
||||
class Stream(models.Model):
|
||||
title = models.CharField(max_length=255)
|
||||
ident = models.CharField(max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
class Entry(models.Model):
|
||||
"""Implementation of the Entry from the feedly API as generic Django model
|
||||
"""
|
||||
raw = models.TextField(blank=True, editable=False)
|
||||
updated = models.DateTimeField(auto_now=True, editable=False)
|
||||
published = models.DateTimeField(auto_now_add=True, editable=False)
|
||||
entry_id = models.CharField(max_length=255, unique=True, blank=True, editable=False)
|
||||
|
||||
title = models.CharField(max_length=255)
|
||||
author = models.CharField(max_length=255, blank=True)
|
||||
link = models.URLField()
|
||||
visual = models.URLField(blank=True)
|
||||
|
||||
content = models.TextField()
|
||||
tags = models.TextField(blank=True)
|
||||
|
||||
stream = models.ForeignKey(Stream,
|
||||
blank=True, on_delete=models.CASCADE,
|
||||
verbose_name='Original stream')
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = 'Entries'
|
||||
|
||||
class FeedPage(Page):
|
||||
intro = RichTextField(default='', blank=True)
|
||||
stream = models.ForeignKey(Stream, on_delete=models.PROTECT,
|
||||
null=True, blank=True, verbose_name='Filter to stream (optional)')
|
||||
|
||||
content_panels = [
|
||||
FieldPanel('title'),
|
||||
FieldPanel('intro'),
|
||||
FieldPanel('stream'),
|
||||
]
|
||||
|
||||
@property
|
||||
def feedentries(self):
|
||||
if self.stream:
|
||||
entries = Entry.objects.filter(stream=self.stream)
|
||||
else:
|
||||
entries = Entry.objects.all()
|
||||
# Order by most recent date first
|
||||
entries = entries.order_by('-published')
|
||||
return entries[:10]
|
||||
|
||||
def get_context(self, request):
|
||||
# Update template context
|
||||
context = super(FeedPage, self).get_context(request)
|
||||
context['feedentries'] = self.feedentries
|
||||
return context
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Feeds"
|
52
feedler/templates/feedler/feed_page.html
Normal file
52
feedler/templates/feedler/feed_page.html
Normal file
|
@ -0,0 +1,52 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static wagtailcore_tags %}
|
||||
|
||||
{% block body_class %}template-{{ self.get_verbose_name|slugify }}{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}Feeds{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<section id="article-index" class="article-index-page">
|
||||
<div class="container">
|
||||
<h2>{{ page.title }}</h2>
|
||||
{% if page.intro %}
|
||||
<p class="lead">{{ page.intro|richtext }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Page body -->
|
||||
<section id="news" class="feedpage-body">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{% for entry in feedentries %}
|
||||
<div class="col-md-4 col-sm-6 col-xs-12">
|
||||
{% 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>
|
||||
<em><small><span>{{ entry.author }}</span></small></em><br><br>
|
||||
{{ entry.content|striptags|truncatewords_html:25 }}
|
||||
</p>
|
||||
</div>
|
||||
<a href="{{ entry.link }}" target="_blank" class="fill"></a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- {{ entry.raw }} -->
|
||||
{% empty %}
|
||||
<!-- No news today -->
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% endblock %}
|
3
feedler/tests.py
Normal file
3
feedler/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
10
feedler/urls.py
Normal file
10
feedler/urls.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from django.conf.urls import include, url
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
|
||||
from .api import api_router
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^api/v2/', api_router.urls),
|
||||
]
|
28
feedler/wagtail_hooks.py
Normal file
28
feedler/wagtail_hooks.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from wagtail.contrib.modeladmin.options import (
|
||||
ModelAdmin, modeladmin_register)
|
||||
|
||||
from .models import Entry, Stream
|
||||
|
||||
class EntryModelAdmin(ModelAdmin):
|
||||
model = Entry
|
||||
menu_icon = 'date'
|
||||
menu_order = 200
|
||||
add_to_settings_menu = False
|
||||
exclude_from_explorer = True
|
||||
list_display = ('published', 'title', 'author', 'tags')
|
||||
list_filter = ('author', 'tags')
|
||||
search_fields = ('title', 'author', 'content', 'tags')
|
||||
|
||||
modeladmin_register(EntryModelAdmin)
|
||||
|
||||
class StreamModelAdmin(ModelAdmin):
|
||||
model = Stream
|
||||
menu_icon = 'date'
|
||||
menu_order = 1000
|
||||
add_to_settings_menu = True
|
||||
exclude_from_explorer = True
|
||||
list_display = ('title', 'ident')
|
||||
|
||||
modeladmin_register(StreamModelAdmin)
|
19
publichealth/home/migrations/0019_auto_20170703_1244.py
Normal file
19
publichealth/home/migrations/0019_auto_20170703_1244.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.3 on 2017-07-03 10:44
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0018_contact_contact_form'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='dataletssettings',
|
||||
options={'verbose_name': 'Get support'},
|
||||
),
|
||||
]
|
|
@ -1,4 +1,4 @@
|
|||
from .forms import *
|
||||
from .models import *
|
||||
from .snippets import *
|
||||
from .settings import *
|
||||
from .admin import *
|
||||
|
|
|
@ -7,6 +7,8 @@ from django.core.mail import send_mail
|
|||
|
||||
from wagtail.contrib.settings.models import BaseSetting, register_setting
|
||||
|
||||
# A simple feedback module built into the site admin
|
||||
|
||||
@register_setting
|
||||
class DataletsSettings(BaseSetting):
|
||||
feedback_question = models.TextField(
|
||||
|
@ -23,7 +25,7 @@ class DataletsSettings(BaseSetting):
|
|||
feedback_comment = models.TextField(
|
||||
help_text='Any general feedback', blank=True)
|
||||
class Meta:
|
||||
verbose_name = 'Datalets'
|
||||
verbose_name = 'Get support'
|
||||
|
||||
@receiver(pre_save, sender=DataletsSettings)
|
||||
def handle_save_settings(sender, instance, *args, **kwargs):
|
|
@ -62,10 +62,15 @@ class ArticleIndexPage(Page):
|
|||
context['subcategories'] = subcategories
|
||||
return context
|
||||
|
||||
parent_page_types = [
|
||||
'home.ArticleIndexPage',
|
||||
'home.HomePage'
|
||||
]
|
||||
subpage_types = [
|
||||
'home.ArticlePage',
|
||||
'home.ArticleIndexPage',
|
||||
'home.ContactForm'
|
||||
'home.ContactForm',
|
||||
'wagtailcore.Page'
|
||||
]
|
||||
class Meta:
|
||||
verbose_name = "Rubrik"
|
||||
|
@ -145,6 +150,10 @@ class ArticlePage(Page):
|
|||
MultiFieldPanel(Page.promote_panels, "Einstellungen"),
|
||||
]
|
||||
|
||||
parent_page_types = [
|
||||
'home.ArticleIndexPage',
|
||||
'home.HomePage'
|
||||
]
|
||||
subpage_types = []
|
||||
class Meta:
|
||||
verbose_name = "Artikel"
|
||||
|
@ -212,7 +221,7 @@ class HomePage(Page):
|
|||
return articles[:4]
|
||||
|
||||
@property
|
||||
def newsfeed(self):
|
||||
def blogentries(self):
|
||||
# Get list of latest news
|
||||
curlang = translation.get_language()
|
||||
if not curlang in ['de', 'fr']: curlang = 'de' # Default language
|
||||
|
@ -221,15 +230,15 @@ class HomePage(Page):
|
|||
entries = EntryPage.objects.live().descendant_of(parent[0])
|
||||
# Order by most recent date first
|
||||
entries = entries.order_by('-date')
|
||||
return entries[:4]
|
||||
return entries[:6]
|
||||
|
||||
def get_context(self, request):
|
||||
# Update template context
|
||||
context = super(HomePage, self).get_context(request)
|
||||
context['featured'] = self.featured
|
||||
context['newsfeed'] = self.newsfeed
|
||||
context['blogentries'] = self.blogentries
|
||||
return context
|
||||
|
||||
parent_page_types = []
|
||||
parent_page_types = ['wagtailcore.Page']
|
||||
class Meta:
|
||||
verbose_name = "Frontpage"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<section id="news">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{% for entry in newsfeed %}
|
||||
{% for entry in blogentries %}
|
||||
<div class="col-md-4 col-sm-6 col-xs-12">
|
||||
<div class="panel panel-default">
|
||||
{% if entry.header_image %}
|
||||
|
@ -15,7 +15,7 @@
|
|||
{% if entry.excerpt %}
|
||||
{{ entry.excerpt|striptags }}
|
||||
{% else %}
|
||||
{{ entry.body|striptags|truncatewords_html:70 }}
|
||||
{{ entry.body|striptags|truncatewords_html:40 }}
|
||||
{% endif %}
|
||||
</p>
|
||||
<a href="{% pageurl entry %}" class="btn btn-default btn-xs">Mehr erfahren</a>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="{{ blog_page.title }}" />
|
||||
<meta name="twitter:description" content="{{ blog_page.description }}" />
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ blog_page.title }}" href="{% feeds_url blog_page %}" />
|
||||
{% endblock social_share %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -56,4 +57,8 @@
|
|||
</div>
|
||||
{% endwith %}
|
||||
</section>
|
||||
|
||||
<a href="{% feeds_url blog_page %}" target="_blank" title="RSS">
|
||||
<i class="fa fa-rss-square"></i> <span>RSS Feed</span>
|
||||
</a>
|
||||
{% endblock content %}
|
||||
|
|
|
@ -39,7 +39,10 @@ def top_menu(context, parent, calling_page=None):
|
|||
menuitem.show_dropdown = has_menu_children(menuitem)
|
||||
menuitem.active = (calling_page.url.startswith(menuitem.url)
|
||||
if calling_page else False)
|
||||
menuitem.title = menuitem.trans_title
|
||||
try:
|
||||
menuitem.title = menuitem.trans_title
|
||||
except AttributeError:
|
||||
pass
|
||||
return {
|
||||
'calling_page': calling_page,
|
||||
'menuitems': menuitems,
|
||||
|
@ -49,7 +52,10 @@ def top_menu(context, parent, calling_page=None):
|
|||
def menuitems_children(parent):
|
||||
menuitems_children = parent.get_children().live().in_menu().specific()
|
||||
for menuitem in menuitems_children:
|
||||
menuitem.title = menuitem.trans_title
|
||||
try:
|
||||
menuitem.title = menuitem.trans_title
|
||||
except AttributeError:
|
||||
pass
|
||||
return menuitems_children
|
||||
|
||||
# Retrieves the children of the top menu items for the drop downs
|
||||
|
|
|
@ -28,6 +28,7 @@ INSTALLED_APPS = [
|
|||
'wagtail.contrib.wagtailsearchpromotions',
|
||||
'wagtail.contrib.wagtailroutablepage',
|
||||
'wagtail.contrib.wagtailsitemaps',
|
||||
'wagtail.contrib.modeladmin',
|
||||
'wagtail.contrib.settings',
|
||||
'wagtail.wagtailforms',
|
||||
'wagtail.wagtailredirects',
|
||||
|
@ -41,11 +42,15 @@ INSTALLED_APPS = [
|
|||
'wagtail.wagtailadmin',
|
||||
'wagtail.wagtailcore',
|
||||
|
||||
'wagtail.api.v2',
|
||||
'rest_framework',
|
||||
|
||||
'modelcluster',
|
||||
'compressor',
|
||||
'taggit',
|
||||
'puput',
|
||||
'anymail',
|
||||
'feedler',
|
||||
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
|
|
|
@ -2,21 +2,13 @@
|
|||
// News overview
|
||||
#news {
|
||||
|
||||
.panel-default {
|
||||
.panel-default, .panel-fulltext {
|
||||
font-size: 90%;
|
||||
padding-top: 75%; /* 1:1 Aspect Ratio */
|
||||
position: relative; /* If you want text inside of it */
|
||||
overflow: hidden;
|
||||
background: lighten($brand-primary, 10%);
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -24,10 +16,7 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
transform: translateY(60%);
|
||||
background-color: rgba($brand-primary, .8);
|
||||
transition: transform .65s;
|
||||
|
||||
h3, p {
|
||||
color: white;
|
||||
}
|
||||
|
@ -58,9 +47,6 @@
|
|||
text-align: center;
|
||||
}
|
||||
}
|
||||
&:hover .panel-body {
|
||||
transform: rotateY(0);
|
||||
}
|
||||
|
||||
// expand link over the thumbnail
|
||||
a.fill {
|
||||
|
@ -72,6 +58,23 @@
|
|||
font-size: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-default {
|
||||
img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.panel-body {
|
||||
transform: translateY(60%);
|
||||
transition: transform .65s;
|
||||
}
|
||||
&:hover .panel-body {
|
||||
transform: rotateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// News detail article
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
$(document).ready(function() {
|
||||
|
||||
// All external links in a new window
|
||||
$('a[href^="http"]').filter(function() {
|
||||
return this.hostname && this.hostname !== location.hostname;
|
||||
}).attr('target', '_blank');
|
||||
|
||||
// Initialise front page carousel component
|
||||
$('.carousel-inner.slick').slick({
|
||||
autoplay: true,
|
||||
autoplaySpeed: '10000',
|
||||
|
@ -12,4 +18,11 @@ $(document).ready(function() {
|
|||
nextArrow: '<span class="arrow right glyphicon glyphicon-chevron-right" aria-hidden="true">Next</span>',
|
||||
});
|
||||
|
||||
// Formatting of live news
|
||||
$('.feedpage-body .panel').each(function() {
|
||||
var hue = Math.floor(Math.random() * 360);
|
||||
var pastel = 'hsl(' + hue + ', 100%, 87.5%)';
|
||||
$(this).css('border-top', '3px solid ' + pastel);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -6,13 +6,16 @@ from django.conf.urls.i18n import i18n_patterns
|
|||
from wagtail.wagtailadmin import urls as wagtailadmin_urls
|
||||
from wagtail.wagtaildocs import urls as wagtaildocs_urls
|
||||
from wagtail.wagtailcore import urls as wagtail_urls
|
||||
|
||||
from puput import urls as puput_urls
|
||||
from feedler import urls as feedler_urls
|
||||
|
||||
from publichealth.search import views as search_views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'', include(puput_urls)),
|
||||
url(r'', include(feedler_urls)),
|
||||
|
||||
url(r'^django-admin/', include(admin.site.urls)),
|
||||
|
||||
url(r'^admin/', include(wagtailadmin_urls)),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# Updated: 30.5.2017
|
||||
|
||||
# Core
|
||||
wagtail==1.10.1
|
||||
Django==1.11.1
|
||||
wagtail==1.11
|
||||
Django==1.11.3
|
||||
|
||||
# Database
|
||||
psycopg2==2.7.1
|
||||
|
@ -19,8 +19,8 @@ django-redis==4.8.0
|
|||
|
||||
# Frontend
|
||||
django-libsass==0.7
|
||||
libsass==0.12.3
|
||||
Pillow==4.1.1
|
||||
libsass==0.13.2
|
||||
Pillow==4.2.0
|
||||
|
||||
# Development tools
|
||||
stellar==0.4.3
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
from puput import PUPUT_APPS
|
||||
from feedler import FEEDLER_APPS
|
||||
|
||||
WAGTAIL_SITE_NAME = 'Public Health'
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
@ -16,6 +17,7 @@ INSTALLED_APPS = (
|
|||
'django.contrib.staticfiles',
|
||||
)
|
||||
INSTALLED_APPS += PUPUT_APPS
|
||||
INSTALLED_APPS += FEEDLER_APPS
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
|
|
Loading…
Reference in a new issue