diff options
Diffstat (limited to 'pyaggr3g470r')
-rw-r--r-- | pyaggr3g470r/static/css/bootstrap.css | 6 | ||||
-rw-r--r-- | pyaggr3g470r/templates/article.html | 15 | ||||
-rw-r--r-- | pyaggr3g470r/templates/home.html | 156 | ||||
-rw-r--r-- | pyaggr3g470r/templates/layout.html | 6 | ||||
-rw-r--r-- | pyaggr3g470r/templates/unread.html | 3 | ||||
-rw-r--r-- | pyaggr3g470r/views.py | 88 |
6 files changed, 172 insertions, 102 deletions
diff --git a/pyaggr3g470r/static/css/bootstrap.css b/pyaggr3g470r/static/css/bootstrap.css index 14cc1f48..0b9217fc 100644 --- a/pyaggr3g470r/static/css/bootstrap.css +++ b/pyaggr3g470r/static/css/bootstrap.css @@ -5829,3 +5829,9 @@ td.visible-print { } } /*# sourceMappingURL=bootstrap.css.map */ +.strict-table td{ + min-width: 70px; +} +.strict-table td.date { + min-width: 190px; +} diff --git a/pyaggr3g470r/templates/article.html b/pyaggr3g470r/templates/article.html index 83e23b34..2dbdb8de 100644 --- a/pyaggr3g470r/templates/article.html +++ b/pyaggr3g470r/templates/article.html @@ -9,10 +9,17 @@ <h2><a href="{{ article.link }}">{{ article.title|safe }}</a></h2> <h3>{{ _('from') }} <a href="/feed/{{ article.source.id }}">{{ article.source.title }}</a></h3> <a href="/delete/{{ article.id }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this article') }}"></i></a> - {% if article.like %} - <a href="/like/{{ article.id }}"><i class="glyphicon glyphicon-star" title="{{ _('One of your favorites') }}"></i></a> - {% else %} - <a href="/like/{{ article.id }}"><i class="glyphicon glyphicon-star-empty" title="{{ _('Click if you like this article') }}"></i></a> + <a href="/like/{{ article.id }}"> + {% if article.like %} + <i class="glyphicon glyphicon-star" title="{{ _('One of your favorites') }}"></i> + {% else %} + <i class="glyphicon glyphicon-star-empty" title="{{ _('Click if you like this article') }}"></i> + {% endif %} + </a> + {% if article.readed %} + <a href="/mark_as/unread/article/{{ article.id }}"><i class="glyphicon glyphicon-unchecked" title="{{ _('Mark this article as unread') }}"></i></a> + {% else %} + <a href="/mark_as/read/article/{{ article.id }}"><i class="glyphicon glyphicon-check" title="{{ _('Mark this article as read') }}"></i></a> {% endif %} <h6>{{ article.date | datetime }}</h6> </div> diff --git a/pyaggr3g470r/templates/home.html b/pyaggr3g470r/templates/home.html index c1663997..96cd83a5 100644 --- a/pyaggr3g470r/templates/home.html +++ b/pyaggr3g470r/templates/home.html @@ -1,70 +1,96 @@ - {% extends "layout.html" %} +{% extends "layout.html" %} {% block content %} -<div class="container-fluid"> - <div classe="row"> - <div id="affix-nav" class="col-md-4 sidebar hidden-xs hidden-sm"> - <ul class="nav sidenav affix affix-top" data-spy="affix" data-offset-top="0" data-offset-bottom="0"> - {% for feed in result|sort(attribute="title")|sort(attribute="nb_unread", reverse=True) %} - <li> - <a href="#{{ feed.id }}"> - {% if feed.nb_unread != 0 %}<span class="badge pull-right">{{ feed.nb_unread }}</span>{% endif %} - {{ feed.title|safe }} - </a> - </li> +<div id="affix-nav" class="col-md-1 sidebar hidden-xs hidden-sm"> + <ul class="nav sidenav affix affix-top" data-spy="affix" data-offset-top="0" data-offset-bottom="0"> + <li><a href="{{ gen_url(feed=0) }}"> + {% if not feed_id %}<b>{% endif %} + {{ _('All feeds') }} + {% if not feed_id %}</b>{% endif %} + </a></li> + {% for fid in unread %} + <li><a href="{{ gen_url(feed=fid) }}"> + {% if feed_id == fid %}<b>{% endif %} + <span class="badge pull-right">{{ unread[fid] }}</span> + {{ feeds[fid]|safe }} + {% if feed_id == fid %}</b>{% endif %} + </a></li> + {% endfor %} + {% for fid, ftitle in feeds.items() if not fid in unread %} + <li><a href="{{ gen_url(feed=fid) }}"> + {% if feed_id == fid %}<b>{% endif %} + {{ ftitle|safe }} + {% if feed_id == fid %}</b>{% endif %} + </a></li> + {% endfor %} + </ul> +</div> +<div class="container col-md-9" style="float: right;"> + <h1>{{ _('Your articles') }} ({{ articles.__len__() }})</h1> + <div> + {% if filter_ == 'all' %}<b>{% endif %} + <a href="{{ gen_url(filter_='all') }}">{{ _('All') }}</a> + {% if filter_ == 'all' %}</b>{% endif %} + | + {% if filter_ == 'read' %}<b>{% endif %} + <a href="{{ gen_url(filter_='read') }}">{{ _('Read') }}</a> + {% if filter_ == 'read' %}</b>{% endif %} + | + {% if filter_ == 'unread' %}<b>{% endif %} + <a href="{{ gen_url(filter_='unread') }}">{{ _('Unread') }}</a> + {% if filter_ == 'unread' %}</b>{% endif %} + - + {% if limit == 10 %}<b>{% endif %} + <a href="{{ gen_url(limit=10) }}">{{ _(10) }}</a> + {% if limit == 10 %}</b>{% endif %} + | + {% if limit == 100 %}<b>{% endif %} + <a href="{{ gen_url(limit=100) }}">{{ _(100) }}</a> + {% if limit == 100 %}</b>{% endif %} + | + {% if limit == 1000 %}<b>{% endif %} + <a href="{{ gen_url(limit=1000) }}">{{ _(1000) }}</a> + {% if limit == 1000 %}</b>{% endif %} + | + {% if limit == 'all' %}<b>{% endif %} + <a href="{{ gen_url(limit='all') }}">{{ _('All') }}</a> + {% if limit == 'all' %}</b>{% endif %} + + </div> + <div class="table-responsive"> + <table class="table table-striped strict-table"> + <thead> + <tr> + <th>{{ _('Feed') }}</th> + <th>{{ _('Article') }}</th> + <th>{{ _('Date') }}</th> + <th></th> + </tr> + </thead> + <tbody> + {% for article in articles %} + <tr data-article="{{ article.id }}" data-feed="{{ article.feed_id }}"> + <td><a href="/article/redirect/{{ article.id}}">{{ article.source.title|safe }}</a></td> + <td><a href="/article/{{ article.id }}">{{ article.title|safe }}</a></td> + <td class="date">{{ article.date|datetime }}</a></td> + <td> + <a href="/delete/{{ article.id }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this article') }}"></i></a> + <a href="/like/{{ article.id }}"> + {% if article.like %} + <i class="glyphicon glyphicon-star" title="{{ _('One of your favorites') }}"></i> + {% else %} + <i class="glyphicon glyphicon-star-empty" title="{{ _('Click if you like this article') }}"></i> + {% endif %} + </a> + {% if article.readed %} + <a href="/mark_as/unread/article/{{ article.id }}"><i class="glyphicon glyphicon-unchecked" title="{{ _('Mark this article as unread') }}"></i></a> + {% else %} + <a href="/mark_as/read/article/{{ article.id }}"><i class="glyphicon glyphicon-check" title="{{ _('Mark this article as read') }}"></i></a> + {% endif %} + </td> + </tr> {% endfor %} - </ul> - </div> - <div class="col-md-8"> - {% if result|count == 0 %} - <h1>{{ _("You don't have any feeds.") }}</h1> - <h1><a href="/create_feed/">{{ _('Add some') }}</a>, {{ _('or') }} <a href="/management/#import">{{ _('upload an OPML file.') }}</a></h1> - {% else %} - {% for feed in result|sort(attribute="title") %} - <div class="top"><a id="{{ feed.id }}"></a></div> - <div class="row"> - <div class="col-md-6 col-md-offset-3"> - <h1>{{ feed.title|safe }} - {% if feed.nb_unread != 0 %} - <a href="/unread/{{ feed.id }}" title="Unread articles"><span class="badge">{{ feed.nb_unread }}</span></a> - {% endif %}</h1> - <a href="/articles/{{ feed.id }}/100"><i class="glyphicon glyphicon-th-list" title="{{ _('More articles') }}"></i></a> - <a href="/feed/{{ feed.id }}"><i class="glyphicon glyphicon-info-sign" title="{{ _('Details') }}"></i></a> - <a href="/edit_feed/{{ feed.id }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this feed') }}"></i></a> - {% if feed.enabled %} - <a href="/fetch/{{ feed.id }}"><i class="glyphicon glyphicon-cloud-download" title="{{ _('Fetch this feed') }}"></i></a> - {% endif %} - {% if feed.nb_unread != 0 %} - <a href="/mark_as_read/{{ feed.id }}"><i class="glyphicon glyphicon-check" title="{{ _('Mark all as read') }}"></i></a> - {% endif %} - </div> - </div> - {% for number in range(0, feed.articles.all()|count-(feed.articles.all()|count % 3), 3) %} - <div class="row"> - {% for n in range(number, number+3) %} - <div class="col-xs-6 col-sm-4 col-md-4"> - {% if feed.articles[n].readed %}<h3>{% else %}<h1>{% endif %} - <a href="/article/{{ feed.articles[n].id }}">{{ feed.articles[n].title|safe }}</a> - {% if feed.articles[n].readed %}</h3>{% else %}</h1>{% endif %} - <h6>{{ feed.articles[n].date | datetime }}</h6> - </div> - {% endfor %} - </div> - {% endfor %} - {% if feed.articles.all()|count % 3 != 0 %} - <div class="row"> - {% for n in range(feed.articles.all()|count-(feed.articles.all()|count % 3), feed.articles.all()|count) %} - <div class="col-xs-6 col-sm-4 col-md-4"> - {% if feed.articles[n].readed %}<h3>{% else %}<h1>{% endif %} - <a href="/article/{{ feed.articles[n].id }}">{{ feed.articles[n].title|safe }}</a> - {% if feed.articles[n].readed %}</h3>{% else %}</h1>{% endif %} - <h6>{{ feed.articles[n].date | datetime }}</h6> - </div> - {% endfor %} - </div> - {% endif %} - {% endfor %} - {% endif %} - </div> + </tbody> + </table> </div> </div><!-- /.container --> {% endblock %} diff --git a/pyaggr3g470r/templates/layout.html b/pyaggr3g470r/templates/layout.html index d1040422..543fc823 100644 --- a/pyaggr3g470r/templates/layout.html +++ b/pyaggr3g470r/templates/layout.html @@ -27,7 +27,7 @@ deployed on Heroku or on a traditional server." /> height: 0; } ul.affix { - position: fixed; + position: fixed; top: 0px; } ul.affix-top { @@ -102,7 +102,7 @@ deployed on Heroku or on a traditional server." /> <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ _('Articles') }} <b class="caret"></b></a> <ul class="dropdown-menu"> <li><a accesskey="r" href="/fetch/">{{ _('Fetch') }}</a></li> - <li><a href="/mark_as_read/">{{ _('Mark all as read') }}</a></li> + <li><a href="/mark_as/read/">{{ _('Mark all as read') }}</a></li> <li role="presentation" class="divider"></li> <li><a href="/create_feed/">{{ _('Add a feed') }}</a></li> </ul> @@ -145,7 +145,7 @@ deployed on Heroku or on a traditional server." /> </div><!-- /.container --> </nav> - <div class="container"> + <div class="container" style="float: right"> {% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} {% for category, message in messages %} diff --git a/pyaggr3g470r/templates/unread.html b/pyaggr3g470r/templates/unread.html index 6c5c5d08..cdfb0dfe 100644 --- a/pyaggr3g470r/templates/unread.html +++ b/pyaggr3g470r/templates/unread.html @@ -16,7 +16,8 @@ <a href="/articles/{{ feed.id }}/100"><i class="glyphicon glyphicon-th-list" title="{{ _('More articles') }}"></i></a> <a href="/feed/{{ feed.id }}"><i class="glyphicon glyphicon-info-sign" title="{{ _('Details') }}"></i></a> <a href="/edit_feed/{{ feed.id }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this feed') }}"></i></a> - <a href="/mark_as_read/{{ feed.id }}"><i class="glyphicon glyphicon-check" title="{{ _('Mark all as read') }}"></i></a> + <a href="/mark_as/read/feed/{{ feed.id }}"><i class="glyphicon glyphicon-check" title="{{ _('Mark all feed as read') }}"></i></a> + <a href="/mark_as/unread/feed/{{ feed.id }}"><i class="glyphicon glyphicon-unchecked" title="{{ _('Mark all feed as unread') }}"></i></a> <h3>{{ feed.articles.all()|length }} {{ _('unread articles') }}.</h3> </div> </div> diff --git a/pyaggr3g470r/views.py b/pyaggr3g470r/views.py index e4668f54..1b3be75e 100644 --- a/pyaggr3g470r/views.py +++ b/pyaggr3g470r/views.py @@ -28,11 +28,15 @@ __license__ = "AGPLv3" import os import datetime -from flask import render_template, request, flash, session, 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, identity_changed, identity_loaded, Permission, RoleNeed, UserNeed +from flask import abort, render_template, request, flash, session, \ + 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, \ + identity_changed, identity_loaded, Permission,\ + RoleNeed, UserNeed from flask.ext.babel import gettext -from sqlalchemy import desc +from sqlalchemy import desc, func from sqlalchemy.exc import IntegrityError from werkzeug import generate_password_hash @@ -209,23 +213,41 @@ def signup(): @app.route('/') @login_required def home(): - """ - The home page lists most recent articles of all feeds. - """ - #user = User.query.filter(User.email == g.user.email).first() - result = [] - for feed in g.user.feeds: - new_feed = Feed() - new_feed.id = feed.id - new_feed.title = feed.title - new_feed.enabled = feed.enabled - new_feed.articles = feed.articles[:9] - #new_feed.articles = Article.query.filter(Article.user_id == g.user.id, - #Article.feed_id == feed.id).order_by(desc("Article.date")).limit(9) - new_feed.nb_unread = Article.query.filter(Article.user_id == g.user.id, Article.feed_id == feed.id, Article.readed == False).count() - result.append(new_feed) - unread_articles = len(Article.query.filter(Article.user_id == g.user.id, Article.readed == False).all()) - return render_template('home.html', result=result, head_title=unread_articles) + feeds = {feed.id: feed.title for feed in g.user.feeds if feed.enabled} + articles = Article.query.filter(Article.feed_id.in_(feeds.keys())) + filter_ = request.args.get('filter_', 'unread') + feed_id = int(request.args.get('feed', 0)) + limit = request.args.get('limit', 1000) + if filter_ != 'all': + articles = articles.filter(Article.readed == (filter_ == 'read')) + if feed_id: + articles = articles.filter(Article.feed_id == feed_id) + + articles = articles.order_by(Article.date.desc()) + if limit != 'all': + limit = int(limit) + articles = articles.limit(limit) + unread = db.session.query(Article.feed_id, func.count(Article.id))\ + .filter(Article.readed == False)\ + .group_by(Article.feed_id).all() + def gen_url(filter_=filter_, limit=limit, feed=feed_id): + return '/?filter_=%s&limit=%s&feed=%d' % (filter_, limit, feed) + return render_template('home.html', gen_url=gen_url, feed_id=feed_id, + filter_=filter_, limit=limit, feeds=feeds, + unread=dict(unread), articles=articles.all()) + + +@app.route('/article/redirect/<int:article_id>', methods=['GET']) +@login_required +def redirect_to_article(article_id): + article = Article.query.filter(Article.id == article_id, + Article.user_id == g.user.id).first() + if article is None: + abort(404) + article.readed = True + db.session.commit() + return redirect(article.link) + @app.route('/fetch/', methods=['GET']) @app.route('/fetch/<int:feed_id>', methods=['GET']) @@ -312,22 +334,30 @@ def article(article_id=None): return redirect(redirect_url()) -@app.route('/mark_as_read/', methods=['GET']) -@app.route('/mark_as_read/<int:feed_id>', methods=['GET']) +@app.route('/mark_as/<string:new_value>', methods=['GET']) +@app.route('/mark_as/<string:new_value>/feed/<int:feed_id>', methods=['GET']) +@app.route('/mark_as/<string:new_value>/article/<int:article_id>', methods=['GET']) @login_required @feed_access_required -def mark_as_read(feed_id=None): +def mark_as(new_value='read', feed_id=None, article_id=None): """ Mark all unreaded articles as read. """ + readed = new_value == 'read' + articles = Article.query.filter(Article.user_id == g.user.id) if feed_id is not None: - Article.query.filter(Article.user_id == g.user.id, Article.feed_id == feed_id, - Article.readed == False).update({"readed": True}) - flash(gettext('Articles marked as read.'), 'info') + articles = articles.filter(Article.feed_id == feed_id) + message = 'Feed marked as %s.' + elif article_id is not None: + articles = articles.filter(Article.id == article_id) + message = 'Article marked as %s.' else: - Article.query.filter(Article.user_id == g.user.id, Article.readed == False).update({"readed": True}) - flash(gettext("All articles marked as read"), 'info') + message = 'All article marked as %s.' + articles.filter(Article.readed == (not readed)).update({"readed": readed}) + flash(gettext(message % new_value), 'info') db.session.commit() + if readed: + return redirect(redirect_url()) return redirect(redirect_url()) @app.route('/like/<int:article_id>', methods=['GET']) |