Skip to content
This repository has been archived by the owner on Feb 9, 2018. It is now read-only.

Commit

Permalink
Implemented atom and rss feeds fix #18
Browse files Browse the repository at this point in the history
  • Loading branch information
rochacbruno committed Jan 9, 2018
1 parent e2f7949 commit 5b625b1
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 9 deletions.
4 changes: 4 additions & 0 deletions quokka/core/content/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ def configure(app):
module = QuokkaModule(__name__)
ext = app.config.get("CONTENT_EXTENSION", "html")

extensions = list(app.config.get('CONTENT_EXTENSION_MAP', {}).keys())
ext_list = ','.join(extensions or ['html', 'htm', 'rss', 'atom'])
ext = f'<any({ext_list}):ext>'

# INDEX|HOME
# handle /
module.add_url_rule('/', view_func=ArticleListView.as_view('index'))
Expand Down
2 changes: 1 addition & 1 deletion quokka/core/content/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def author_avatar(self):
return self.author.profile_page.author_avatar
return self.metadata.get(
'author_avatar',
app.theme_context(
app.theme_context.get(
'AVATAR',
'https://api.adorable.io/avatars/250/quokkacms.png'
)
Expand Down
122 changes: 117 additions & 5 deletions quokka/core/content/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
from flask import current_app as app, render_template, abort
import hashlib
import PyRSS2Gen as pyrss
from datetime import datetime, timedelta
from flask import current_app as app, render_template, abort, request
from flask.views import MethodView

# from werkzeug.contrib.atom import AtomFeed
# The werkzeug AtomFeed escapes all html tags
from quokka.utils.atom import AtomFeed

from .models import make_model, make_paginator, Category, Tag, Author
from quokka.utils.text import slugify_category, normalize_var, slugify
from quokka.utils.text import (
slugify_category, normalize_var, slugify, cdata, make_external_url
)


class BaseView(MethodView):
Expand Down Expand Up @@ -80,16 +90,22 @@ def set_elements_visibility(self, context, content_type):

class ArticleListView(BaseView):

def get(self, category=None, tag=None, author=None, page_number=1):
def get(self, category=None, tag=None, author=None,
page_number=1, ext=None):
context = {}
query = {'published': True}
home_template = app.theme_context.get('HOME_TEMPLATE')
list_categories = app.theme_context.get('LIST_CATEGORIES', [])
index_category = app.theme_context.get('INDEX_CATEGORY')
content_type = 'index'
template = custom_template = 'index.html'
ext = ext or app.config.get('CONTENT_EXTENSION', 'html')
FEED_ALL_ATOM = app.theme_context.get('FEED_ALL_ATOM')
FEED_ALL_RSS = app.theme_context.get('FEED_ALL_RSS')

if category:
FEED_ALL_ATOM = f"{category}/index.atom"
FEED_ALL_RSS = f"{category}/index.rss"
content_type = 'category'
custom_template = f'{content_type}/{normalize_var(category)}.html'
if category != index_category:
Expand All @@ -101,12 +117,16 @@ def get(self, category=None, tag=None, author=None, page_number=1):
else:
content_type = 'index'
elif tag:
FEED_ALL_ATOM = f"tag/{tag}/index.atom"
FEED_ALL_RSS = f"tag/{tag}/index.rss"
content_type = 'tag'
custom_template = f'{content_type}/{normalize_var(tag)}.html'
template = 'tag.html'
# https://github.com/schapman1974/tinymongo/issues/42
query['tags_string'] = {'$regex': f'.*,{tag},.*'}
elif author:
FEED_ALL_ATOM = f"author/{author}/index.atom"
FEED_ALL_RSS = f"author/{author}/index.rss"
content_type = 'author'
custom_template = f'{content_type}/{normalize_var(author)}.html'
template = 'author.html'
Expand Down Expand Up @@ -157,15 +177,107 @@ def get(self, category=None, tag=None, author=None, page_number=1):
'articles_paginator': paginator,
'articles_page': page,
'articles_next_page': page.next_page,
'articles_previous_page': page.previous_page
'articles_previous_page': page.previous_page,
'FEED_ALL_ATOM': FEED_ALL_ATOM,
'FEED_ALL_RSS': FEED_ALL_RSS
}
)

self.set_elements_visibility(context, content_type)
self.set_elements_visibility(context, category)
templates = [f'custom/{custom_template}', template]

return self.render(ext, content_type, templates, **context)

def render(self, ext, content_type, templates, **context):
extension_map = app.config.get('CONTENT_EXTENSION_MAP', {})
method_name = extension_map.get(ext, 'render_template')
return getattr(self, method_name)(content_type, templates, **context)

def render_template(self, content_type, templates, **context):
return render_template(templates, **context)

def render_atom(self, content_type, templates, **context):
feed_name = (
f"{app.theme_context.get('SITENAME')}"
f" | {content_type.title()} | atom feed"
)
if context.get('articles_page'):
contents = context['articles_page'].object_list
else:
contents = context['articles']

