aboutsummaryrefslogtreecommitdiff
path: root/pyaggr3g470r/views
diff options
context:
space:
mode:
Diffstat (limited to 'pyaggr3g470r/views')
-rw-r--r--pyaggr3g470r/views/__init__.py1
-rw-r--r--pyaggr3g470r/views/api/feed.py4
-rw-r--r--pyaggr3g470r/views/article.py2
-rw-r--r--pyaggr3g470r/views/feed.py21
-rw-r--r--pyaggr3g470r/views/icon.py14
-rw-r--r--pyaggr3g470r/views/views.py26
6 files changed, 34 insertions, 34 deletions
diff --git a/pyaggr3g470r/views/__init__.py b/pyaggr3g470r/views/__init__.py
index 029dcb7d..36d382bd 100644
--- a/pyaggr3g470r/views/__init__.py
+++ b/pyaggr3g470r/views/__init__.py
@@ -3,3 +3,4 @@ from .api import *
from .article import article_bp, articles_bp
from .feed import feed_bp, feeds_bp
+from .icon import icon_bp
diff --git a/pyaggr3g470r/views/api/feed.py b/pyaggr3g470r/views/api/feed.py
index 530f3fef..7d8cdf38 100644
--- a/pyaggr3g470r/views/api/feed.py
+++ b/pyaggr3g470r/views/api/feed.py
@@ -20,7 +20,7 @@ FEED_ATTRS = {'title': {'type': str},
'site_link': {'type': str},
'enabled': {'type': bool, 'default': True},
'etag': {'type': str, 'default': ''},
- 'icon': {'type': str, 'default': ''},
+ 'icon_url': {'type': str, 'default': ''},
'last_modified': {'type': str},
'last_retrieved': {'type': str},
'last_error': {'type': str},
@@ -54,7 +54,7 @@ class FetchableFeedAPI(PyAggAbstractResource):
if g.user.refresh_rate:
args['refresh_rate'] = g.user.refresh_rate
- if args.pop('retreive_all'):
+ if args.pop('retreive_all', False):
contr = self.wider_controller
else:
contr = self.controller
diff --git a/pyaggr3g470r/views/article.py b/pyaggr3g470r/views/article.py
index 6de07ad3..524bf6dd 100644
--- a/pyaggr3g470r/views/article.py
+++ b/pyaggr3g470r/views/article.py
@@ -4,6 +4,7 @@
from flask import Blueprint, g, render_template, redirect
from pyaggr3g470r import controllers, utils
+from pyaggr3g470r.lib.view_utils import etag_match
from pyaggr3g470r.decorators import pyagg_default_decorator
articles_bp = Blueprint('articles', __name__, url_prefix='/articles')
@@ -19,6 +20,7 @@ def redirect_to_article(article_id):
@article_bp.route('/<int:article_id>', methods=['GET'])
@pyagg_default_decorator
+@etag_match
def article(article_id=None):
"""
Presents the content of an article.
diff --git a/pyaggr3g470r/views/feed.py b/pyaggr3g470r/views/feed.py
index fb3ea4c7..afb51903 100644
--- a/pyaggr3g470r/views/feed.py
+++ b/pyaggr3g470r/views/feed.py
@@ -14,6 +14,7 @@ from flask.ext.login import login_required
import conf
from pyaggr3g470r import utils
+from pyaggr3g470r.lib.view_utils import etag_match
from pyaggr3g470r.lib.feed_utils import construct_feed_from
from pyaggr3g470r.forms import AddFeedForm
from pyaggr3g470r.controllers import FeedController, ArticleController
@@ -24,6 +25,7 @@ feed_bp = Blueprint('feed', __name__, url_prefix='/feed')
@feeds_bp.route('/', methods=['GET'])
@login_required
+@etag_match
def feeds():
"Lists the subscribed feeds in a table."
art_contr = ArticleController(g.user.id)
@@ -35,6 +37,7 @@ def feeds():
@feed_bp.route('/<int:feed_id>', methods=['GET'])
@login_required
+@etag_match
def feed(feed_id=None):
"Presents detailed information about a feed."
feed = FeedController(g.user.id).get(id=feed_id)
@@ -138,6 +141,7 @@ def update(action, feed_id=None):
@feed_bp.route('/create', methods=['GET'])
@feed_bp.route('/edit/<int:feed_id>', methods=['GET'])
@login_required
+@etag_match
def form(feed_id=None):
action = gettext("Add a feed")
head_titles = [action]
@@ -196,20 +200,3 @@ def process_form(feed_id=None):
flash(gettext("Downloading articles for the new feed..."), 'info')
return redirect(url_for('feed.form', feed_id=new_feed.id))
-
-
-@feed_bp.route('/icon/<int:feed_id>', methods=['GET'])
-@login_required
-def icon(feed_id):
- icon = FeedController(None if g.user.is_admin() else g.user.id)\
- .get(id=feed_id).icon
- etag = md5(icon.encode('utf8')).hexdigest()
- headers = {'Cache-Control': 'max-age=86400', 'etag': etag}
- if request.headers.get('if-none-match') == etag:
- return Response(status=304, headers=headers)
- if '\n' in icon:
- content_type, *_, icon = icon.split()
- headers['content-type'] = content_type
- else:
- headers['content-type'] = 'application/image'
- return Response(base64.b64decode(icon), headers=headers)
diff --git a/pyaggr3g470r/views/icon.py b/pyaggr3g470r/views/icon.py
new file mode 100644
index 00000000..2f51304a
--- /dev/null
+++ b/pyaggr3g470r/views/icon.py
@@ -0,0 +1,14 @@
+import base64
+from flask import Blueprint, Response, request
+from pyaggr3g470r.controllers import IconController
+from pyaggr3g470r.lib.view_utils import etag_match
+
+icon_bp = Blueprint('icon', __name__, url_prefix='/icon')
+
+@icon_bp.route('/', methods=['GET'])
+@etag_match
+def icon():
+ icon = IconController().get(url=request.args['url'])
+ headers = {'Cache-Control': 'max-age=86400',
+ 'Content-Type': icon.mimetype}
+ return Response(base64.b64decode(icon.content), headers=headers)
diff --git a/pyaggr3g470r/views/views.py b/pyaggr3g470r/views/views.py
index 0071f887..b649d5c6 100644
--- a/pyaggr3g470r/views/views.py
+++ b/pyaggr3g470r/views/views.py
@@ -36,7 +36,7 @@ from collections import OrderedDict
from bootstrap import application as app, db
from flask import render_template, request, flash, session, \
- url_for, redirect, g, current_app, make_response, Response
+ url_for, redirect, g, current_app, make_response
from flask.ext.login import LoginManager, login_user, logout_user, \
login_required, current_user, AnonymousUserMixin
from flask.ext.principal import Principal, Identity, AnonymousIdentity, \
@@ -48,8 +48,8 @@ from sqlalchemy.exc import IntegrityError
from werkzeug import generate_password_hash
import conf
-from pyaggr3g470r.lib.utils import to_hash
from pyaggr3g470r import utils, notifications, export
+from pyaggr3g470r.lib.view_utils import etag_match
from pyaggr3g470r.models import User, Feed, Article, Role
from pyaggr3g470r.decorators import feed_access_required
from pyaggr3g470r.forms import SignupForm, SigninForm, InformationMessageForm,\
@@ -229,6 +229,7 @@ def signup():
return render_template('signup.html', form=form)
+@etag_match
def render_home(filters=None, head_titles=None,
page_to_render='home', **kwargs):
if filters is None:
@@ -292,19 +293,12 @@ def render_home(filters=None, head_titles=None,
and filter_ != 'all' and not articles:
return redirect(gen_url(filter_='all'))
- etag = to_hash("".join([str(filters[key]) for key in sorted(filters)])
- + "".join([str(art.id) for art in articles]))
- if request.headers.get('if-none-match') == etag:
- return Response(status=304, headers={'etag': etag,
- 'Cache-Control': 'pragma: no-cache'})
- response = make_response(render_template('home.html', gen_url=gen_url,
- feed_id=feed_id, page_to_render=page_to_render,
- filter_=filter_, limit=limit, feeds=feeds,
- unread=arti_contr.count_by_feed(readed=False),
- articles=articles, in_error=in_error,
- head_titles=head_titles, sort_=sort_, **kwargs))
- response.headers['etag'] = etag
- return response
+ return render_template('home.html', gen_url=gen_url,
+ feed_id=feed_id, page_to_render=page_to_render,
+ filter_=filter_, limit=limit, feeds=feeds,
+ unread=arti_contr.count_by_feed(readed=False),
+ articles=articles, in_error=in_error,
+ head_titles=head_titles, sort_=sort_, **kwargs)
@app.route('/')
@@ -362,7 +356,9 @@ def fetch(feed_id=None):
"for administrator, on the Heroku platform."), "info")
return redirect(redirect_url())
+
@app.route('/about', methods=['GET'])
+@etag_match
def about():
"""
'About' page.
bgstack15