Skip to content

Commit

Permalink
Merge branch 'main' into krmax44/nix
Browse files Browse the repository at this point in the history
  • Loading branch information
krmax44 authored Aug 21, 2023
2 parents 2ad6ed5 + 1c758d2 commit e6ff6da
Show file tree
Hide file tree
Showing 236 changed files with 5,291 additions and 3,125 deletions.
61 changes: 60 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1 +1,60 @@
module.exports = require('froide/.eslintrc')
module.exports = {
parserOptions: {
sourceType: 'module',
parser: '@typescript-eslint/parser',
project: true,
extraFileExtensions: ['.vue']
},
parser: 'vue-eslint-parser',
extends: [
'eslint:recommended',
'plugin:vue/vue3-essential',
'plugin:vue/vue3-strongly-recommended',
'prettier'
],
plugins: ['@typescript-eslint', 'prettier', 'html'],
ignorePatterns: ['node_modules/**', '**/static/**'],
rules: {
indent: 'off',
'prettier/prettier': 'error',
'space-before-function-paren': [
'error',
{
anonymous: 'always',
named: 'never',
asyncArrow: 'always'
}
],
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'error'
},
overrides: [
{
files: ['*.ts', '*.tsx'],
rules: {
// TODO: remove these exceptions
'@typescript-eslint/strict-boolean-expressions': 'warn',
'@typescript-eslint/prefer-nullish-coalescing': 'warn',

// compatibility with prettier
'@typescript-eslint/space-before-function-paren': [
'error',
{
anonymous: 'always',
named: 'never',
asyncArrow: 'always'
}
],
'@typescript-eslint/member-delimiter-style': [
'error',
{
multiline: {
delimiter: 'none'
},
singleline: { delimiter: 'semi', requireLast: false }
}
]
}
}
]
}
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
DATABASE_URL: postgis://postgres:postgres@localhost/fragdenstaat_de
services:
postgres:
image: postgis/postgis:12-3.0
image: postgis/postgis:14-3.3
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
Expand Down
5 changes: 3 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
repos:
- repo: https://github.com/pycqa/isort
rev: 5.10.1
rev: 5.11.5
hooks:
- id: isort
args: ['--profile', 'black', '--filter-files']
- repo: https://github.com/psf/black
rev: '22.6.0' # Replace by any tag/version: https://github.com/psf/black/tags
rev: '23.3.0' # Replace by any tag/version: https://github.com/psf/black/tags
hooks:
- id: black
language_version: python3 # Should be a command that runs python3.6+
Expand All @@ -32,3 +32,4 @@ repos:
- prettier
- eslint-plugin-prettier
- eslint-config-standard
exclude: .eslintrc.js
13 changes: 12 additions & 1 deletion fragdenstaat_de/fds_blog/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from froide.helper.widgets import TagAutocompleteWidget

from .documents import index_article
from .models import Article, ArticleTag, Author, Category, TaggedArticle
from .models import Article, ArticleTag, Author, Category, Publication, TaggedArticle


class RelatedPublishedFilter(admin.SimpleListFilter):
Expand Down Expand Up @@ -172,6 +172,16 @@ class ArticleAdmin(SortableAdminBase, PlaceholderAdminMixin, admin.ModelAdmin):
"classes": ("collapse", "collapse-closed"),
},
),
(
_("Audio"),
{
"fields": (
"audio",
"audio_duration",
),
"classes": ("collapse", "collapse-closed"),
},
),
(
_("Advanced"),
{
Expand Down Expand Up @@ -380,3 +390,4 @@ class TaggedArticleAdmin(admin.ModelAdmin):
admin.site.register(Category, CategoryAdmin)
admin.site.register(ArticleTag, ArticleTagAdmin)
admin.site.register(TaggedArticle, TaggedArticleAdmin)
admin.site.register(Publication)
1 change: 1 addition & 0 deletions fragdenstaat_de/fds_blog/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def add_search(request):
"title": _("Investigations"),
"name": "blog",
"url": reverse("blog:article-search"),
"order": 6,
}
except NoReverseMatch:
return
155 changes: 141 additions & 14 deletions fragdenstaat_de/fds_blog/feeds.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,51 @@
from xml.sax.saxutils import escape

from django.conf import settings
from django.contrib.syndication.views import Feed
from django.core.cache import cache
from django.urls import reverse
from django.utils.feedgenerator import Enclosure, Rss201rev2Feed
from django.utils.safestring import SafeString, mark_safe
from django.utils.xmlutils import SimplerXMLGenerator

import bleach

from froide.helper.feed_utils import clean_feed_output
from froide.helper.text_utils import convert_html_to_text

from .models import Article
from .models import Article, Publication


class CDataSimplerXMLGenerator(SimplerXMLGenerator):
def characters(self, content):
if content:
self._finish_pending_start_element()
if isinstance(content, SafeString):
self._write("<![CDATA[{}]]>".format(content))
else:
if not isinstance(content, str):
content = str(content, self._encoding)
self._write(escape(content))


class CDataRss201rev2Feed(Rss201rev2Feed):
def write(self, outfile, encoding):
# Overwrite Generator, keep rest the same
handler = CDataSimplerXMLGenerator(outfile, encoding, short_empty_elements=True)
handler.startDocument()
handler.startElement("rss", self.rss_attributes())
handler.startElement("channel", self.root_attributes())
self.add_root_elements(handler)
self.write_items(handler)
self.endChannelElement(handler)
handler.endElement("rss")


class BaseFeed(Feed):
protocol = settings.META_SITE_PROTOCOL
limit = 20
cache_namespace = "feed"
feed_type = CDataRss201rev2Feed

def __call__(self, request, *args, **kwargs):
cache_key = self.get_cache_key(*args, **kwargs)
Expand All @@ -32,7 +65,14 @@ def get_cache_key(self, *args, **kwargs):
"/".join(["%s,%s" % (key, val) for key, val in kwargs.items()]),
)