feed = AtomFeed(
feed_name,
feed_url=request.url,
url=request.url_root
)
for content in contents:
content = make_model(content)
feed.add(
content.title,
cdata(content.content),
content_type="html",
author=content.author,
url=make_external_url(content.url),
updated=content.modified,
published=content.date
)
return feed.get_response()
# return BaseResponse(self.to_string(), mimetype='application/atom+xml')

def render_rss(self, content_type, templates, **context):

feed_name = description = (
f"{app.theme_context.get('SITENAME')}"
f" | {content_type.title()} | RSS feed"
)

if context.get('articles_page'):
contents = context['articles_page'].object_list
else:
contents = context['articles']

rss = pyrss.RSS2(
title=feed_name,
link=request.url_root,
description=description,
language=app.config.get('RSS_LANGUAGE', 'en-us'),
copyright=app.config.get('RSS_COPYRIGHT', 'All rights reserved.'),
lastBuildDate=datetime.now(),
categories=[str(context.get('tag') or context.get('category'))],
)

# set rss.pubDate to the newest post in the collection
# back 10 years in the past
rss_pubdate = datetime.today() - timedelta(days=365 * 10)

for content in contents:
content = make_model(content)

if content.date > rss_pubdate:
rss_pubdate = content.date

rss.items.append(
pyrss.RSSItem(
title=content.title,
link=make_external_url(content.url),
description=cdata(content.content),
author=str(content.author),
categories=[str(content.tags)],
guid=hashlib.sha1(
content.title.encode('utf-8') +
content.url.encode('utf-8')
).hexdigest(),
pubDate=content.date,
)
)

# set the new published date after iterating the contents
rss.pubDate = rss_pubdate

return rss.to_xml(encoding=app.config.get('RSS_ENCODING', 'utf-8'))


class CategoryListView(BaseView):
def get(self):
Expand Down Expand Up @@ -235,7 +347,7 @@ def get(self):
class DetailView(BaseView):
is_preview = False

def get(self, slug):
def get(self, slug, ext=None):
category, _, item_slug = slug.rpartition('/')
content = app.db.get_with_content(
slug=item_slug,
Expand Down
14 changes: 13 additions & 1 deletion quokka/project_template/quokka.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ THEME:
DEFAULT_DATE_FORMAT: '%a %d %B %Y'


FEED_ALL_RSS: index/index.rss
FEED_ALL_ATOM: index/index.atom

## ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ஜ۩۞۩ஜ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
## ▓▓▓▒▒▒░░░ THEME SPECIFIC VARIABLES ░░░▒▒▒▓▓▓
## ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ஜ۩۞۩ஜ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
Expand Down Expand Up @@ -440,6 +443,7 @@ QUOKKA:
## [ --- EXTENSIONS --- ]
## Use EXTRA_EXTENSIONS for thirty party extensions
CORE_EXTENSIONS:
# - quokka.core.regex_url_support.Regex
- flask_babelex.Babel
- quokka.core.logger.configure
- quokka.core.db.QuokkaDB
Expand Down Expand Up @@ -634,9 +638,17 @@ QUOKKA:
## Media root folder (to store uploaded files)
MEDIA_ROOT: uploads

## content urls will end with .html
## by default content urls will end with .html
CONTENT_EXTENSION: html

## Accepted extension and its render method
CONTENT_EXTENSION_MAP:
html: render_template
htm: render_template
rss: render_rss
atom: render_atom
feed: render_atom

## Default categories to be listed on create form
## all existing categories on database will also be listed
# CATEGORIES:
Expand Down
3 changes: 1 addition & 2 deletions quokka/project_template/themes/malt/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
<meta name="description" content="{{ SITEDESCRIPTION }}"/>
<meta name="keywords" content="{{ SITE_KEYWORDS|join(', ') }}"/>
<meta name="author" content="{{ AUTHOR }}">
<meta name="generator" content="Feito com Materialize, Pelican,
Python e VIM. ">
<meta name="generator" content="Quokka CMS">
<meta name="theme-color" content="{{ MALT_BASE_COLOR }}"/>
<title>{% block title %}{{ SITENAME }}{% endblock %}</title>

Expand Down
12 changes: 12 additions & 0 deletions quokka/utils/text.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from flask import request
from urllib.parse import urljoin
from slugify.main import Slugify

slugify = Slugify()
Expand Down Expand Up @@ -53,3 +55,13 @@ def make_social_link(network, txt):
def make_social_name(txt):
"""from a link like http://foo.com/username returns username"""
return txt.split('/')[-1]


def cdata(data):
if not data:
return ""
return f"<![CDATA[\n{data}\n]]>"


def make_external_url(url):
return urljoin(request.url_root, url)

0 comments on commit 5b625b1

Please sign in to comment.