diff options
author | Cédric Bonhomme <cedric@cedricbonhomme.org> | 2020-03-09 23:16:05 +0100 |
---|---|---|
committer | Cédric Bonhomme <cedric@cedricbonhomme.org> | 2020-03-09 23:16:05 +0100 |
commit | 3ab6290d4994b33cdbf831523938cdb18a13bf49 (patch) | |
tree | 685980f53aaa3eda4e27ddfc7032554f55528e57 /newspipe/templates | |
parent | Improved method to detect current version of the Newspipe instance. (diff) | |
download | newspipe-3ab6290d4994b33cdbf831523938cdb18a13bf49.tar.gz newspipe-3ab6290d4994b33cdbf831523938cdb18a13bf49.tar.bz2 newspipe-3ab6290d4994b33cdbf831523938cdb18a13bf49.zip |
Refactoring the backend.
Diffstat (limited to 'newspipe/templates')
33 files changed, 1513 insertions, 0 deletions
diff --git a/newspipe/templates/about.html b/newspipe/templates/about.html new file mode 100644 index 00000000..43d8c73d --- /dev/null +++ b/newspipe/templates/about.html @@ -0,0 +1,26 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <div class="card"> + <div class="card-body"> + <h2>{{ _('About') }}</h2> + <p> + {{ _('Newspipe is a news aggregator platform.') }} + <p>{{ _('This software is under AGPLv3 license. You are welcome to copy, modify or + redistribute the <a href="https://git.sr.ht/~cedric/newspipe">source code</a> + according to the <a href="https://www.gnu.org/licenses/agpl-3.0.html">Affero GPL</a> license.') }}</p> + <p>{{ _('Found a bug? Report it <a href="https://todo.sr.ht/~cedric/newspipe">here</a>.') }}</p> + <p><a href="{{ url_for('about_more') }}">{{ _('More information') }}</a> {{ _('about this instance.') }}</p> + </div> + </div> + <br /> + <div class="card"> + <div class="card-body"> + <h2>{{ _('Help') }}</h2> + <p>{{ _('Contact')}}: <a href="mailto:{{ contact }}">{{ contact }}</a></p> + <p>{{ _('You can subscribe to new feeds with a bookmarklet. Drag the following button to your browser bookmarks.') }}</p> + {{ _('<a class="btn btn-primary" href="%(bookmarklet)s" rel="bookmark">Subscribe to this feed using Newspipe</a>', bookmarklet='javascript:window.location="%s?url="+encodeURIComponent(document.location)' % url_for('feed.bookmarklet', _external=True)) }} + </div> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/about_more.html b/newspipe/templates/about_more.html new file mode 100644 index 00000000..94a01c07 --- /dev/null +++ b/newspipe/templates/about_more.html @@ -0,0 +1,11 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <ul class="list-group"> + <li class="list-group-item">{{ _('Newspipe version') }}: <a href="{{ version_url }}">{{newspipe_version}}</a></li> + <li class="list-group-item">{{ _('Registration') }}: {{registration}}</li> + <li class="list-group-item">{{ _('Python version') }}: {{python_version}}</li> + <li class="list-group-item">{{ _('Number of users') }}: {{nb_users}}</li> + </ul> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/admin/create_user.html b/newspipe/templates/admin/create_user.html new file mode 100644 index 00000000..8844f987 --- /dev/null +++ b/newspipe/templates/admin/create_user.html @@ -0,0 +1,26 @@ +{% extends "layout.html" %} +{% block head%} +{{super()}} +{% endblock %} +{% block content %} +<div class="container"> + <div class="well"> + <h2>{{ message | safe }}</h2> + <form action="" method="post" name="saveprofileform" id="profileform"> + {{ form.hidden_tag() }} + + {{ form.nickname.label }} + {{ form.nickname(class_="form-control") }} {% for error in form.nickname.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + + {{ form.password.label }} + {{ form.password(class_="form-control") }} {% for error in form.password.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + + {{ form.automatic_crawling.label }} + {{ form.automatic_crawling(class_="form-control") }} {% for error in form.automatic_crawling.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + + <br /> + {{ form.submit(class_="btn btn-primary") }} + </form> + </div> +</div> +{% endblock %} diff --git a/newspipe/templates/admin/dashboard.html b/newspipe/templates/admin/dashboard.html new file mode 100644 index 00000000..8bcc2db0 --- /dev/null +++ b/newspipe/templates/admin/dashboard.html @@ -0,0 +1,68 @@ +{% extends "layout.html" %} +{% block head%} +{{super()}} +{% endblock %} +{% block content %} +<div class="container"> +<h1>{{ _('Registered users') }}</h1> +<table id="table-users" class="table table-striped"> + <thead> + <tr> + <th>#</th> + <th>{{ _('Nickname') }}</th> + <th>{{ _('Member since') }}</th> + <th>{{ _('Last seen') }}</th> + <th>{{ _('Actions') }}</th> + </tr> + </thead> + <tbody> + {% for user in users %} + <tr {% if not user.is_active %}class="warning"{% endif %}> + <td>{{ loop.index }}</td> + <td> + {% if user.is_public_profile %} + <a href="{{ url_for("user.profile_public", nickname=user.nickname) }}">{{ user.nickname }}</a> + {% else %} + {{ user.nickname }} + {% endif %} + {% if user.id == current_user.id %} (It's you!){% endif %} + </td> + <td class="date">{{ user.date_created | datetime }}</td> + <td class="date">{{ user.last_seen | datetime }}</td> + <td> + <a href="{{ url_for("admin.user_form", user_id=user.id) }}"><i class="fa fa-pencil-square-o" aria-hidden="true" title="{{ _('Edit this user') }}"></i></a> + {% if user.id != current_user.id %} + <a href="{{ url_for("admin.toggle_user", user_id=user.id) }}"> + {% if user.is_active %} + <i class="fa fa-ban" title="{{ _("Disable this account") }}"></i> + {% else %} + <i class="fa fa-check-circle-o" aria-hidden="true" title="{{ _("Enable this account") }}"></i> + {% endif %} + </a> + <a href="{{ url_for("admin.delete_user", user_id=user.id) }}"><i class="fa fa-times" aria-hidden="true" title="{{ _('Delete this user') }}" onclick="return confirm('{{ _('You are going to delete this account.') }}');"></i></a> + {% endif %} + </td> + </tr> + {% endfor %} + </tbody> +</table> +<a href="{{ url_for("admin.user_form") }}" class="btn btn-primary">{{ _('Add a new user') }}</a> +</div> +<script> +$(document).ready(function() { + $('#table-users').DataTable( { + responsive: true, + columnDefs: [ + { + targets: [0, 4], + "searchable": false + }, + { + targets: [3], + "orderSequence": ["desc"] + } + ] + }); +}); +</script> +{% endblock %} diff --git a/newspipe/templates/article.html b/newspipe/templates/article.html new file mode 100644 index 00000000..884bf677 --- /dev/null +++ b/newspipe/templates/article.html @@ -0,0 +1,35 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container" data-article="{{ article.id }}"> + <div class="well"> + <h2><a href="{{ article.link }}" target="_blank">{{ article.title|safe }}</a></h2> + <h3>{{ _('from') }} <a href="/feed/{{ article.source.id }}">{{ article.source.title }}</a></h3> + <a href="{{ url_for("article.delete", article_id=article.id) }}"><i class="fa fa-times" aria-hidden="true" title="{{ _('Delete this article') }}"></i></a> + {% if article.like %} + <a href="#"><i class="fa fa-star" aria-hidden="true" title="{{ _('One of your favorites') }}"></i></a> + {% else %} + <a href="#"><i class="fa fa-star-o" aria-hidden="true" title="{{ _('Click if you like this article') }}"></i></a> + {% endif %} + {% if article.readed %} + <a href="#"><i class="fa fa-check-square readed" title="{{ _('Mark this article as unread') }}"></i></a> + {% else %} + <a href="#"><i class="fa fa-check-square-o readed" aria-hidden="true" aria-hidden="true" title="{{ _('Mark this article as read') }}"></i></a> + {% endif %} + <h6>{{ article.date | datetime }}</h6> + </div> + <div class="well"> + {{ article.content | safe }} + </div> + <div class="well"> + <a href="https://api.pinboard.in/v1/posts/add?url={{ article.link }}&description={{ article.title }}" rel="noreferrer" target="_blank"> + <img src="{{ url_for('static', filename='img/pinboard.png') }}" title="{{ _('Share on') }} Pinboard" /> + </a> + <a href="https://reddit.com/submit?url={{ article.link }}&title={{ article.title }}" rel="noreferrer" target="_blank"> + <img src="{{ url_for('static', filename='img/reddit.png') }}" title="{{ _('Share on') }} reddit" /> + </a> + <a href="https://twitter.com/intent/tweet?url={{ article.link }}&text={{ article.title }}" rel="noreferrer" target="_blank"> + <img src="{{ url_for('static', filename='img/twitter.png') }}" title="{{ _('Share on') }} twitter" > + </a> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/article_pub.html b/newspipe/templates/article_pub.html new file mode 100644 index 00000000..e810d18f --- /dev/null +++ b/newspipe/templates/article_pub.html @@ -0,0 +1,24 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container" data-article="{{ article.id }}"> + <div class="well"> + <h2><a href="{{ article.link }}" target="_blank">{{ article.title|safe }}</a></h2> + <h3>{{ _('from') }} <a href="{{ url_for('feed.feed_pub', feed_id=article.source.id) }}">{{ article.source.title }}</a></h3> + <h6>{{ article.date | datetime }}</h6> + </div> + <div class="well"> + {{ article.content | safe }} + </div> + <div class="well"> + <a href="https://api.pinboard.in/v1/posts/add?url={{ article.link }}&description={{ article.title }}" rel="noreferrer" target="_blank"> + <img src="{{ url_for('static', filename='img/pinboard.png') }}" title="{{ _('Share on') }} Pinboard" /> + </a> + <a href="https://reddit.com/submit?url={{ article.link }}&title={{ article.title }}" rel="noreferrer" target="_blank"> + <img src="{{ url_for('static', filename='img/reddit.png') }}" title="{{ _('Share on') }} reddit" /> + </a> + <a href="https://twitter.com/intent/tweet?url={{ article.link }}&text={{ article.title }}" rel="noreferrer" target="_blank"> + <img src="{{ url_for('static', filename='img/twitter.png') }}" title="{{ _('Share on') }} twitter" > + </a> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/bookmarks.html b/newspipe/templates/bookmarks.html new file mode 100644 index 00000000..0f6e02c4 --- /dev/null +++ b/newspipe/templates/bookmarks.html @@ -0,0 +1,81 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <div class="row"> + <div class="col-6"> + {{ pagination.info }} + </div> + <div class="col-6 text-right"> + {% if current_user.is_authenticated %} + <a class="text-muted" href="{{ url_for('bookmarks.list_') }}">all</a> ⸱ + <a class="text-muted" href="{{ url_for('bookmarks.list_') + 'private' }}">private</a> ⸱ + <a class="text-muted" href="{{ url_for('bookmarks.list_') + 'public' }}">public</a> ⸱ + <a class="text-muted" href="{{ url_for('bookmarks.list_') + 'unread' }}">unread</a> + {% endif %} + </div> + </div> + <br /> + <div class="row"> + <div class="col-6"> + {% if tag %} + <i class="fa fa-tag" aria-hidden="true"></i> {{ tag }} + {% endif %} + {% if query %} + {% if tag %}<br />{% endif %} + <i class="fa fa-search" aria-hidden="true"></i> {{ query }} + {% endif %} + </div> + <div class="col-6 text-right pull-right"> + <form method="GET"> + <div class="form-row align-items-center pull-right"> + <div class="col-auto"> + <div class="input-group"> + <input type="text" name="query" class="form-control" /> + <div class="input-group-append"> + <button type="submit" class="btn btn-primary">Search</button> + </div> + </div> + </div> + </div> + </form> + </div> + </div> + <br /> + <div class="row"> + <div class="col-8"> + {{ pagination.links }} + </div> + </div> + + <div class="row"> + <div class="col"> + <ul class="list-group"> + {% for bookmark in bookmarks %} + <li class="list-group-item"> + <a href="#"> + <h4 class="list-group-item-heading"> + <a href="{{ bookmark.href }}">{{ bookmark.title }}</a> + </h4> + <p class="list-group-item-text"> + <div class="text-muted">{{ bookmark.description }}</div> + <div>{% for tag in bookmark.tags %}<a href="{{ url_for('bookmarks.list_', tag=tag.text) }}">{{ tag.text }} </a>{% endfor %}</div> + {{ bookmark.time | datetime }} + {% if current_user.is_authenticated %} + <a class="text-muted" href="{{ url_for('bookmark.form', bookmark_id=bookmark.id) }}">edit</a> + <a class="text-muted" href="{{ url_for('bookmark.delete', bookmark_id=bookmark.id) }}">delete</a> + {% endif %} + </p> + </a> + </li> + {% endfor %} + </ul> + </div> + </div> + <br /> + <div class="row"> + <div class="col-md-8"> + {{ pagination.links }} + </div> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/categories.html b/newspipe/templates/categories.html new file mode 100644 index 00000000..ea5388a3 --- /dev/null +++ b/newspipe/templates/categories.html @@ -0,0 +1,36 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <h1>{{ _("You have %(categories)d categories · Add a %(start_link)scategory%(end_link)s", categories=categories|count, start_link=("<a href='%s'>" % url_for("category.form"))|safe, end_link="</a>"|safe) }}</h1> + {% if categories|count == 0 %} + <h1>{{_("No category")}}</h1> + {% else %} + <div class="table-responsive"> + <table class="table table-striped"> + <thead> + <tr> + <th>#</th> + <th>{{ _('Name') }}</th> + <th>{{ _('Feeds') }}</th> + <th>{{ _('Articles') }}</th> + <th>{{ _('Actions') }}</th> + </tr> + </thead> + <tbody> + {% for category in categories %} + <tr> + <td>{{ loop.index }}</td> + <td>{{ category.name }}</td> + <td>{{ feeds_count.get(category.id, 0) }}</td> + <td>( {{ unread_article_count.get(category.id, 0) }} ) {{ article_count.get(category.id, 0) }}</td> + <td> + <a href="{{ url_for("category.form", category_id=category.id) }}"><i class="fa fa-pencil-square-o" aria-hidden="true" title='{{ _("Edit this category") }}'></i></a> + <a href="{{ url_for("category.delete", category_id=category.id) }}"><i class="fa fa-times" aria-hidden="true" title='{{ _("Delete this category") }}' onclick="return confirm('{{ _('You are going to delete this category.') }}');"></i></a> + </td> + </tr> + {% endfor %} + </tbody> + </table> + </div> + {% endif %} +{% endblock %} diff --git a/newspipe/templates/duplicates.html b/newspipe/templates/duplicates.html new file mode 100644 index 00000000..38dc52b1 --- /dev/null +++ b/newspipe/templates/duplicates.html @@ -0,0 +1,30 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <p><h1>{{ _('Duplicates in the feed') }} <a href="/feed/{{ feed.id }}">{{ feed.title }}</a>.</h1><p> + <div class="table-responsive"> + <table class="table table-striped"> + <thead> + <tr> + <th>#</th> + <th align="center"> + <span class="delete-all btn btn-primary">{{ _('Delete all in this column') }}</span> + </th> + <th align="center"> + <span class="delete-all btn btn-primary">{{ _('Delete all in this column') }}</span> + </th> + </tr> + </thead> + <tbody> + {% for pair in duplicates %} + <tr> + <td>{{ loop.index }}</td> + <td id="{{ pair[0].id }}"><a href="{{ url_for("article.delete", article_id=pair[0].id) }}"><i class="fa fa-times" aria-hidden="true" title="{{ _('Delete this article') }}"></i></a> <a href="/article/{{ pair[0].id }}">{{ pair[0].title }}</a> ({{ pair[0].retrieved_date }})</td> + <td id="{{ pair[1].id }}"><a href="{{ url_for("article.delete", article_id=pair[1].id) }}"><i class="fa fa-times" aria-hidden="true" title="{{ _('Delete this article') }}"></i></a> <a href="/article/{{ pair[1].id }}">{{ pair[1].title }}</a> ({{ pair[1].retrieved_date }})</td> + </tr> + {% endfor %} + </tbody> + </table> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/edit_bookmark.html b/newspipe/templates/edit_bookmark.html new file mode 100644 index 00000000..ee0e0243 --- /dev/null +++ b/newspipe/templates/edit_bookmark.html @@ -0,0 +1,84 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <div class="well"> + <h3>{{ action }}</h3> + <form action="" method="post" name="save" class="form-horizontal"> + {{ form.hidden_tag() }} + <div class="form-group"> + <label for="{{ form.href.id }}" class="col-sm-3 control-label">{{ form.href.label }}</label> + <div class="col-sm-9"> + {{ form.href(class_="form-control", size="100%") }} + </div> + {% for error in form.href.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + </div> + + <div class="form-group"> + <label for="{{ form.title.id }}" class="col-sm-3 control-label">{{ form.title.label }}</label> + <div class="col-sm-9"> + {{ form.title(class_="form-control", size="100%") }} + </div> + {% for error in form.title.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + </div> + + <div class="form-group"> + <label for="{{ form.description.id }}" class="col-sm-3 control-label">{{ form.description.label }}</label> + <div class="col-sm-9"> + {{ form.description(class_="form-control", size="100%") }} + </div> + {% for error in form.description.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + </div> + + <div class="form-group"> + <label for="{{ form.tags.id }}" class="col-sm-3 control-label">{{ form.tags.label }}</label> + <div class="col-sm-9"> + {{ form.tags(class_="form-control", size="100%") }} + </div> + {% for error in form.tags.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + </div> + + <div class="form-group"> + <label for="{{ form.shared.id }}" class="col-sm-3 control-label">{{ form.shared.label }}</label> + <div class="col-sm-9"> + <div class="checkbox"> + {{ form.shared(class_="checkbox", style="margin-left: 0px;") }} + </div> + </div> + </div> + + <div class="form-group"> + <label for="{{ form.to_read.id }}" class="col-sm-3 control-label">{{ form.to_read.label }}</label> + <div class="col-sm-9"> + <div class="checkbox"> + {{ form.to_read(class_="checkbox", style="margin-left: 0px;") }} + </div> + </div> + </div> + + <div class="form-group"> + <div class="col-sm-offset-3 col-sm-9"> + {{ form.submit(class_="btn btn-primary") }} + </div> + </div> + </form> + </div> + {% if action == _('Add a new bookmark') %} + <div class="row"> + <div class="col-md-6 pull-right"> + <p>{{ _('You can add a bookmark with a bookmarklet. Drag the following button to your browser bookmarks.') }}</p> + {{ _('<a class="btn btn-primary" href="%(bookmarklet)s" rel="bookmark">Bookmark this page using Newspipe</a>', bookmarklet='javascript:window.location="%s?href="+encodeURIComponent(document.location)+"&title="+document.title' % url_for('bookmark.bookmarklet', _external=True)) }} + </div> + <div class="col-md-6"> + <form action="{{ url_for('bookmark.import_pinboard') }}" method="post" id="formImportPinboard" enctype="multipart/form-data"> + <p>{{ _('Import bookmarks from Pinboard') }} (*.json)</p> + <span> + <input type="file" name="jsonfile" /> + <br /> + <button class="btn btn-primary" type="submit">OK</button> + </span> + </form> + </div> + </div> + {% endif %} +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/edit_category.html b/newspipe/templates/edit_category.html new file mode 100644 index 00000000..955d17b9 --- /dev/null +++ b/newspipe/templates/edit_category.html @@ -0,0 +1,23 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <div class="well"> + <h3>{{ action }}</h3> + <form action="" method="post" name="save" class="form-horizontal"> + {{ form.hidden_tag() }} + <div class="form-group"> + <label for="{{ form.name.id }}" class="col-sm-3 control-label">{{ form.name.label }}</label> + <div class="col-sm-9"> + {{ form.name(class_="form-control", size="100%") }} + </div> + {% for error in form.name.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + </div> + <div class="form-group"> + <div class="col-sm-offset-3 col-sm-9"> + {{ form.submit(class_="btn btn-primary") }} + </div> + </div> + </form> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/edit_feed.html b/newspipe/templates/edit_feed.html new file mode 100644 index 00000000..a439c78d --- /dev/null +++ b/newspipe/templates/edit_feed.html @@ -0,0 +1,98 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <div class="well"> + <h3>{{ action }}</h3> + <form action="" method="post" name="save" class="form-horizontal"> + {{ form.hidden_tag() }} + <div class="form-group"> + <label for="{{ form.link.id }}" class="col-sm-3 control-label">{{ form.link.label }}</label> + <div class="col-sm-9"> + {{ form.link(class_="form-control", size="100%") }} + </div> + {% for error in form.link.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + </div> + + <div class="form-group"> + <label for="{{ form.title.id }}" class="col-sm-3 control-label">{{ form.title.label }}</label> + <div class="col-sm-9"> + {{ form.title(class_="form-control", size="100%", placeholder=_('Optional')) }} + </div> + {% for error in form.title.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + </div> + + <div class="form-group"> + <label for="{{ form.site_link.id }}" class="col-sm-3 control-label">{{ form.site_link.label }}</label> + <div class="col-sm-9"> + {{ form.site_link(class_="form-control", size="100%", placeholder=_('Optional')) }} + </div> + {% for error in form.site_link.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + </div> + + <div class="form-group"> + <label for="{{ form.category_id.id }}" class="col-sm-3 control-label">{{ form.category_id.label }}</label> + <div class="col-sm-9"> + {{ form.category_id(class_="form-control", placeholder=_('Optional')) }} + </div> + {% for error in form.category_id.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + </div> + + <div class="form-group"> + <label for="{{ form.enabled.id }}" class="col-sm-3 control-label">{{ form.enabled.label }}</label> + <div class="col-sm-9"> + <div class="checkbox"> + {{ form.enabled(class_="checkbox", style="margin-left: 0px;") }} + </div> + </div> + </div> + + <div class="form-group"> + <label for="{{ form.private.id }}" class="col-sm-3 control-label">{{ form.private.label }}</label> + <div class="col-sm-9"> + <div class="checkbox"> + {{ form.private(class_="checkbox", style="margin-left: 0px;") }} + </div> + <span class="text-muted">{{ _("If checked, articles of this feed won't be available to others and the feed won't be listed on <a href='%(url)s'>your profile page</a>.", url=url_for('user.profile_public', nickname=current_user.nickname) ) }}</span> + <span class="text-muted">{{ _("Check this box if there is a private token in the link of the feed.") }}</span> + </div> + + </div> + + <div class="form-group"> + <label class="col-sm-3 control-label">{{ _("Filters") }}</label> + <div class="col-sm-1"> + <input value="+" type="button" class="form-control" id="add-feed-filter-row" /> + </div> + </div> + <div class="form-inline col-sm-offset-4 col-sm-8" id="filters-container"> + {% if feed %} + {% for filter_ in feed.filters or [] %} + <div class="form-group"> + <input value="-" type="button" class="form-control del-feed-filter-row" /> + <select name="type" class="form-control" > + <option value="simple match" {% if filter_.get("type") == "simple match" %}selected{% endif %}>{{ _("simple match") }}</option> + <option value="regex" {% if filter_.get("type") == "regex" %}selected{% endif %}>{{ _("regex") }}</option> + </select> + <input type="text" class="form-control" value="{{ filter_.get("pattern") }}" name="pattern" /> + <select name="action_on" class="form-control"> + <option value="match" {% if filter_.get("action on") == "match" %}selected{% endif %}>{{ _("match") }}</option> + <option value="no match" {% if filter_.get("action on") == "no match" %}selected{% endif %}>{{ _("no match") }}</option> + </select> + <select name="action" class="form-control"> + <option value="mark as read" {% if filter_.get("action") == "mark as read" %}selected{% endif %}>{{ _("mark as read") }}</option> + <option value="mark as favorite" {% if filter_.get("action") == "mark as favorite" %}selected{% endif %}>{{ _("mark as favorite") }}</option> + </select> + </div> + {% endfor %} + {% endif %} + </div> + + <div class="form-group"> + <div class="col-sm-offset-3 col-sm-9"> + {{ form.submit(class_="btn btn-primary") }} + </div> + </div> + </form> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/emails/account_activation.txt b/newspipe/templates/emails/account_activation.txt new file mode 100644 index 00000000..c7d9c52e --- /dev/null +++ b/newspipe/templates/emails/account_activation.txt @@ -0,0 +1,9 @@ +Hello {{ user.nickname }}, + +Your account has been created. +Click on the following link in order to confirm it: +{{ platform_url }}user/confirm_account/{{ token }} + +The link expires at {{ expire_time.strftime('%Y-%m-%d %H:%M') }}. + +See you, diff --git a/newspipe/templates/emails/new_password.txt b/newspipe/templates/emails/new_password.txt new file mode 100644 index 00000000..1a04a36d --- /dev/null +++ b/newspipe/templates/emails/new_password.txt @@ -0,0 +1,8 @@ +Hello {{ user.nickname }}, + +A new password has been generated at your request: +{{ password }} + +It is advised to replace it as soon as connected to Newspipe. + +See you, diff --git a/newspipe/templates/errors/404.html b/newspipe/templates/errors/404.html new file mode 100644 index 00000000..c64a2be8 --- /dev/null +++ b/newspipe/templates/errors/404.html @@ -0,0 +1,12 @@ +{% extends "layout.html" %} +{% block head %} +{{ super() }} +{% endblock %} +{% block content %} +<div class="container"> + <div class="well"> + <h1>Page Not Found</h1> + <p>What you were looking for is just not there, go to the <a href="{{ url_for('home') }}">home page</a>.</p> + </div> +</div> +{% endblock %} diff --git a/newspipe/templates/errors/500.html b/newspipe/templates/errors/500.html new file mode 100644 index 00000000..417fc0c7 --- /dev/null +++ b/newspipe/templates/errors/500.html @@ -0,0 +1,12 @@ +{% extends "layout.html" %} +{% block head %} +{{ super() }} +{% endblock %} +{% block content %} +<div class="container"> + <div class="well"> + <h1>Internal Server Error</h1> + <p>Something bad just happened! Go to the <a href="{{ url_for('home') }}">home page</a>.</p> + </div> +</div> +{% endblock %} diff --git a/newspipe/templates/feed.html b/newspipe/templates/feed.html new file mode 100644 index 00000000..4246669b --- /dev/null +++ b/newspipe/templates/feed.html @@ -0,0 +1,76 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <div class="well"> + <h2>{{ feed.title }}</h2> + {% if feed.description %} <p>{{ feed.description }}</p> {% endif %} + {% if current_user.is_authenticated %} + <a href="{{ url_for("feed.delete", feed_id=feed.id) }}"><i class="fa fa-times" aria-hidden="true" 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="fa fa-pencil-square-o" aria-hidden="true" title="{{ _('Edit this feed') }}"></i></a> + {% endif %} + </div> + <div class="well"> + <p> + {{ _('This feed contains') }} {{ feed.articles.all()|count }} {{ _('articles') }}.<br /> + {% if category %} + {{ _('This feed is part of category %(category_name)s', category_name=category.name) }}<br /> + {% endif %} + {{ _('Address of the feed') }}: <a href="{{ feed.link }}" target="_blank">{{ feed.link }}</a><br /> + {% if feed.site_link != "" %} + {{ _('Address of the site') }}: <a href="{{ feed.site_link }}" target="_blank">{{ feed.site_link }}</a><br /> + {% endif %} + + <br /> + + {% if feed.last_retrieved %} + {{ _("Last download:") }} {{ feed.last_retrieved | datetime }}<br /> + {% endif %} + + {% if feed.error_count >= conf.DEFAULT_MAX_ERROR %} + <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 %} + + {% if feed.last_error %} + {{ _("Here's the last error encountered while retrieving this feed:") }} <pre>{{ feed.last_error }}</pre><br /> + {% endif %} + + {% if feed.articles.all()|count != 0 %} + {{ _('The last article was posted') }} {{ elapsed.days }} {{ _('day(s) ago.') }}<br /> + {{ _('Daily average') }}: {{ average }}, {{ _('between the') }} {{ first_post_date | datetime }} {{ _('and the') }} {{ end_post_date | datetime }}. + {% endif %} + </p> + </div> + + <div class="row"> + <div class="col-md-12"> + <div class="table-responsive"> + <table id="table-articles" class="table table-striped"> + <thead> + <tr> + <th>{{ _('Article') }}</th> + <th>{{ _('Date') }}</th> + </tr> + </thead> + <tbody> + {% for article in articles %} + <tr> + <td><a href="{{ url_for("article.article_pub", article_id=article.id) }}">{{ article.title }}</a></td> + <td>{{ article.date | datetime }}</td> + </tr> + {% endfor %} + </tbody> + </table> + </div> + </div> + </div> + + <div class="row"> + <div class="col-md-8 offset-md-1"> + {{ pagination.links }} + </div> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/feed_list.html b/newspipe/templates/feed_list.html new file mode 100644 index 00000000..2ce7841f --- /dev/null +++ b/newspipe/templates/feed_list.html @@ -0,0 +1,55 @@ +{% if feeds.count() != 0 %} +<div class="table-responsive"> + <table id="table-feeds" class="table table-striped"> + <thead> + <tr> + <th>#</th> + <th>{{ _('Status') }}</th> + <th>{{ _('Title') }}</th> + <th>{{ _('Site') }}</th> + <th>{{ _('Articles') }}</th> + <th>{{ _('Actions') }}</th> + </tr> + </thead> + <tbody> + {% for feed in feeds %} + <tr {% if not feed.enabled %}class="warning"{% endif %}> + <td>{{ loop.index }}</td> + <td> + {% if feed.enabled %} + <i class="fa fa-eye" aria-hidden="true" title="{{ _('Feed enabled') }}"></i> + {% else %} + <i class="fa fa-eye-slash" aria-hidden="true" title="{{ _('Feed disabled') }}"></i> + {% endif %} + {% if feed.error_count >= application.config['DEFAULT_MAX_ERROR'] %} + <i class="fa fa-exclamation" aria-hidden="true" title="{{ _('Feed encountered too much errors.') }}"></i> + {% endif %} + </td> + <td>{% if feed.icon_url %}<img src="{{ url_for('icon.icon', url=feed.icon_url) }}" width="16px" /> {% endif %}{{ feed.title }}</td> + <td><a href="{{ feed.site_link }}">{{ feed.site_link }}</a></td> + <td>( {{ unread_article_count.get(feed.id, 0) }} ) {{ article_count.get(feed.id, 0) }}</td> + <td> + <a href="{{ url_for("feed.feed", feed_id=feed.id) }}"><i class="fa fa-info" aria-hidden="true" title="{{ _('Information') }}"></i></a> + <a href="{{ url_for("feed.form", feed_id=feed.id) }}"><i class="fa fa-pencil-square-o" aria-hidden="true" title="{{ _('Edit this feed') }}"></i></a> + <a href="{{ url_for("feed.duplicates", feed_id=feed.id) }}"><i class="fa fa-book" aria-hidden="true" title="{{ _('Duplicate articles') }}"></i></a> + <a href="{{ url_for("feed.delete", feed_id=feed.id) }}"><i class="fa fa-times" aria-hidden="true" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a> + </td> + </tr> + {% endfor %} + </tbody> + </table> +</div> +<script> +$(document).ready(function() { + $('#table-feeds').DataTable( { + responsive: true, + columnDefs: [ + { + bSortable: false, + targets: [0, 1, 4, 5] + } + ] + }); +}); +</script> +{% endif %} diff --git a/newspipe/templates/feed_list_per_categories.html b/newspipe/templates/feed_list_per_categories.html new file mode 100644 index 00000000..34d10ddd --- /dev/null +++ b/newspipe/templates/feed_list_per_categories.html @@ -0,0 +1,52 @@ +<div class="row"> + <div class="col-md-8"> + <form class="form-inline"> + <div class="form-group"> + <label>Filter per category</label> + <select class="form-control" id="category-select" name="category_id"> + <option value="0">All</option> + {% for category in user.categories %} + <option value="{{category.id}}" {% if category.id==selected_category_id %}selected{% endif %}>{{ category.name }}</option> + {% endfor %} + </select> + <button type="submit" class="btn btn-primary mb-2">OK</button> + </div> + </form> + </div> +</div> + +<br /> + +<div class="table-responsive"> + <table id="table-feeds" class="table table-striped"> + <thead> + <tr> + <th>#</th> + <th>{{ _('Title') }}</th> + <th>{{ _('Site') }}</th> + </tr> + </thead> + <tbody> + {% for feed in feeds %} + <tr> + <td>{{ loop.index }}</td> + <td>{% if feed.icon_url %}<img src="{{ url_for('icon.icon', url=feed.icon_url) }}" width="16px" /> {% endif %} <a href="{{ url_for('feed.feed_pub', feed_id=feed.id) }}">{{ feed.title }}</a></td> + <td><a href="{{ feed.site_link }}">{{ feed.site_link }}</a></td> + </tr> + {% endfor %} + </tbody> + </table> +</div> +<script> +$(document).ready(function() { + $('#table-feeds').DataTable( { + responsive: true, + columnDefs: [ + { + bSortable: false, + targets: [0] + } + ] + }); +}); +</script> diff --git a/newspipe/templates/feed_list_simple.html b/newspipe/templates/feed_list_simple.html new file mode 100644 index 00000000..5f692a53 --- /dev/null +++ b/newspipe/templates/feed_list_simple.html @@ -0,0 +1,35 @@ +{% if feeds | length != 0 %} +<div class="table-responsive"> + <table id="table-feeds" class="table table-striped"> + <thead> + <tr> + <th>#</th> + <th>{{ _('Title') }}</th> + <th>{{ _('Site') }}</th> + </tr> + </thead> + <tbody> + {% for feed in feeds %} + <tr> + <td>{{ loop.index }}</td> + <td>{% if feed.icon_url %}<img src="{{ url_for('icon.icon', url=feed.icon_url) }}" width="16px" /> {% endif %} <a href="{{ url_for('feed.feed_pub', feed_id=feed.id) }}">{{ feed.title }}</a></td> + <td><a href="{{ feed.site_link }}">{{ feed.site_link }}</a></td> + </tr> + {% endfor %} + </tbody> + </table> +</div> +<script> +$(document).ready(function() { + $('#table-feeds').DataTable( { + responsive: true, + columnDefs: [ + { + bSortable: false, + targets: [0] + } + ] + }); +}); +</script> +{% endif %} diff --git a/newspipe/templates/feeds.html b/newspipe/templates/feeds.html new file mode 100644 index 00000000..805e1b74 --- /dev/null +++ b/newspipe/templates/feeds.html @@ -0,0 +1,7 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <h1>{{ _('You are subscribed to %(feed_count)d feeds.', feed_count=feeds.count()) }} <a href="{{ url_for("feed.form") }}">{{ _('Add') }}</a> {{ _('a feed') }}.</h1> + {% include "feed_list.html" %} +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/history.html b/newspipe/templates/history.html new file mode 100644 index 00000000..ba567106 --- /dev/null +++ b/newspipe/templates/history.html @@ -0,0 +1,26 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <h1>{{ _('History') }}</h1> + {% if month != None %} + <h2><a href="{{ url_for("articles.history", year=year) }}"><span class="fa fa-chevron-left" aria-hidden="true"></span> {{ year }}</a></h2> + <h3>{{ month | month_name }}</h3> + {% elif year != None %} + <h2><a href="{{ url_for("articles.history") }}"><span class="fa fa-chevron-left" aria-hidden="true"></span> {{ _('all years') }}</a></h2> + <h3>{{ year }}</h3> + {% endif %} + <ul class="list-group"> + {% for article in articles_counter | sort(reverse = True) %} + {% if year == None %} + <li class="list-group-item"><a href="{{ url_for("articles.history", year=article) }}">{{ article }}</a> : {{ articles_counter[article] }} articles</li> + {% elif month == None %} + <li class="list-group-item"><a href="{{ url_for("articles.history", year=year, month=article) }}">{{ article | month_name }}</a> : {{ articles_counter[article] }} articles</li> + {% else %} + {% for article in articles %} + <li class="list-group-item">{{ article.date | datetime }} - <a href="/article/{{ article.id }}">{{ article.title | safe }}</a></li> + {% endfor %} + {% endif %} + {% endfor %} + </ul> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/home.html b/newspipe/templates/home.html new file mode 100644 index 00000000..8da8dbe1 --- /dev/null +++ b/newspipe/templates/home.html @@ -0,0 +1,135 @@ +{% extends "layout.html" %} +{% block content %} +<style> + li.feed-commands {display: none; text-align: right;} + li.feed-commands > span > a {margin-right: 10px;} + li.feed-menu:hover + li.feed-commands, li.feed-commands:hover {display: block;} +</style> +<div class="container-fluid"> +{% if feeds|count == 0 %} + <div class="col-md-4 col-md-offset-4"> + <h1>{{ _("You don't have any feeds.") }}</h1> + <h1><a href="{{ url_for('feed.form') }}">{{ _('Add some') }}</a>, {{ _('or') }} <a href="{{ url_for('user.management') }}">{{ _('upload an OPML file.') }}</a></h1> + </div> +{% else %} + <div class="row"> + <div id="sidebar" class="col-2 d-none d-lg-block"> + <ul class="nav flex-column" data-offset-top="0" data-offset-bottom="0" style="min-height: 650px;"> + <li class="nav-item feed-menu"><a class="nav-link" href="{{ gen_url(feed=0) }}"> + {% if not feed_id %}<b>{% endif %} + {{ _('All feeds') }} + <span id="total-unread" class="badge pull-right"> {% if nb_unread > 1000 %}>{% endif %} {{ articles.__len__() }}</span> + {% if not feed_id %}</b>{% endif %} + </a></li> + <li class="nav-item feed-commands"><span class="nav-link"> + <a href="{{ url_for('article.mark_as', new_value='read') }}"><i class="fa fa-check-square-o" aria-hidden="true" title="{{ _('Mark all as read') }}"></i></a> + </span></li> + {% for fid, nbunread in unread|dictsort(by='value')|reverse %} + <li class="nav-item feed-menu"><a class="nav-link" href="{{ gen_url(feed=fid) }}"> + {% if feed_id == fid %}<b>{% endif %} + {% if in_error.get(fid, 0) > 0 %} + <span style="background-color: {{ "red" if in_error[fid] > 2 else "orange" }} ;" class="badge pull-right" title="Some errors occured while trying to retrieve that feed.">{{ in_error[fid] }}</span> + {% endif %} + <span id="unread-{{ fid }}" class="badge pull-right">{{ nbunread }}</span> + <!-- <img src="{{ url_for('icon.icon', url=feeds[fid].url) }}" width="16px"> --> + {{ feeds[fid].title | safe | truncate(25, True) }} + {% if feed_id == fid %}</b>{% endif %} + </a></li> + <li class="nav-item feed-commands"><span class="nav-link"> + <a href="/feed/{{ fid }}"><i class="fa fa-info" aria-hidden="true" title="{{ _('Details') }}"></i></a> + <a href="{{ url_for('feed.form', feed_id=fid) }}"><i class="fa fa-pencil-square-o" aria-hidden="true" title="{{ _('Edit this feed') }}"></i></a> + <a href="{{ url_for('article.mark_as', new_value='read', feed_id=fid) }}"><i class="fa fa-check-square-o" aria-hidden="true" title="{{ _('Mark this feed as read') }}"></i></a> + <a href="{{ url_for('article.mark_as', new_value='unread', feed_id=fid) }}"><i class="fa fa-square-o" aria-hidden="true" title="{{ _('Mark this feed as unread') }}"></i></a> + <a href="{{ url_for('feed.delete', feed_id=fid) }}"><i class="fa fa-times" aria-hidden="true" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a> + </span></li> + {% endfor %} + + {% for fid, feed in feeds.items() if not fid in unread %} + <li class="nav-item feed-menu"><a class="nav-link" href="{{ gen_url(feed=fid) }}"> + {% if in_error.get(fid, 0) > 0 %} + <span style="background-color: {{ "red" if in_error[fid] > 2 else "orange" }} ;" class="badge pull-right" title="Some errors occured while trying to retrieve that feed.">{{ in_error[fid] }}</span> + {% endif %} + {% if feed_id == fid %}<b>{% endif %} + <!-- <img src="{{ url_for('icon.icon', url=feeds[fid].url) }}" width="16px"> --> + {{ feed.title | safe | truncate(25, True) }} + {% if feed_id == fid %}</b>{% endif %} + </a></li> + <li class="nav-item feed-commands"><span class="nav-link"> + <a href="/feed/{{ fid }}"><i class="fa fa-info" aria-hidden="true" title="{{ _('Details') }}"></i></a> + <a href="{{ url_for('feed.form', feed_id=fid) }}"><i class="fa fa-pencil-square-o" aria-hidden="true" title="{{ _('Edit this feed') }}"></i></a> + <a href="{{ url_for('article.mark_as', new_value='read', feed_id=fid) }}"><i class="fa fa-check-square-o" aria-hidden="true" title="{{ _('Mark this feed as read') }}"></i></a> + <a href="{{ url_for('article.mark_as', new_value='unread', feed_id=fid) }}"><i class="fa fa-square-o" aria-hidden="true" title="{{ _('Mark this feed as unread') }}"></i></a> + <a href="{{ url_for('feed.delete', feed_id=fid) }}"><i class="fa fa-times" aria-hidden="true" title="{{ _('Delete this feed') }}" onclick="return confirm('{{ _('You are going to delete this feed.') }}');"></i></a> + </span></li> + {% endfor %} + </ul> + </div> + <div class="col-md-12 col-lg-10"> + <div id="filters" data-filter="{{ filter_ }}"> + <ul class="nav nav-tabs ml-auto"> + <li id="tab-all" class="nav-item"> + <a class="nav-link {% if filter_ == 'all' %}active{% endif %}" href="{{ gen_url(filter_='all') }}">{{ _('All') }}</a> + </li> + <li id="tab-read" class="nav-item"> + <a class="nav-link {% if filter_ == 'read' %}active{% endif %}" href="{{ gen_url(filter_='read') }}">{{ _('Read') }}</a> + </li> + <li id="tab-unread" class="nav-item"> + <a class="nav-link {% if filter_ == 'unread' %}active{% endif %}" href="{{ gen_url(filter_='unread') }}">{{ _('Unread') }}</a> + </li> + <li id="tab-unread" class="px-5 nav-item"> + <a class="nav-link {% if liked %}active{% endif %}" href="{{ gen_url(liked=not liked) }}"><i class="fa fa-heart" aria-hidden="true"></i> {{ _('Liked') }}</a> + </li> + <li id="tab-nbdisplay" class="px-5 nav-item d-none d-lg-block"> + <div id="nbdisplay"> + <a href="{{ gen_url(limit=10) }}" class="badge {% if limit == 10 %}badge-primary{% else %}badge-info{% endif %}">{{ _(10) }}</a> + <a href="{{ gen_url(limit=100) }}" class="badge {% if limit == 100 %}badge-primary{% else %}badge-info{% endif %}">{{ _(100) }}</a> + <a href="{{ gen_url(limit=1000) }}" class="badge {% if limit == 1000 %}badge-primary{% else %}badge-info{% endif %}">{{ _(1000) }}</a> + <a href="{{ gen_url(limit='all') }}" class="badge {% if limit == 'all' %}badge-primary{% else %}badge-info{% endif %}">{{ _('All') }}</a> + </div> + </li> + </ul> + </div> + {% if articles | count != 0%} + <div class="table-responsive"> + <table class="table table-striped strict-table"> + <thead> + <tr> + <th></th> + <th>{{ _('Feed') }}</th> + <th>{{ _('Article') }}</th> + <th>{{ _('Date') }}</th> + </tr> + </thead> + <tbody> + {% for article in articles %} + <tr data-article="{{ article.id }}" data-feed="{{ article.feed_id }}"> + <td> + <a href="#"><i class="fa fa-times" aria-hidden="true" title="{{ _('Delete this article') }}"></i></a> + {% if article.like %} + <a href="#"><i class="fa fa-star" aria-hidden="true" title="{{ _('One of your favorites') }}"></i></a> + {% else %} + <a href="#"><i class="fa fa-star-o" aria-hidden="true" title="{{ _('Click if you like this article') }}"></i></a> + {% endif %} + {% if article.readed %} + <a href="#"><i class="fa fa-square-o readed" aria-hidden="true" title="{{ _('Mark this article as unread') }}"></i></a> + {% else %} + <a href="#"><i class="fa fa-check-square-o readed" aria-hidden="true" title="{{ _('Mark this article as read') }}"></i></a> + {% if filter_ == 'all' %}</b>{% endif %} + {% endif %} + </td> + <td><a href="/article/redirect/{{ article.id}}" target="_blank">{{ article.source.title | safe }}</a></td> + <td {%if filter_ == 'all' and article.readed == False %}style='font-weight:bold'{% endif %}> + <a href="/article/{{ article.id }}">{{ article.title | safe }}</a> + </td> + <td class="date">{{ article.date | datetime }}</a></td> + </tr> + {% endfor %} + </tbody> + </table> + </div> + {% endif %} + </div> + </div> +{% endif %} +</div> +{% endblock %} diff --git a/newspipe/templates/inactives.html b/newspipe/templates/inactives.html new file mode 100644 index 00000000..e89a5fe1 --- /dev/null +++ b/newspipe/templates/inactives.html @@ -0,0 +1,26 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <div class="well"> + <form method=get action="{{ url_for("feeds.inactives") }}"> + <p>{{ _('Days of inactivity') }}:</p> + <input type="number" name="nb_days" class="form-control" value="{{ nb_days }}" min="0" max="1000000" step="1" size="4" style="text-align: center" /> + </form> + <br /> + {% if inactives != [] %} + <ul class="list-group"> + {% for feed, delta in inactives %} + <li class="list-group-item"> + <a href="{{ url_for('feed.feed', feed_id=feed.id) }}"> + {% if feed.icon %}<img src="{{ url_for('feed.icon', feed_id=feed.id) }}" width="16px" />{% endif %} + {{ feed.title }} + </a> - {{ delta.days }} {{ _('days') }} + </li> + {% endfor %} + </ul> + {% else %} + <p>{{ _('No inactive feeds.') }}<p> + {% endif %} + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/layout.html b/newspipe/templates/layout.html new file mode 100644 index 00000000..b40b540c --- /dev/null +++ b/newspipe/templates/layout.html @@ -0,0 +1,140 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + {% block head %} + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta name="description" content="Newspipe is a web news aggregator and reader." /> + <meta name="author" content="" /> + <title>Newspipe{% if head_titles %} - {{ ' - '.join(head_titles) }}{% endif %}</title> + <link rel="shortcut icon" href="{{ url_for("static", filename="img/favicon.ico") }}" /> + <!-- CSS --> + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='npm_components/bootstrap/dist/css/bootstrap.min.css') }}" media="screen" /> + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='npm_components/datatables.net-bs4/css/dataTables.bootstrap4.min.css') }}" /> + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='npm_components/fork-awesome/css/fork-awesome.min.css') }}" /> + <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/custom.css') }}" /> + <!-- JavaScript --> + <script type="text/javascript" src="{{ url_for('static', filename = 'npm_components/jquery/dist/jquery.min.js') }}"></script> + <script type="text/javascript" src="{{ url_for('static', filename = 'npm_components/bootstrap/dist/js/bootstrap.min.js') }}"></script> + <script type="text/javascript" src="{{ url_for('static', filename='npm_components/datatables.net/js/jquery.dataTables.min.js') }}"></script> + <script type="text/javascript" src="{{ url_for('static', filename='npm_components/datatables.net-bs4/js/dataTables.bootstrap4.min.js') }}"></script> + {% endblock %} + </head> + <body> + {% block menu %} + <nav class="navbar navbar-expand-lg navbar-dark bg-newspipe-blue"> + <a class="navbar-brand" href="/">🗞 Newspipe</a> + <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> + <span class="navbar-toggler-icon"></span> + </button> + {% block menu_links %} + <div class="collapse navbar-collapse" id="navbarSupportedContent"> + + <ul class="navbar-nav mr-auto w-100 justify-content-end"> + {% if current_user.is_authenticated %} + {% if current_user.is_admin %} + <li class="nav-item"> + <a class="nav-link" href="{{ url_for('fetch') }}" title="{{ _('Fetch') }}"><i class="fa fa-download" aria-hidden="true"></i> {{ _('Fetch') }}</a> + </li> + {% endif %} + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownRSS" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-rss" aria-hidden="true"></i> {{ _('Feeds') }}</a> + <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownRSS"> + <a class="dropdown-item" href="{{ url_for('feeds.feeds') }}">{{ _('Subscribed') }}</a> + <a class="dropdown-item" href="{{ url_for('feeds.inactives') }}">{{ _('Inactive') }}</a> + <a class="dropdown-item" href="{{ url_for('popular') }}">{{ _('Popular') }}</a> + <a class="dropdown-item" href="{{ url_for('articles.history') }}">{{ _('History') }}</a> + <div class="dropdown-divider"></div> + <a class="dropdown-item" href="{{ url_for('feed.form') }}">{{ _('Add a new feed') }}</a> + <form class="navbar-form navbar-left px-4 py-3" action="{{ url_for('feed.bookmarklet') }}"> + <label class="sr-only" for="inlineFormInputGroupAPIKey">{{ _('Add a new feed') }}</label> + <div class="input-group input-group-inline"> + <div class="input-group-prepend"> + <input class="form-control" name="url" type="url" placeholder="{{_('Site or feed url')}}" required="required"/> + </div> + <button type="submit" class="btn btn-primary"><i class="fa fa-plus" aria-hidden="true"></i></button> + </div> + </form> + </div> + </li> + + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownCategory" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-tag" aria-hidden="true"></i> {{ _('Categories') }}</a> + <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownCategory"> + <a class="dropdown-item" href="{{ url_for('categories.list_') }}">{{ _('Categories') }}</a> + <div class="dropdown-divider"></div> + <a class="dropdown-item" href="{{ url_for('category.form') }}">{{ _('Add a new category') }}</a> + <form class="navbar-form navbar-left px-4 py-3" action="{{ url_for('category.form') }}" method="POST" name="category"> + <label class="sr-only" for="inlineFormInputGroupAPIKey">{{ _('Add a new category') }}</label> + <div class="input-group input-group-inline"> + <div class="input-group-prepend"> + <input class="form-control" name="name" type="text" placeholder="{{_('Category name')}}" required="required"/> + </div> + <button type="submit" class="btn btn-primary"><i class="fa fa-plus" aria-hidden="true"></i></button> + </div> + </form> + </div> + </li> + + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownBookmark" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-bookmark" aria-hidden="true"></i> {{ _('Bookmarks') }}</a> + <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownBookmark"> + <a class="dropdown-item" href="{{ url_for('bookmarks.list_') }}">{{ _('Bookmarks') }}</a> + <a class="dropdown-item" href="{{ url_for('bookmark.form') }}">{{ _('Add a new bookmark') }}</a> + </div> + </li> + + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownUser" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-user" aria-hidden="true"></i></a> + <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownUser"> + <a class="dropdown-item" href="{{ url_for('user.profile') }}">{{ _('Profile') }}</a> + <a class="dropdown-item" href="{{ url_for('user.management') }}">{{ _('Your data') }}</a> + {% if current_user.is_admin %} + <div class="dropdown-divider"></div> + <a class="dropdown-item" href="{{ url_for('admin.dashboard') }}">{{ _('Dashboard') }}</a> + <div class="dropdown-divider"></div> + {% endif %} + <a class="dropdown-item" href="{{ url_for('about') }}">{{ _('About') }}</a> + <a class="dropdown-item" href="{{ url_for('logout') }}" title="{{ _('Logout') }}">Logout</a> + </div> + </li> + {% else %} + <li class="nav-item"> + <a class="nav-link" href="{{ url_for('bookmarks.list_') }}" title="{{ _('Recent bookmarks') }}">{{ _('Recent bookmarks') }}</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="{{ url_for('popular') }}" title="{{ _('Popular feeds') }}">{{ _('Popular feeds') }}</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="{{ url_for('about') }}" title="{{ _('About') }}">{{ _('About') }}</a> + </li> + {% endif %} + </ul> + </div> + {% endblock %} + </nav> + {% endblock %} + <br /> + + <div class="container alert-message not-at-home"> + {% block messages %} + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + <div class="alert alert-{{category}}"> + <button type="button" class="close" data-dismiss="alert">×</button> + {{ message }} + </div> + {% endfor %} + {% endif %} + {% endwith %} + {% endblock %} + </div> + + {% block content %}{% endblock %} + + <!-- Placed at the end of the document so the pages load faster --> + <script type="text/javascript" src="{{ url_for('static', filename = 'js/articles.js') }}"></script> + <script type="text/javascript" src="{{ url_for('static', filename = 'js/feed.js') }}"></script> + </body> +</html> diff --git a/newspipe/templates/login.html b/newspipe/templates/login.html new file mode 100644 index 00000000..1253e2d3 --- /dev/null +++ b/newspipe/templates/login.html @@ -0,0 +1,32 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <div class="row"> + <div class="col"> + <div class="card"> + <div class="card-body"> + <h2>{{ _('Log In') }}</h2> + <form action="{{ url_for('login') }}" method=post> + {{ form.hidden_tag() }} + <div class="form-group"> + {{ form.nickmane(class_="form-control", placeholder=_('Your nickname')) }} + </div> + {% for message in form.nickmane.errors %} + <div class="alert alert-warning" role="alert">{{ message }}</div> + {% endfor %} + <div class="form-group"> + {{ form.password(class_="form-control", placeholder=_('Your Password')) }} + </div> + {% for message in form.password.errors %} + <div class="alert alert-warning" role="alert">{{ message }}</div> + {% endfor %} + {{ form.submit(class_="btn btn-primary") }} + </form> + </div> + </div> + <br /> + <a href="/signup" class="btn btn-primary">{{ _('Sign up') }}</a> + </div> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/management.html b/newspipe/templates/management.html new file mode 100644 index 00000000..22ebde8a --- /dev/null +++ b/newspipe/templates/management.html @@ -0,0 +1,99 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <div class="card"> + <div class="card-body"> + <div class="row"> + <div class="col"> + <h2>{{ _('Your subscriptions') }}</h2> + <p>{{ _('You are subscribed to') }} {{ nb_feeds }} <a href="/feeds">{{ _('feeds') }}</a>. <a href="{{ url_for("feed.form") }}">{{ _('Add') }}</a> {{ _('a feed') }}.</p> + <p>{{ nb_articles }} {{ _('articles are stored in the database with') }} {{ nb_unread_articles }} {{ _('unread articles') }}.</p> + <p>{{ _('You have') }} {{ nb_categories }} <a href="{{ url_for("categories.list_")}}">{{ _('categories') }}</a>.</p> + <a href="{{ url_for("articles.expire", weeks=10) }}" class="btn btn-primary" onclick="return confirm('{{ _('You are going to delete old articles.') }}');">{{ _('Delete articles older than 10 weeks') }}</a> + </div> + <div class="col"> + <h2>{{ _('Your bookmarks') }}</h2> + <p>{{ _('You have') }} {{ nb_bookmarks }} <a href="{{ url_for("bookmarks.list_")}}">{{ _('bookmarks') }}</a>.</p> + <a href="{{ url_for("bookmarks.delete_all") }}" class="btn btn-primary" onclick="return confirm('{{ _('You are going to delete all bookmarks.') }}');">{{ _('Delete all bookmarks') }}</a> + </div> + </div> + </div> + </div> + <br /> + <div class="card"> + <div class="card-body"> + <div class="row"> + <div class="col"> + <h2>{{ _('Your data') }}</h2> + <br /> + </div> + </div> + <div class="row"> + <div class="col"> + <h3>{{ _('Articles') }}</h3> + </div> + </div> + <div class="row"> + <div class="col"> + <h5>{{ _('Import') }}</h5> + <p>{{ _('Import a Newspipe account (*.json).') }}</p> + <form action="" method="post" id="formImportJSON" enctype="multipart/form-data"> + <input type="file" name="jsonfile" /> + <button class="btn btn-primary" type="submit">OK</button> + </form> + </div> + <div class="col"> + <h5>{{ _('Export') }}</h5> + <p>{{ _('Export your newspipe account to JSON.') }}</p> + <a href="{{ url_for('articles.export') }}" class="btn btn-primary">{{ _('Export') }}</a> + </div> + </div> + <br /> + <div class="row"> + <div class="col"> + <h3 id="import">{{ _('Feeds') }}</h3> + </div> + </div> + <div class="row"> + <div class="col"> + <h5>{{ _('Import') }}</h5> + <p>{{ _('Import feeds from OPML (*.xml or *.opml).') }}</p> + <form action="" method="post" id="formImportOPML" enctype="multipart/form-data"> + <input type="file" name="opmlfile" /> + <button class="btn btn-primary" type="submit">OK</button> + </form> + </div> + <div class="col"> + <h5>{{ _('Export') }}</h5> + <p>{{ _('Export feeds to OPML.') }}</p> + <form class="form" action="{{ url_for('feeds.export') }}" method="GET" id="formExportOPML"> + <div class="form-group"> + <div class="input-group"> + <label>Include disabled feeds</label> + <input type="checkbox" class="form-control" name="includedisabled" checked /> + </div> + <div class="input-group"> + <label title="Newspipe encountered too much problems when retrieving these feeds.">Include dead feeds</label> + <input type="checkbox" class="form-control" name="includeexceedederrorcount" checked /> + </div> + <div class="input-group"> + <label>Include private feeds</label> + <input type="checkbox" class="form-control" name="includeprivate" checked /> + </div> + </div> + <button class="btn btn-primary" type="submit">{{ _('Export') }}</button> + </form> + </div> + </div> + + <div class="row"> + <div class="col-md-12"> + <br /> + <h2>{{ _('Bookmarks') }}</h2> + <a href="{{ url_for('bookmarks.export') }}" class="btn btn-primary">{{ _('Export your bookmarks to JSON') }}</a> + </div> + </div> + </div> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/opml.xml b/newspipe/templates/opml.xml new file mode 100644 index 00000000..7159e279 --- /dev/null +++ b/newspipe/templates/opml.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- OPML generated by Newspipe on {{ now | datetime }} --> +<opml version="1.1"> + <head> + <title>Feeds of {{ user.nickname }}</title> + <dateCreated>{{ now | datetime }}</dateCreated> + <dateModified>{{ now | datetime }}</dateModified> + <ownerName>{{ user.nickname }}</ownerName> + </head> + <body> + {% for feed in feeds %} <outline title="{{ feed.title|escape }}" text="{{ feed.title|escape }}" description="{{ feed.description|escape }}" {% if feed.category_id != None %}category="/{{ categories[feed.category_id].name }}"{% endif %} xmlUrl="{{ feed.link|escape }}" htmlUrl="{{ feed.site_link|escape }}" /> + {% endfor %}</body> +</opml> diff --git a/newspipe/templates/popular.html b/newspipe/templates/popular.html new file mode 100644 index 00000000..a80cee57 --- /dev/null +++ b/newspipe/templates/popular.html @@ -0,0 +1,27 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <div class="row"> + <div class="col-md-12"> + <h1>{{ _('Popular feeds') }}</h1> + <a href="{{ url_for('popular', nb_days='all') }}">all</a> ‧ <a href="{{ url_for('popular', nb_days=365) }}">last year</a> ‧ <a href="{{ url_for('popular', nb_days=31) }}">last month</a> + </div> + </div> + <div class="row"> + <div class="col-md-12"> + <ul class="list-group"> + {% for feed in popular %} + <li class="list-group-item d-flex justify-content-between align-items-center"> + <a href="{{ feed[0] }}">{{ feed[0] }}</a> + <a href="{{ url_for('feed.bookmarklet', url=feed[0]) }}" > + <span class="fa fa-plus text-muted" aria-hidden="true" title="follow this feed"></span> + <span class="text-muted">{{ _('add this feed') }}</span> + </a> + <span class="badge badge-primary badge-pill">{{ feed[1] }}</span> + </li> + {% endfor %} + </ul> + </div> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/profile.html b/newspipe/templates/profile.html new file mode 100644 index 00000000..edbae368 --- /dev/null +++ b/newspipe/templates/profile.html @@ -0,0 +1,68 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <h2>{{ _('Your Profile') }}</h2> + <div class="row"> + <div class="col"> + <p>{{ _('Member since') }}: {{ user.date_created | datetime }}.</p> + <p>{{ _('Last seen') }}: {{ user.last_seen | datetime }}.</p> + </div> + </div> + <div class="card"> + <div class="card-body"> + <div class="row"> + <div class="col"> + <h3>Edit your profile</h3> + </div> + </div> + <form action="" method="post" name="save"> + <div class="row"> + {{ form.hidden_tag() }} + + <div class="col"> + {{ form.nickname.label }} + {{ form.nickname(class_="form-control") }} {% for error in form.nickname.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + + {{ form.password.label }} + {{ form.password(class_="form-control") }} {% for error in form.password.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + + {{ form.password_conf.label }} + {{ form.password_conf(class_="form-control") }} {% for error in form.password_conf.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + </div> + + <div class="col"> + {{ form.bio.label }} + {{ form.bio(class_="form-control") }} {% for error in form.bio.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + + {{ form.webpage.label }} + {{ form.webpage(class_="form-control") }} {% for error in form.webpage.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + + {{ form.twitter.label }} + {{ form.twitter(class_="form-control") }} {% for error in form.twitter.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + + {{ form.is_public_profile.label }} + {{ form.is_public_profile(class_="form-control") }} {% for error in form.is_public_profile.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + <p>{{ _('Your profile will be available <a href="%(url)s">here</a>.', url=url_for('user.profile_public', nickname=user.nickname) ) }}</p> + + {{ form.automatic_crawling.label }} + {{ form.automatic_crawling(class_="form-control") }} {% for error in form.automatic_crawling.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + <p>{{ _('Uncheck if you are using your own crawler.') }}</p> + </div> + </div> + <div class="row"> + <div class="col"> + <br /> + {{ form.submit(class_="btn btn-primary") }} + </div> + </div> + </form> + </div> + </div> + <br /> + <div class="row"> + <div class="col"> + <a href="/delete_account" class="btn btn-warning" onclick="return confirm('{{ _('You are going to delete your account.') }}');">{{ _('Delete your account') }}</a> + </div> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/profile_public.html b/newspipe/templates/profile_public.html new file mode 100644 index 00000000..9ba3578c --- /dev/null +++ b/newspipe/templates/profile_public.html @@ -0,0 +1,45 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <h1>{{ user.nickname }} / <a href="{{ url_for('user.user_stream', nickname=user.nickname) }}">stream</a></h1> + <div class="row"> + <div class="col-md-12"> + <p> + <span class="fa fa-clock-o" aria-hidden="true"></span> + {{ _('Member since') }}: {{ user.date_created | datetime }} + </p> + <p> + <span class="fa fa-clock-o" aria-hidden="true"></span> + {{ _('Last seen') }}: {{ user.last_seen | datetime }} + </p> + {% if user.webpage %} + <p> + <span class="fa fa-home" aria-hidden="true"></span> + {{ _('Webpage') }}: <a href="{{ user.webpage | safe }}">{{ user.webpage | safe }}</a> + </p> + {% endif %} + {% if user.twitter %} + <p> + <span class="fa fa-twitter" aria-hidden="true"></span> + {{ _('Twitter') }}: <a href="{{ user.twitter | safe }}">{{ user.twitter | safe }}</a> + </p> + {% endif %} + </div> + </div> + <div class="row"> + <div class="col-md-5"> + {% if user.bio %} + <p align="justify">{{ user.bio }}</p> + {% endif %} + </div> + <div class="col-md-6 pull-right"></div> + </div> + + <h2>{{ _('Feeds') }}</h2> + <div class="row"> + <div class="col-md-12"> + {% include "feed_list_per_categories.html" %} + </div> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/signup.html b/newspipe/templates/signup.html new file mode 100644 index 00000000..8d34b3bf --- /dev/null +++ b/newspipe/templates/signup.html @@ -0,0 +1,24 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <div class="form well"> + <form action="" method="post" name="save"> + {{ form.hidden_tag() }} + <div class="form-group"> + {{ form.nickname(class_="form-control", placeholder=_('Your nickname')) }} {% for error in form.nickname.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + <p class="help-block">{{ _('Letters, numbers, dots and underscores only.') }}</p> + </div> + <div class="form-group"> + {{ form.email(class_="form-control", placeholder=_('Your email')) }} {% for error in form.email.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + <p class="help-block">{{ _("Only for account activation. Your email won't be stored.") }}</p> + </div> + <div class="form-group"> + {{ form.password(class_="form-control", placeholder=_('Your password')) }} {% for error in form.password.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %} + <p class="help-block">{{ _('Minimum 6 characters.') }}</p> + </div> + <br /> + {{ form.submit(class_="btn btn-default") }} + </form> + </div> +</div><!-- /.container --> +{% endblock %} diff --git a/newspipe/templates/user_stream.html b/newspipe/templates/user_stream.html new file mode 100644 index 00000000..b05376a8 --- /dev/null +++ b/newspipe/templates/user_stream.html @@ -0,0 +1,70 @@ +{% extends "layout.html" %} +{% block content %} +<div class="container"> + <div class="row"> + <div class="col-md-8"> + <form class="form-inline"> + <div class="form-group"> + <label>Filter per category</label> + <select class="form-control" id="category-select" name="category_id"> + <option value="0">All</option> + {% for cur_category in user.categories %} + <option value="{{cur_category.id}}" {% if cur_category.id==category.id %}selected{% endif %}>{{ cur_category.name }}</option> + {% endfor %} + </select> + <button type="submit" class="btn btn-primary mb-2">OK</button> + </div> + </form> + </div> + </div> + + <br /><br /> + + {% if category %} + <div class="row"> + <div class="col-md-8 offset-md-1"> + <p class="lead">Articles from the category <a href="{{ url_for('user.profile_public', nickname=user.nickname, category_id=category.id) }}">{{ category.name }}</a></p> + </div> + </div> + {% endif %} + + <div class="row"> + <div class="col-md-8 offset-md-1"> + {{ pagination.info }} + </div> + </div> + + <div class="row"> + <div class="col-md-8 offset-md-1"> + {{ pagination.links }} + </div> + </div> + + <div class="table-responsive"> + <table id="table-feeds" class="table table-striped"> + <thead> + <tr> + <th>#</th> + <th>{{ _('Title') }}</th> + <th>{{ _('Published at') }}</th> + </tr> + </thead> + <tbody> + {% for article in articles %} + <tr> + <td>{{ loop.index }}</td> + <td><a href="{{ url_for('article.article_pub', article_id=article.id) }}">{{ article.title }}</a></td> + <td>{{ article.date }}</td> + </tr> + {% endfor %} + </tbody> + </table> + </div> + + <div class="row"> + <div class="col-md-8 offset-md-1"> + {{ pagination.links }} + </div> + </div> +</div><!-- /.container --> +{% endblock %} |