aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstrap.py9
-rw-r--r--conf.py3
-rw-r--r--pyaggr3g470r/controllers/abstract.py9
-rw-r--r--pyaggr3g470r/controllers/article.py9
-rw-r--r--pyaggr3g470r/static/css/bootstrap.css1
-rw-r--r--pyaggr3g470r/templates/about.html2
-rw-r--r--pyaggr3g470r/templates/admin/user.html6
-rw-r--r--pyaggr3g470r/templates/favorites.html48
-rw-r--r--pyaggr3g470r/templates/feed.html5
-rw-r--r--pyaggr3g470r/templates/feeds.html8
-rw-r--r--pyaggr3g470r/templates/home.html30
-rw-r--r--pyaggr3g470r/templates/layout.html73
-rw-r--r--pyaggr3g470r/templates/management.html2
-rw-r--r--pyaggr3g470r/templates/unread.html51
-rw-r--r--pyaggr3g470r/views/feed.py150
-rw-r--r--pyaggr3g470r/views/views.py186
-rwxr-xr-xrunserver.py7
17 files changed, 283 insertions, 316 deletions
diff --git a/bootstrap.py b/bootstrap.py
index 3b76d0ac..9c2ce049 100644
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -6,6 +6,7 @@
import os
import conf
import logging
+from urllib.parse import urlsplit
def set_logging(log_path, log_level=logging.INFO,
log_format='%(asctime)s %(levelname)s %(message)s'):
@@ -22,8 +23,12 @@ from flask.ext.sqlalchemy import SQLAlchemy
# Create Flask application
application = Flask('pyaggr3g470r')
application.debug = conf.WEBSERVER_DEBUG
-set_logging(conf.LOG_PATH, log_level=logging.DEBUG if conf.WEBSERVER_DEBUG
- else logging.INFO)
+scheme, domain, _, _, _ = urlsplit(conf.PLATFORM_URL)
+application.config['SERVER_NAME'] = domain
+application.config['PREFERRED_URL_SCHEME'] = scheme
+
+set_logging(conf.LOG_PATH,
+ log_level=logging.DEBUG if conf.WEBSERVER_DEBUG else logging.INFO)
# Create dummy secrey key so we can use sessions
application.config['SECRET_KEY'] = getattr(conf, 'WEBSERVER_SECRET', None)
diff --git a/conf.py b/conf.py
index 0d11cae7..3673850a 100644
--- a/conf.py
+++ b/conf.py
@@ -66,7 +66,8 @@ if not ON_HEROKU:
WEBZINE_ROOT = PATH + "/pyaggr3g470r/var/export/"
else:
- PLATFORM_URL = os.environ.get('PLATFORM_URL', 'https://pyaggr3g470r.herokuapp.com/')
+ PLATFORM_URL = os.environ.get('PLATFORM_URL',
+ 'https://pyaggr3g470r.herokuapp.com/')
ADMIN_EMAIL = os.environ.get('ADMIN_EMAIL', '')
RECAPTCHA_PUBLIC_KEY = os.environ.get('RECAPTCHA_PUBLIC_KEY', '')
RECAPTCHA_PRIVATE_KEY = os.environ.get('RECAPTCHA_PRIVATE_KEY', '')
diff --git a/pyaggr3g470r/controllers/abstract.py b/pyaggr3g470r/controllers/abstract.py
index f1173817..8f0a8e3f 100644
--- a/pyaggr3g470r/controllers/abstract.py
+++ b/pyaggr3g470r/controllers/abstract.py
@@ -56,8 +56,8 @@ class AbstractController(object):
if not obj:
raise NotFound({'message': 'No %r (%r)'
% (self._db_cls.__class__.__name__, filters)})
- if self.user_id is not None \
- and getattr(obj, self._user_id_key) != self.user_id:
+
+ if not self._has_right_on(obj):
raise Forbidden({'message': 'No authorized to access %r (%r)'
% (self._db_cls.__class__.__name__, filters)})
return obj
@@ -84,3 +84,8 @@ class AbstractController(object):
db.session.delete(obj)
db.session.commit()
return obj
+
+ def _has_right_on(self, obj):
+ # user_id == None is like being admin
+ return self.user_id is None \
+ or getattr(obj, self._user_id_key, None) == self.user_id
diff --git a/pyaggr3g470r/controllers/article.py b/pyaggr3g470r/controllers/article.py
index 46ca0988..0ec53a2f 100644
--- a/pyaggr3g470r/controllers/article.py
+++ b/pyaggr3g470r/controllers/article.py
@@ -1,3 +1,6 @@
+from sqlalchemy import func
+
+from bootstrap import db
import conf
from .abstract import AbstractController
from pyaggr3g470r.models import Article
@@ -25,3 +28,9 @@ class ArticleController(AbstractController):
if self.read(**id_).first():
continue
yield id_
+
+ def get_unread(self):
+ return dict(db.session.query(Article.feed_id, func.count(Article.id))
+ .filter(Article.readed == False,
+ Article.user_id == self.user_id)
+ .group_by(Article.feed_id).all())
diff --git a/pyaggr3g470r/static/css/bootstrap.css b/pyaggr3g470r/static/css/bootstrap.css
index fb15e3d6..686809a5 100644
--- a/pyaggr3g470r/static/css/bootstrap.css
+++ b/pyaggr3g470r/static/css/bootstrap.css
@@ -6582,3 +6582,4 @@ button.close {
}
}
/*# sourceMappingURL=bootstrap.css.map */
+.glyphicon.delete, .glyphicon.like, .glyphicon.readed { cursor: pointer; }
diff --git a/pyaggr3g470r/templates/about.html b/pyaggr3g470r/templates/about.html
index 08b80fbb..901b3b35 100644
--- a/pyaggr3g470r/templates/about.html
+++ b/pyaggr3g470r/templates/about.html
@@ -17,7 +17,7 @@
<h1>{{ _('Help') }}</h1>
<p>{{ _('If you have any problem, <a href="http://wiki.cedricbonhomme.org/contact">contact</a> the administrator.') }}</p>
<p>{{ _('The documentation of the RESTful API is <a href="https://pyaggr3g470r.readthedocs.org/en/latest/web-services.html">here</a>.') }}</p>
- <p>{{ _('You can subscribe to new feeds with a bookmarklet. Drag <a href="%(bookmarklet)s">this link</a> to your browser bookmarks.', bookmarklet='javascript:window.location="https://pyaggr3g470r.herokuapp.com/bookmarklet?url="+encodeURIComponent(document.location)') }}</p>
+ <p>{{ _('You can subscribe to new feeds with a bookmarklet. Drag <a href="%(bookmarklet)s">this link</a> to your browser bookmarks.', bookmarklet='javascript:window.location="%s?url="+encodeURIComponent(document.location)' % url_for('feed.bookmarklet', _external=True)) }}</p>
</div>
<div class="well">
<h1>{{ _('Donation') }}</h1>
diff --git a/pyaggr3g470r/templates/admin/user.html b/pyaggr3g470r/templates/admin/user.html
index f20d53dd..317fef49 100644
--- a/pyaggr3g470r/templates/admin/user.html
+++ b/pyaggr3g470r/templates/admin/user.html
@@ -39,9 +39,9 @@
<td>{{ feed.site_link }}</td>
<td>{{ feed.articles.all()|count }}</td>
<td>
- <a href="/feed/{{ feed.id }}"><i class="glyphicon glyphicon-th-list" title="{{ _('Feed') }}"></i></a>
- <a href="/edit_feed/{{ feed.id }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this feed') }}"></i></a>
- <a href="/delete_feed/{{ feed.id }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a>
+ <a href="{{ url_for("feed.feed", feed_id=feed.id) }}"><i class="glyphicon glyphicon-th-list" title="{{ _('Feed') }}"></i></a>
+ <a href="{{ url_for("feed.form", feed_id=feed.id) }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this feed') }}"></i></a>
+ <a href="{{ url_for("feed.delete", feed_id=feed.id) }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a>
</td>
{% endfor %}
</tbody>
diff --git a/pyaggr3g470r/templates/favorites.html b/pyaggr3g470r/templates/favorites.html
deleted file mode 100644
index 210f7343..00000000
--- a/pyaggr3g470r/templates/favorites.html
+++ /dev/null
@@ -1,48 +0,0 @@
- {% extends "layout.html" %}
-{% block content %}
-<div class="container">
- {% if feeds|count == 0 %}
- <div class="page-header">
- <h1>{{ _('No favorites') }}</h1>
- </div>
- {% else %}
- <div class="page-header">
- <h1>{{ _('Favorites articles') }} <small>{{ nb_favorites }}</small></h1>
- </div>
- {% for feed in feeds|sort(attribute="title") %}
- <div class="row">
- <div class="col-md-6 col-md-offset-3">
- <h1>{{ feed.title|safe }}</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>
- </div>
- </div>
- {% for number in range(0, feed.articles|length-(feed.articles|length % 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|length % 3 != 0 %}
- <div class="row">
- {% for n in range(feed.articles|length-(feed.articles|length % 3), feed.articles|length) %}
- <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><!-- /.container -->
-{% endblock %}
diff --git a/pyaggr3g470r/templates/feed.html b/pyaggr3g470r/templates/feed.html
index 268cbf7d..c888e04f 100644
--- a/pyaggr3g470r/templates/feed.html
+++ b/pyaggr3g470r/templates/feed.html
@@ -4,8 +4,8 @@
<div class="well">
<h2>{{ feed.title }}</h2>
{% if feed.description %} <p>{{ feed.description }}</p> {% endif %}
- <a href="/delete_feed/{{ feed.id }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a>
- <a href="/edit_feed/{{ feed.id }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this feed') }}"></i></a>
+ <a href="{{ url_for("feed.delete", feed_id=feed.id) }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a>
+ <a href="{{ url_for("feed.form", feed_id=feed.id) }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this feed') }}"></i></a>
</div>
<div class="well">
<p>
@@ -23,6 +23,7 @@
{% if feed.error_count > 2 %}
<b>{{ _("That feed has encountered too much consecutive errors and won't be retrieved anymore.") }}</b><br />
+ {{ _("You can click <a href='%(reset_error_url)s'>here</a> to reset the error count and reactivate the feed.", reset_error_url=url_for("feed.reset_errors", feed_id=feed.id)) }}
{% elif feed.error_count > 0 %}
{{ _("The download of this feed has encountered some problems. However its error counter will be reinitialized at the next successful retrieving.") }}<br />
{% endif %}
diff --git a/pyaggr3g470r/templates/feeds.html b/pyaggr3g470r/templates/feeds.html
index 460ae4a2..9c57bf42 100644
--- a/pyaggr3g470r/templates/feeds.html
+++ b/pyaggr3g470r/templates/feeds.html
@@ -1,7 +1,7 @@
{% extends "layout.html" %}
{% block content %}
<div class="container">
- <h1>{{ _('You are subscribed to') }} {{ feeds.count() }} {{ _('feeds') }} &middot; {{ _('Add a') }} <a href="/create_feed">{{ _('feed') }}</a></h1>
+ <h1>{{ _('You are subscribed to') }} {{ feeds.count() }} {{ _('feeds') }} &middot; {{ _('Add a') }} <a href="{{ url_for("feed.form") }}">{{ _('feed') }}</a></h1>
<div class="table-responsive">
<table class="table table-striped">
<thead>
@@ -25,14 +25,14 @@
<i class="glyphicon glyphicon-eye-close" title="{{ _('Feed disabled') }}"></i>
{% endif %}
</td>
- <td><a href="/feed/{{ feed.id }}" {% if feed.description %}title="{{ feed.description }}"{% endif %}>{{ feed.title }}</a></td>
+ <td><a href="{{ url_for("feed.feed", feed_id=feed.id) }}" {% if feed.description %}title="{{ feed.description }}"{% endif %}>{{ feed.title }}</a></td>
<td><a href="{{ feed.site_link }}">{{ feed.site_link }}</a></td>
<td>{{ feed.articles.count() }}</td>
<td>
<a href="/articles/{{ feed.id }}/100"><i class="glyphicon glyphicon-th-list" title="{{ _('Articles') }}"></i></a>
- <a href="/edit_feed/{{ feed.id }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this feed') }}"></i></a>
+ <a href="{{ url_for("feed.form", feed_id=feed.id) }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this feed') }}"></i></a>
<a href="/duplicates/{{ feed.id }}"><i class="glyphicon glyphicon-book" title="{{ _('Duplicate articles') }}"></i></a>
- <a href="/delete_feed/{{ feed.id }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a>
+ <a href="{{ url_for("feed.delete", feed_id=feed.id) }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a>
</td>
</tr>
{% endfor %}
diff --git a/pyaggr3g470r/templates/home.html b/pyaggr3g470r/templates/home.html
index c20cacfa..e055b3e0 100644
--- a/pyaggr3g470r/templates/home.html
+++ b/pyaggr3g470r/templates/home.html
@@ -3,7 +3,7 @@
{% if feeds|count == 0 %}
<div class="col-md-4 col-md-offset-4">
<h1>{{ _("You don't have any feeds.") }}</h1>
- <h1><a href="/create_feed">{{ _('Add some') }}</a>, {{ _('or') }} <a href="/management">{{ _('upload an OPML file.') }}</a></h1>
+ <h1><a href="{{ url_for("feed.form") }}">{{ _('Add some') }}</a>, {{ _('or') }} <a href="/management">{{ _('upload an OPML file.') }}</a></h1>
</div>
{% else %}
<div id="affix-nav" class="col-md-3 sidebar hidden-xs hidden-sm">
@@ -26,10 +26,10 @@
<li class="feed-commands"><span>
<a href="/feed/{{ fid }}"><i class="glyphicon glyphicon-info-sign" title="{{ _('Details') }}"></i></a>
<a href="/articles/{{ fid }}/100"><i class="glyphicon glyphicon-th-list" title="{{ _('Articles') }}"></i></a>
- <a href="/edit_feed/{{ fid }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this feed') }}"></i></a>
- <a href="/delete_feed/{{ fid }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a>
- <a href="/mark_as/read/feed/{{ fid }}"><i class="glyphicon glyphicon-check" title="{{ _('Mark this feed as read') }}"></i></a>
- <a href="/mark_as/unread/feed/{{ fid }}"><i class="glyphicon glyphicon-unchecked" title="{{ _('Mark this feed as unread') }}"></i></a>
+ <a href="{{ url_for("feed.form", feed_id=fid) }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this feed') }}"></i></a>
+ <a href="{{ url_for("feed.delete", feed_id=fid) }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a>
+ <a href="{{ url_for("feed.update", feed_id=fid, action="read") }}"><i class="glyphicon glyphicon-check" title="{{ _('Mark this feed as read') }}"></i></a>
+ <a href="{{ url_for("feed.update", feed_id=fid, action="unread") }}"><i class="glyphicon glyphicon-unchecked" title="{{ _('Mark this feed as unread') }}"></i></a>
</span></li>
{% endfor %}
{% for fid, ftitle in feeds|dictsort(case_sensitive=False, by='value') if not fid in unread %}
@@ -42,12 +42,12 @@
{% if feed_id == fid %}</b>{% endif %}
</a></li>
<li class="feed-commands"><span>
- <a href="/feed/{{ fid }}"><i class="glyphicon glyphicon-info-sign" title="{{ _('Details') }}"></i></a>
+ <a href="{{ url_for("feed.feed", feed_id=fid) }}"><i class="glyphicon glyphicon-info-sign" title="{{ _('Details') }}"></i></a>
<a href="/articles/{{ fid }}/100"><i class="glyphicon glyphicon-th-list" title="{{ _('Articles') }}"></i></a>
- <a href="/edit_feed/{{ fid }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this feed') }}"></i></a>
- <a href="/delete_feed/{{ fid }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a>
- <a href="/mark_as/read/feed/{{ fid }}"><i class="glyphicon glyphicon-check" title="{{ _('Mark this feed as read') }}"></i></a>
- <a href="/mark_as/unread/feed/{{ fid }}"><i class="glyphicon glyphicon-unchecked" title="{{ _('Mark this feed as unread') }}"></i></a>
+ <a href="{{ url_for("feed.form", feed_id=fid) }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this feed') }}"></i></a>
+ <a href="{{ url_for("feed.delete", feed_id=fid) }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a>
+ <a href="{{ url_for("feed.update", feed_id=fid, action="read") }}"><i class="glyphicon glyphicon-check" title="{{ _('Mark this feed as read') }}"></i></a>
+ <a href="{{ url_for("feed.update", feed_id=fid, action="unread") }}"><i class="glyphicon glyphicon-unchecked" title="{{ _('Mark this feed as unread') }}"></i></a>
</span></li>
{% endfor %}
</ul>
@@ -82,16 +82,16 @@
{% for article in articles %}
<tr data-article="{{ article.id }}" data-feed="{{ article.feed_id }}">
<td>
- <a href="#"><i class="glyphicon glyphicon-remove delete" title="{{ _('Delete this article') }}"></i></a>
+ <a><i class="glyphicon glyphicon-remove delete" title="{{ _('Delete this article') }}"></i></a>
{% if article.like %}
- <a href="#"><i class="glyphicon glyphicon-star like" title="{{ _('One of your favorites') }}"></i></a>
+ <a><i class="glyphicon glyphicon-star like" title="{{ _('One of your favorites') }}"></i></a>
{% else %}
- <a href="#"><i class="glyphicon glyphicon-star-empty like" title="{{ _('Click if you like this article') }}"></i></a>
+ <a><i class="glyphicon glyphicon-star-empty like" title="{{ _('Click if you like this article') }}"></i></a>
{% endif %}
{% if article.readed %}
- <a href="#"><i class="glyphicon glyphicon-unchecked readed" title="{{ _('Mark this article as unread') }}"></i></a>
+ <a><i class="glyphicon glyphicon-unchecked readed" title="{{ _('Mark this article as unread') }}"></i></a>
{% else %}
- <a href="#"><i class="glyphicon glyphicon-check readed" title="{{ _('Mark this article as read') }}"></i></a>
+ <a><i class="glyphicon glyphicon-check readed" title="{{ _('Mark this article as read') }}"></i></a>
{% if filter_ == 'all' %}</b>{% endif %}
{% endif %}
</td>
diff --git a/pyaggr3g470r/templates/layout.html b/pyaggr3g470r/templates/layout.html
index 60efa69e..e673a128 100644
--- a/pyaggr3g470r/templates/layout.html
+++ b/pyaggr3g470r/templates/layout.html
@@ -6,13 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="pyAggr3g470r is a web-based news aggregator." />
<meta name="author" content="" />
- <title>{% if head_title %}{{ head_title }} - {% endif %}pyAggr3g470r</title>
+ <title>pyAggr3g470r{% if head_title %} - {{ head_title }}{% endif %}</title>
<link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.png') }}" />
<!-- Bootstrap core CSS -->
- <link href="{{ url_for('static', filename = 'css/bootstrap.css') }}" rel="stylesheet" media="screen" />
+ <link href="{{ url_for('static', filename='css/bootstrap.css') }}" rel="stylesheet" media="screen" />
<!-- Add custom CSS here -->
- <link href="{{ url_for('static', filename = 'css/customized-bootstrap.css') }}" rel="stylesheet" media="screen" />
- <link href="{{ url_for('static', filename = 'css/side-nav.css') }}" rel="stylesheet" media="screen" />
+ <link href="{{ url_for('static', filename='css/customized-bootstrap.css') }}" rel="stylesheet" media="screen" />
+ <link href="{{ url_for('static', filename='css/side-nav.css') }}" rel="stylesheet" media="screen" />
{% endblock %}
</head>
<body>
@@ -25,38 +25,44 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
- <a class="navbar-brand" href="/">pyAggr3g470r</a>
+ <a class="navbar-brand" href="{{ url_for("home") }}">pyAggr3g470r</a>
+ <span class="navbar-brand">{% if head_title %} - {{ head_title }}{% endif %}</span>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav navbar-right">
{% if g.user.is_authenticated() %}
+ <li><a href="{{ url_for("feed.form") }}"><span class="glyphicon glyphicon-plus-sign"></span>{{ _('Add a feed') }}</a></li>
+ {% if favorites %}
+ <li><a href="{{ url_for("home") }}"><span class="glyphicon glyphicon-home"></span> {{ _('Home') }}</a></li>
+ {% else %}
+ <li><a accesskey="f" href="{{ url_for("favorites") }}"><span class="glyphicon glyphicon-star"></span> {{ _('Favorites') }}</a></li>
+ {% endif %}
+ {% if conf.ON_HEROKU and g.user.is_admin() %}
+ <li><a accesskey="r" href="/fetch"><span class="glyphicon glyphicon-import"></span>{{ _('Fetch') }}</a></li>
+ {% endif %}
<li class="dropdown">
- <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ _('Articles') }} <b class="caret"></b></a>
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ _('Feed') }} <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="{{ url_for("feeds.update", action="read") }}">{{ _('Mark all as read') }}</a></li>
<li role="presentation" class="divider"></li>
- <li><a href="/create_feed">{{ _('Add a feed') }}</a></li>
+ <li><a accesskey="i" href="{{ url_for("inactives") }}">{{ _('Inactive') }}</a></li>
+ <li><a href="{{ url_for("history") }}">{{ _('History') }}</a></li>
+ <li><a href="{{ url_for("feeds.feeds") }}">{{ _('All') }}</a></li>
</ul>
</li>
<li class="dropdown">
- <a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-filter"></span> {{ _('Filter') }} <b class="caret"></b></a>
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ _('Tools') }} <b class="caret"></b></a>
<ul class="dropdown-menu">
- <li><a accesskey="u" href="/unread">{{ _('Unread') }}</a></li>
- <li><a accesskey="f" href="/favorites">{{ _('Favorites') }}</a></li>
- <li><a accesskey="i" href="/inactives">{{ _('Inactive feeds') }}</a></li>
- <li><a href="/feeds">{{ _('All feeds') }}</a></li>
- <li><a href="/history">{{ _('History') }}</a></li>
+i <li><a accesskey="m" href="{{ url_for("management") }}"><span class="glyphicon glyphicon-cog"></span> {{ _('Management') }}</a></li>
+ {% if g.user.is_admin() %}
+ <li><a href="{{ url_for("dashboard") }}"><span class="glyphicon glyphicon-dashboard"></span> {{ _('Dashboard') }}</a></li>
+ {% endif %}
+ <li><a href="{{ url_for("about") }}"><span class="glyphicon glyphicon-question-sign"></span>{{ _('About') }}</a></li>
</ul>
</li>
- <li><a accesskey="m" href="/management"><span class="glyphicon glyphicon-cog"></span> {{ _('Management') }}</a></li>
- {% if g.user.is_admin() %}
- <li><a href="{{ url_for('dashboard') }}"><span class="glyphicon glyphicon-dashboard"></span> {{ _('Dashboard') }}</a></li>
- {% endif %}
- <li><a href="/about">{{ _('About') }}</a></li>
- <li><a href="{{ url_for('logout') }}"><span class="glyphicon glyphicon-log-out"></span> {{ _('Logout') }}</a></li>
+ <li><a href="{{ url_for("logout") }}"><span class="glyphicon glyphicon-log-out"></span> {{ _('Logout') }}</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<div><span class="glyphicon glyphicon-search"></span>&nbsp;<b class="caret"></b></div>
@@ -72,7 +78,7 @@
</ul>
</li>
{% else %}
- <li><a href="/about">{{ _('About') }}</a></li>
+ <li><a href="{{ url_for("about") }}"><span class="glyphicon glyphicon-question-sign"></span>{{ _('About') }}</a></li>
{% endif %}
</ul>
</div><!-- /.navbar-collapse -->
@@ -102,18 +108,19 @@
<script src="{{ url_for('static', filename = 'js/bootstrap.js') }}"></script>
<script src="{{ url_for('static', filename = 'js/articles.js') }}"></script>
<script type="text/javascript" class="source">
- if (window.location.href.indexOf("filter_=all") > -1){
- $("#tab-all").attr('class', "active");
- }
- else if (window.location.href.indexOf("filter_=unread") > -1) {
- $("#tab-unread").attr('class', "active");
- }
- else if (window.location.href.indexOf("filter_=read") > -1) {
- $("#tab-read").attr('class', "active");
- }
- else {
- $("#tab-unread").attr('class', "active");
+ var filter_ = {% if filter_ %}"{{ filter_ }}"{% else %}undefined{% endif %};
+ if (filter_ == undefined) {
+ if (window.location.href.indexOf("filter_=all") > -1){
+ filter_ = 'all';
+ }
+ else if (window.location.href.indexOf("filter_=unread") > -1) {
+ filter_ = 'unread';
+ }
+ else if (window.location.href.indexOf("filter_=read") > -1) {
+ filter_ = 'read';
+ }
}
+ $("#tab-" + filter_).attr('class', "active");
</script>
</body>
</html>
diff --git a/pyaggr3g470r/templates/management.html b/pyaggr3g470r/templates/management.html
index 722300af..b9d02de5 100644
--- a/pyaggr3g470r/templates/management.html
+++ b/pyaggr3g470r/templates/management.html
@@ -3,7 +3,7 @@
<div class="container">
<div class="well">
<h1>{{ _('Your subscriptions') }}</h1>
- <p>{{ _('You are subscribed to') }} {{ nb_feeds }} <a href="/feeds">{{ _('feeds') }}</a>. {{ _('Add a') }} <a href="/create_feed">{{ _('feed') }}</a>.</p>
+ <p>{{ _('You are subscribed to') }} {{ nb_feeds }} <a href="/feeds">{{ _('feeds') }}</a>. {{ _('Add a') }} <a href="{{ url_for("feed.form") }}">{{ _('feed') }}</a>.</p>
<p>{{ nb_articles }} {{ _('articles are stored in the database with') }} {{ nb_unread_articles }} <a href="/unread">{{ _('unread articles') }}</a>.</p>
{% if not_on_heroku %}
<a href="/index_database" class="btn btn-default">{{ _('Index database') }}</a>
diff --git a/pyaggr3g470r/templates/unread.html b/pyaggr3g470r/templates/unread.html
deleted file mode 100644
index 9808572b..00000000
--- a/pyaggr3g470r/templates/unread.html
+++ /dev/null
@@ -1,51 +0,0 @@
- {% extends "layout.html" %}
-{% block content %}
-<div class="container">
- {% if feeds|count == 0 %}
- <div class="page-header">
- <h1>{{ _('No unread articles') }}</h1>
- </div>
- {% else %}
- <div class="page-header">
- <h1>{{ _('Unread articles') }} <small>{{ nb_unread }}</small></h1>
- </div>
- {% for feed in feeds|sort(attribute="title") %}
- <div class="row">
- <div class="col-md-6 col-md-offset-3">
- <h1>{{ feed.title|safe }}</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>
- <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|length }} {{ _('unread articles') }}.</h3>
- </div>
- </div>
- {% for number in range(0, feed.articles|length-(feed.articles|length % 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|length % 3 != 0 %}
- <div class="row">
- {% for n in range(feed.articles|length-(feed.articles|length % 3), feed.articles|length) %}
- <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><!-- /.container -->
-{% endblock %}
diff --git a/pyaggr3g470r/views/feed.py b/pyaggr3g470r/views/feed.py
index 4fe4e5da..e4c0dc9a 100644
--- a/pyaggr3g470r/views/feed.py
+++ b/pyaggr3g470r/views/feed.py
@@ -2,33 +2,40 @@
# -*- coding: utf-8 -
from datetime import datetime
-from flask import Blueprint, g, render_template
from sqlalchemy import desc
+from werkzeug.exceptions import BadRequest
-from pyaggr3g470r import controllers, utils
-from pyaggr3g470r.decorators import pyagg_default_decorator, \
- feed_access_required
+from flask import Blueprint, g, render_template, flash, \
+ redirect, request, url_for
+from flask.ext.babel import gettext
+from flask.ext.login import login_required
+
+import conf
+from pyaggr3g470r import utils
+from pyaggr3g470r.forms import AddFeedForm
+from pyaggr3g470r.controllers import FeedController, ArticleController
feeds_bp = Blueprint('feeds', __name__, url_prefix='/feeds')
feed_bp = Blueprint('feed', __name__, url_prefix='/feed')
+
@feeds_bp.route('/', methods=['GET'])
+@login_required
def feeds():
"Lists the subscribed feeds in a table."
return render_template('feeds.html',
- feeds=controllers.FeedController(g.user.id).read())
+ feeds=FeedController(g.user.id).read())
@feed_bp.route('/<int:feed_id>', methods=['GET'])
-@pyagg_default_decorator
-@feed_access_required
+@login_required
def feed(feed_id=None):
"Presents detailed information about a feed."
- feed = controllers.FeedController(g.user.id).get(id=feed_id)
+ feed = FeedController(g.user.id).get(id=feed_id)
word_size = 6
- articles = controllers.ArticleController(g.user.id) \
- .read(feed_id=feed_id) \
- .order_by(desc("Article.date")).all()
+ articles = ArticleController(g.user.id) \
+ .read(feed_id=feed_id) \
+ .order_by(desc("Article.date")).all()
top_words = utils.top_words(articles, n=50, size=int(word_size))
tag_cloud = utils.tag_cloud(top_words)
@@ -51,3 +58,124 @@ def feed(feed_id=None):
first_post_date=first_article,
end_post_date=last_article,
average=average, delta=delta, elapsed=elapsed)
+
+
+@feed_bp.route('/delete/<feed_id>', methods=['GET'])
+@login_required
+def delete(feed_id=None):
+ feed_contr = FeedController(g.user.id)
+ feed = feed_contr.get(id=feed_id)
+ feed_contr.delete(feed_id)
+ flash(gettext("Feed %(feed_title)s successfully deleted.",
+ feed_title=feed.title), 'success')
+ return redirect(url_for('home'))
+
+
+@feed_bp.route('/reset_errors/<int:feed_id>', methods=['GET', 'POST'])
+@login_required
+def reset_errors(feed_id):
+ feed_contr = FeedController(g.user.id)
+ feed = feed_contr.get(id=feed_id)
+ feed_contr.update({'id': feed_id}, {'error_count': 0, 'last_error': ''})
+ flash(gettext('Feed %(feed_title)r successfully updated.',
+ feed_title=feed.title), 'success')
+ return redirect(request.referrer or url_for('home'))
+
+
+@feed_bp.route('/bookmarklet', methods=['GET'])
+@login_required
+def bookmarklet():
+ feed_contr = FeedController(g.user.id)
+ url = request.args.get('url', None)
+ if not url:
+ flash(gettext("Couldn't add feed: url missing."), "error")
+ raise BadRequest("url is missing")
+
+ existing_feeds = list(feed_contr.read(link=url))
+ if existing_feeds:
+ flash(gettext("Couldn't add feed: feed already exists."),
+ "warning")
+ return redirect(url_for('feed.form',
+ feed_id=existing_feeds[0].id))
+
+ feed = feed_contr.create(link=url)
+ flash(gettext('Feed was successfully created.'), 'success')
+ return redirect(url_for('feed.form', feed_id=feed.id))
+
+
+@feed_bp.route('/read/<int:feed_id>', methods=['GET', 'POST'])
+@login_required
+def read(feed_id):
+ FeedController(g.user.id).update(readed=True)
+ flash(gettext('Feed successfully updated.',
+ feed_title=feed.title), 'success')
+ return redirect(request.referrer or url_for('home'))
+
+
+@feed_bp.route('/update/<action>/<int:feed_id>', methods=['GET', 'POST'])
+@feeds_bp.route('/update/<action>', methods=['GET', 'POST'])
+@login_required
+def update(action, feed_id=None):
+ readed = action == 'read'
+ filters = {'readed__ne': readed}
+ if feed_id:
+ filters['feed_id'] = feed_id
+ ArticleController(g.user.id).update(filters, {'readed': readed})
+ flash(gettext('Feed successfully updated.'), 'success')
+ return redirect(request.referrer or url_for('home'))
+
+
+@feed_bp.route('/create', methods=['GET', 'POST', 'PUT'])
+@feed_bp.route('/edit/<int:feed_id>', methods=['GET', 'POST'])
+@login_required
+def form(feed_id=None):
+ form = AddFeedForm()
+ feed_contr = FeedController(g.user.id)
+
+ if request.method == 'POST':
+ if not form.validate():
+ return render_template('edit_feed.html', form=form)
+ existing_feeds = list(feed_contr.read(link=form.link.data))
+ if existing_feeds and feed_id is None:
+ flash(gettext("Couldn't add feed: feed already exists."),
+ "warning")
+ return redirect(url_for('feed.form',
+ feed_id=existing_feeds[0].id))
+
+ # Edit an existing feed
+ if feed_id is not None:
+ feed_contr.update({'id': feed_id},
+ {'title': form.title.data,
+ 'link': form.link.data,
+ 'enabled': form.enabled.data,
+ 'site_link': form.site_link.data})
+ flash(gettext('Feed %(feed_title)r successfully updated.',
+ feed_title=form.title.data), 'success')
+ return redirect(url_for('feed.form', feed_id=feed_id))
+
+ # Create a new feed
+ new_feed = FeedController(g.user.id).create(
+ title=form.title.data,
+ description="",
+ link=form.link.data,
+ site_link=form.site_link.data,
+ enabled=form.enabled.data)
+
+ flash(gettext('Feed %(feed_title)r successfully created.',
+ feed_title=new_feed.title), 'success')
+
+ return redirect(url_for('feed.form',
+ feed_id=new_feed.id))
+
+ # Getting the form for an existing feed
+ if feed_id is not None:
+ feed = FeedController(g.user.id).get(id=feed_id)
+ form = AddFeedForm(obj=feed)
+ return render_template('edit_feed.html',
+ action=gettext("Edit the feed"),
+ form=form, feed=feed,
+ not_on_heroku=not conf.ON_HEROKU)
+
+ # Return an empty form in order to create a new feed
+ return render_template('edit_feed.html', action=gettext("Add a feed"),
+ form=form, not_on_heroku=not conf.ON_HEROKU)
diff --git a/pyaggr3g470r/views/views.py b/pyaggr3g470r/views/views.py
index 17d79cab..e06e1a9d 100644
--- a/pyaggr3g470r/views/views.py
+++ b/pyaggr3g470r/views/views.py
@@ -34,25 +34,25 @@ import datetime
from collections import namedtuple
from bootstrap import application as app, db
from flask import render_template, request, flash, session, \
- url_for, redirect, g, current_app, make_response, jsonify
+ 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 func, or_
+from sqlalchemy import or_
from sqlalchemy.exc import IntegrityError
from werkzeug import generate_password_hash
import conf
from pyaggr3g470r import utils, notifications, export
-from pyaggr3g470r import controllers
from pyaggr3g470r.models import User, Feed, Article, Role
from pyaggr3g470r.decorators import feed_access_required
from pyaggr3g470r.forms import SignupForm, SigninForm, AddFeedForm, \
ProfileForm, InformationMessageForm, RecoverPasswordForm
-from pyaggr3g470r.controllers import FeedController
+from pyaggr3g470r.controllers import UserController, FeedController, \
+ ArticleController
if not conf.ON_HEROKU:
import pyaggr3g470r.search as fastsearch
@@ -93,7 +93,7 @@ def before_request():
@login_manager.user_loader
def load_user(email):
# Return an instance of the User model
- return controllers.UserController().get(email=email)
+ return UserController().get(email=email)
#
@@ -153,7 +153,7 @@ def login():
form = SigninForm()
if form.validate_on_submit():
- user = controllers.UserController().get(email=form.email.data)
+ user = UserController().get(email=form.email.data)
login_user(user)
g.user = user
session['email'] = form.email.data
@@ -225,39 +225,60 @@ def signup():
@app.route('/')
@login_required
-def home():
+def home(favorites=False):
"""
Home page for connected users. Displays by default unread articles.
"""
- feeds = {feed.id: feed.title for feed in g.user.feeds}
- articles = Article.query.filter(Article.feed_id.in_(feeds.keys()),
- Article.user_id == g.user.id)
- filter_ = request.args.get('filter_', 'unread')
+ head_title = gettext('Favorites') if favorites else ''
+ feed_contr = FeedController(g.user.id)
+ arti_contr = ArticleController(g.user.id)
+ feeds = {feed.id: feed.title for feed in feed_contr.read()}
+
+ unread = arti_contr.get_unread()
+ in_error = {feed.id: feed.error_count for feed in
+ feed_contr.read(error_count__gt=2)}
+
+ filter_ = request.args.get('filter_', 'all' if favorites else 'unread')
feed_id = int(request.args.get('feed', 0))
limit = request.args.get('limit', 1000)
+
+ filters = {}
+ if favorites:
+ filters['like'] = True
if filter_ != 'all':
- articles = articles.filter(Article.readed == (filter_ == 'read'))
+ filters['readed'] = filter_ == 'read'
if feed_id:
- articles = articles.filter(Article.feed_id == feed_id)
+ filters['feed_id'] = feed_id
+ if head_title:
+ head_title += ' - '
+ head_title += feed_contr.get(id=feed_id).title
- articles = articles.order_by(Article.date.desc())
+ articles = arti_contr.read(**filters).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, Article.user_id == g.user.id)\
- .group_by(Article.feed_id).all()
- in_error = {feed.id: feed.error_count for feed in
- FeedController(g.user.id).read(error_count__gt=2).all()}
+
def gen_url(filter_=filter_, limit=limit, feed=feed_id):
- return '?filter_=%s&limit=%s&feed=%d' % (filter_, limit, feed)
+ return url_for('favorites' if favorites else 'home',
+ filter_=filter_, limit=limit, feed=feed)
+
+ articles = list(articles)
+ if not articles and not favorites and feed_id:
+ return redirect(gen_url(filter_='all'))
+
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(),
- in_error=in_error,
+ unread=unread, articles=articles, in_error=in_error,
+ head_title=head_title, favorites=favorites,
default_max_error = conf.DEFAULT_MAX_ERROR)
+@app.route('/favorites')
+@login_required
+def favorites():
+ return home(favorites=True)
+
+
@app.route('/fetch', methods=['GET'])
@app.route('/fetch/<int:feed_id>', methods=['GET'])
@login_required
@@ -283,7 +304,6 @@ def about():
@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
@@ -342,40 +362,6 @@ def delete(article_id=None):
return redirect(url_for('home'))
-@app.route('/favorites', methods=['GET'])
-@login_required
-def favorites():
- """
- List favorites articles.
- """
- feeds_with_like = Feed.query.filter(Feed.user_id == g.user.id, Feed.articles.any(like=True))
- result, nb_favorites = [], 0
- light_feed = namedtuple('Feed', ['id', 'title', 'articles'], verbose=False, rename=False)
- for feed in feeds_with_like:
- articles = Article.query.filter(Article.user_id == g.user.id, Article.feed_id == feed.id, Article.like == True).all()
- result.append(light_feed(feed.id, feed.title, articles))
- nb_favorites += len(articles)
- return render_template('favorites.html', feeds=result, nb_favorites=nb_favorites)
-
-@app.route('/unread/<int:feed_id>', methods=['GET'])
-@app.route('/unread', methods=['GET'])
-@login_required
-def unread(feed_id=None):
- """
- List unread articles.
- """
- if feed_id is not None:
- feeds_with_unread = Feed.query.filter(Feed.user_id == g.user.id, Feed.id == feed_id)
- else:
- feeds_with_unread = Feed.query.filter(Feed.user_id == g.user.id, Feed.articles.any(readed=False))
- result, nb_unread = [], 0
- light_feed = namedtuple('Feed', ['id', 'title', 'articles'], verbose=False, rename=False)
- for feed in feeds_with_unread:
- articles = Article.query.filter(Article.user_id == g.user.id, Article.feed_id == feed.id, Article.readed == False).all()
- result.append(light_feed(feed.id, feed.title, articles))
- nb_unread += len(articles)
- return render_template('unread.html', feeds=result, nb_unread=nb_unread)
-
@app.route('/inactives', methods=['GET'])
@login_required
def inactives():
@@ -383,7 +369,7 @@ def inactives():
List of inactive feeds.
"""
nb_days = int(request.args.get('nb_days', 365))
- user = controllers.UserController(g.user.id).get(email=g.user.email)
+ user = UserController(g.user.id).get(email=g.user.email)
today = datetime.datetime.now()
inactives = []
for feed in user.feeds:
@@ -430,7 +416,7 @@ def export_articles():
"""
Export all articles to HTML or JSON.
"""
- user = controllers.UserController(g.user.id).get(id=g.user.id)
+ user = UserController(g.user.id).get(id=g.user.id)
if request.args.get('format') == "HTML":
# Export to HTML
try:
@@ -463,7 +449,7 @@ def export_opml():
"""
Export all feeds to OPML.
"""
- user = controllers.UserController(g.user.id).get(id=g.user.id)
+ user = UserController(g.user.id).get(id=g.user.id)
response = make_response(render_template('opml.xml', user=user,
now=datetime.datetime.now()))
response.headers['Content-Type'] = 'application/xml'
@@ -557,82 +543,6 @@ def history(year=None, month=None):
articles=articles,
year=year, month=month)
-@app.route('/bookmarklet', methods=['GET'])
-@app.route('/create_feed', methods=['GET', 'POST'])
-@app.route('/edit_feed/<int:feed_id>', methods=['GET', 'POST'])
-@login_required
-@feed_access_required
-def edit_feed(feed_id=None):
- """
- Add or edit a feed.
- """
- form = AddFeedForm()
-
- if request.method == 'POST':
- if form.validate() == False:
- return render_template('edit_feed.html', form=form)
- if feed_id is not None:
- # Edit an existing feed
- feed = FeedController(g.user.id).get(id=feed_id)
- form.populate_obj(feed)
- if feed.enabled:
- # set the error count to 0
- feed.error_count = 0
- feed.last_error = ""
- db.session.commit()
- flash(gettext('Feed successfully updated.'), 'success')
- return redirect('/edit_feed/' + str(feed_id))
- else:
- # Create a new feed
- existing_feed = [f for f in g.user.feeds if f.link == form.link.data]
- if len(existing_feed) == 0:
- new_feed = Feed(title=form.title.data, description="", link=form.link.data, \
- site_link=form.site_link.data, enabled=form.enabled.data)
- g.user.feeds.append(new_feed)
- #user.feeds = sorted(user.feeds, key=lambda t: t.title.lower())
- db.session.commit()
- flash(gettext('Feed successfully created.'), 'success')
-
- utils.fetch(g.user.id, Feed.query.filter(Feed.link == form.link.data).first().id)
- flash(gettext("Downloading articles for the new feed..."), 'info')
-
- return redirect('/edit_feed/' + str(new_feed.id))
- else:
- flash(gettext('Feed already in the database.'), 'warning')
- return redirect('/edit_feed/' + str(existing_feed[0].id))
-
- if request.method == 'GET':
- if feed_id is not None:
- feed = FeedController(g.user.id).get(id=feed_id)
- form = AddFeedForm(obj=feed)
- return render_template('edit_feed.html', action=gettext("Edit the feed"), form=form, feed=feed, \
- not_on_heroku = not conf.ON_HEROKU)
-
- # Enable the user to add a feed with a bookmarklet
- if None is not request.args.get('url', None):
- existing_feed = [f for f in g.user.feeds if feed.link == request.args.get('url', None)]
- if len(existing_feed) == 0:
- g.user.feeds.append(Feed(link=request.args.get('url', None)))
- db.session.commit()
- return jsonify({"message":"ok"})
- return jsonify({"message":"Feed already in the database."})
-
- # Return an empty form in order to create a new feed
- return render_template('edit_feed.html', action=gettext("Add a feed"), form=form, \
- not_on_heroku = not conf.ON_HEROKU)
-
-@app.route('/delete_feed/<feed_id>', methods=['GET'])
-@login_required
-@feed_access_required
-def delete_feed(feed_id=None):
- """
- Delete a feed with all associated articles.
- """
- feed = Feed.query.filter(Feed.id == feed_id).first()
- db.session.delete(feed)
- db.session.commit()
- flash(gettext('Feed') + ' ' + feed.title + ' ' + gettext('successfully deleted.'), 'success')
- return redirect(redirect_url())
@app.route('/profile', methods=['GET', 'POST'])
@login_required
@@ -640,7 +550,7 @@ def profile():
"""
Edit the profile of the currently logged user.
"""
- user = controllers.UserController(g.user.id).get(id=g.user.id)
+ user = UserController(g.user.id).get(id=g.user.id)
form = ProfileForm()
if request.method == 'POST':
@@ -666,7 +576,7 @@ def delete_account():
"""
Delete the account of the user (with all its data).
"""
- user = controllers.UserController(g.user.id).get(id=g.user.id)
+ user = UserController(g.user.id).get(id=g.user.id)
if user is not None:
db.session.delete(user)
db.session.commit()
diff --git a/runserver.py b/runserver.py
index db1c4410..a80b0c39 100755
--- a/runserver.py
+++ b/runserver.py
@@ -19,7 +19,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import calendar
-from bootstrap import conf, application, db, populate_g
+from bootstrap import conf, application, populate_g
from flask.ext.babel import Babel
from flask.ext.babel import format_datetime
@@ -30,12 +30,11 @@ if conf.ON_HEROKU:
babel = Babel(application)
# Jinja filters
-application.jinja_env.filters['datetime'] = format_datetime
-
-#@register.filter
def month_name(month_number):
return calendar.month_name[month_number]
application.jinja_env.filters['month_name'] = month_name
+application.jinja_env.filters['datetime'] = format_datetime
+application.jinja_env.globals['conf'] = conf
# Views
from flask.ext.restful import Api
bgstack15