def get_object(self, request):
# TODO: get the right one from request.app_name
publication = Publication.objects.all().first()
return publication

def title(self, obj=None):
if obj:
return obj.title
return settings.SITE_NAME

@property
Expand All @@ -42,6 +82,14 @@ def site_url(self):
"""
return settings.SITE_URL

def link(self):
return self.site_url

def description(self, obj=None):
if obj:
return obj.description
return ""

def item_pubdate(self, item):
"""
Publication date of an entry.
Expand All @@ -56,22 +104,18 @@ def get_queryset(self):

@clean_feed_output
def item_title(self, item):
if hasattr(item, "kicker") and item.kicker:
if item.kicker:
return "{}: {}".format(item.kicker, item.title)
return item.title

@clean_feed_output
def item_description(self, obj):
return obj.get_full_html_content()
def item_description(self, item):
return item.get_full_html_content()


class LatestArticlesFeed(BaseFeed):
feed_copyright = settings.SITE_NAME

def get_object(self, request):
self.request = request
return request

def item_author_email(self, item):
"""
Return the first author's email.
Expand All @@ -92,16 +136,99 @@ def item_link(self, item):
def feed_url(self):
return self.site_url + reverse("blog:article-latest-feed")

def link(self):
"""
URL of latest entries.
"""
return self.site_url + reverse("blog:article-latest")


class LatestArticlesTeaserFeed(LatestArticlesFeed):
cache_namespace = "feed-teaser"

@clean_feed_output
def item_description(self, obj):
return convert_html_to_text(obj.description)

def feed_url(self):
return self.site_url + reverse("blog:article-latest-feed-teaser")


class PodcastFeed(CDataRss201rev2Feed):
def __init__(self, *args, **kwargs):
extra_fields = ("author", "image")
self.meta = {}
for field in extra_fields:
self.meta[field] = kwargs.pop(field, "")
super().__init__(*args, **kwargs)

def rss_attributes(self):
attrs = super().rss_attributes()
attrs["xmlns:itunes"] = "http://www.itunes.com/dtds/podcast-1.0.dtd"
attrs["xmlns:content"] = "http://purl.org/rss/1.0/modules/content/"
return attrs

def add_root_elements(self, handler):
super().add_root_elements(handler)
if self.meta["image"]:
handler.addQuickElement(
"itunes:image", None, attrs={"href": self.meta["image"]}
)
if self.meta["author"]:
handler.addQuickElement("itunes:author", self.meta["author"])
handler.addQuickElement("itunes:explicit", "false")

# iTunes Category
handler.startElement("itunes:category", {"text": "News"})
handler.addQuickElement("itunes:category", None, {"text": "Politics"})
handler.endElement("itunes:category")

# iTunes Owner
handler.startElement("itunes:owner", {})
handler.addQuickElement("itunes:name", settings.SITE_NAME)
handler.addQuickElement("itunes:email", settings.SITE_EMAIL)
handler.endElement("itunes:owner")

def add_item_elements(self, handler, item):
"""Adds new elements to each item in the feed"""
super().add_item_elements(handler, item)

# iTunes Elements
handler.addQuickElement("itunes:explicit", "false")
handler.addQuickElement("itunes:author", item["author_name"])
handler.addQuickElement("itunes:duration", item["audio_duration"])


class LatestAudioFeed(LatestArticlesFeed):
feed_type = PodcastFeed
cache_namespace = "feed-audio"

def feed_url(self):
return self.site_url + reverse("blog:article-latest-feed-audio")

def items(self):
"""
Items are published entries.
"""
queryset = self.get_queryset().filter(audio__isnull=False)
return queryset[: self.limit]

@clean_feed_output
def item_description(self, item):
content = item.get_full_html_content()
content = bleach.clean(content, strip=True, tags=["p", "ol", "ul", "li", "a"])
return mark_safe(content)

def item_enclosures(self, item):
return [Enclosure(item.audio.url, str(item.audio.size), item.audio.mime_type)]

def feed_extra_kwargs(self, obj):
if obj:
return {
"author": obj.author,
"image": obj.image.url if obj.image else None,
}
return {
"author": settings.SITE_NAME,
"image": None,
}

def item_extra_kwargs(self, item):
return {
# "image": item.image.url if item.image else None,
"audio_duration": str(item.audio_duration),
}
6 changes: 3 additions & 3 deletions fragdenstaat_de/fds_blog/filters.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from django import forms
from django.utils.translation import gettext_lazy as _

import django_filters

from froide.helper.search.filters import BaseSearchFilterSet
from froide.helper.widgets import BootstrapSelect

from .models import Author, Category

Expand All @@ -14,14 +14,14 @@ class ArticleFilterset(BaseSearchFilterSet):
category = django_filters.ModelChoiceFilter(
queryset=Category.objects.all(),
empty_label=_("all categories"),
widget=forms.Select(attrs={"label": _("category"), "class": "form-control"}),
widget=BootstrapSelect(attrs={"label": _("category")}),
method="filter_category",
)

author = django_filters.ModelChoiceFilter(
queryset=Author.objects.all(),
empty_label=_("all authors"),
widget=forms.Select(attrs={"label": _("author"), "class": "form-control"}),
widget=BootstrapSelect(attrs={"label": _("author")}),
method="filter_author",
)

Expand Down
Loading

0 comments on commit e6ff6da

Please sign in to comment.