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/web | |
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/web')
81 files changed, 95 insertions, 3222 deletions
diff --git a/newspipe/web/controllers/__init__.py b/newspipe/web/controllers/__init__.py deleted file mode 100644 index 9b193cc5..00000000 --- a/newspipe/web/controllers/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -from .feed import FeedController -from .category import CategoryController -from .article import ArticleController -from .user import UserController -from .icon import IconController -from .bookmark import BookmarkController -from .tag import BookmarkTagController - - -__all__ = [ - "FeedController", - "CategoryController", - "ArticleController", - "UserController", - "IconController", - "BookmarkController", - "BookmarkTagController", -] diff --git a/newspipe/web/controllers/abstract.py b/newspipe/web/controllers/abstract.py deleted file mode 100644 index 9d9e84f2..00000000 --- a/newspipe/web/controllers/abstract.py +++ /dev/null @@ -1,177 +0,0 @@ -import logging -import dateutil.parser -from bootstrap import db -from datetime import datetime -from collections import defaultdict -from sqlalchemy import or_, func -from werkzeug.exceptions import Forbidden, NotFound - -logger = logging.getLogger(__name__) - - -class AbstractController: - _db_cls = None # reference to the database class - _user_id_key = "user_id" - - def __init__(self, user_id=None, ignore_context=False): - """User id is a right management mechanism that should be used to - filter objects in database on their denormalized "user_id" field - (or "id" field for users). - Should no user_id be provided, the Controller won't apply any filter - allowing for a kind of "super user" mode. - """ - try: - self.user_id = int(user_id) - except TypeError: - self.user_id = user_id - - def _to_filters(self, **filters): - """ - Will translate filters to sqlalchemy filter. - This method will also apply user_id restriction if available. - - each parameters of the function is treated as an equality unless the - name of the parameter ends with either "__gt", "__lt", "__ge", "__le", - "__ne", "__in" ir "__like". - """ - db_filters = set() - for key, value in filters.items(): - if key == "__or__": - db_filters.add(or_(*self._to_filters(**value))) - elif key.endswith("__gt"): - db_filters.add(getattr(self._db_cls, key[:-4]) > value) - elif key.endswith("__lt"): - db_filters.add(getattr(self._db_cls, key[:-4]) < value) - elif key.endswith("__ge"): - db_filters.add(getattr(self._db_cls, key[:-4]) >= value) - elif key.endswith("__le"): - db_filters.add(getattr(self._db_cls, key[:-4]) <= value) - elif key.endswith("__ne"): - db_filters.add(getattr(self._db_cls, key[:-4]) != value) - elif key.endswith("__in"): - db_filters.add(getattr(self._db_cls, key[:-4]).in_(value)) - elif key.endswith("__contains"): - db_filters.add(getattr(self._db_cls, key[:-10]).contains(value)) - elif key.endswith("__like"): - db_filters.add(getattr(self._db_cls, key[:-6]).like(value)) - elif key.endswith("__ilike"): - db_filters.add(getattr(self._db_cls, key[:-7]).ilike(value)) - else: - db_filters.add(getattr(self._db_cls, key) == value) - return db_filters - - def _get(self, **filters): - """ Will add the current user id if that one is not none (in which case - the decision has been made in the code that the query shouldn't be user - dependent) and the user is not an admin and the filters doesn't already - contains a filter for that user. - """ - if ( - self._user_id_key is not None - and self.user_id - and filters.get(self._user_id_key) != self.user_id - ): - filters[self._user_id_key] = self.user_id - return self._db_cls.query.filter(*self._to_filters(**filters)) - - def get(self, **filters): - """Will return one single objects corresponding to filters""" - obj = self._get(**filters).first() - - if obj and not self._has_right_on(obj): - raise Forbidden( - { - "message": "No authorized to access %r (%r)" - % (self._db_cls.__class__.__name__, filters) - } - ) - if not obj: - raise NotFound( - {"message": "No %r (%r)" % (self._db_cls.__class__.__name__, filters)} - ) - return obj - - def create(self, **attrs): - assert attrs, "attributes to update must not be empty" - if self._user_id_key is not None and self._user_id_key not in attrs: - attrs[self._user_id_key] = self.user_id - assert ( - self._user_id_key is None - or self._user_id_key in attrs - or self.user_id is None - ), "You must provide user_id one way or another" - - obj = self._db_cls(**attrs) - db.session.add(obj) - db.session.flush() - db.session.commit() - return obj - - def read(self, **filters): - return self._get(**filters) - - def update(self, filters, attrs, return_objs=False, commit=True): - assert attrs, "attributes to update must not be empty" - result = self._get(**filters).update(attrs, synchronize_session=False) - if commit: - db.session.flush() - db.session.commit() - if return_objs: - return self._get(**filters) - return result - - def delete(self, obj_id): - obj = self.get(id=obj_id) - db.session.delete(obj) - try: - db.session.commit() - except Exception as e: - db.session.rollback() - return obj - - def _has_right_on(self, obj): - # user_id == None is like being admin - if self._user_id_key is None: - return True - return ( - self.user_id is None - or getattr(obj, self._user_id_key, None) == self.user_id - ) - - def _count_by(self, elem_to_group_by, filters): - if self.user_id: - filters["user_id"] = self.user_id - return dict( - db.session.query(elem_to_group_by, func.count("id")) - .filter(*self._to_filters(**filters)) - .group_by(elem_to_group_by) - .all() - ) - - @classmethod - def _get_attrs_desc(cls, role, right=None): - result = defaultdict(dict) - if role == "admin": - columns = cls._db_cls.__table__.columns.keys() - else: - assert role in {"base", "api"}, "unknown role %r" % role - assert right in {"read", "write"}, ( - "right must be 'read' or 'write' with role %r" % role - ) - columns = getattr(cls._db_cls, "fields_%s_%s" % (role, right))() - for column in columns: - result[column] = {} - db_col = getattr(cls._db_cls, column).property.columns[0] - try: - result[column]["type"] = db_col.type.python_type - except NotImplementedError: - if db_col.default: - result[column]["type"] = db_col.default.arg.__class__ - if column not in result: - continue - if issubclass(result[column]["type"], datetime): - result[column]["default"] = datetime.utcnow() - result[column]["type"] = lambda x: dateutil.parser.parse(x) - elif db_col.default: - result[column]["default"] = db_col.default.arg - return result diff --git a/newspipe/web/controllers/article.py b/newspipe/web/controllers/article.py deleted file mode 100644 index d5efcb74..00000000 --- a/newspipe/web/controllers/article.py +++ /dev/null @@ -1,107 +0,0 @@ -import re -import logging -import sqlalchemy -from sqlalchemy import func -from collections import Counter - -from bootstrap import db -from .abstract import AbstractController -from lib.article_utils import process_filters -from web.controllers import CategoryController, FeedController -from web.models import Article - -logger = logging.getLogger(__name__) - - -class ArticleController(AbstractController): - _db_cls = Article - - def challenge(self, ids): - """Will return each id that wasn't found in the database.""" - for id_ in ids: - if self.read(**id_).first(): - continue - yield id_ - - def count_by_category(self, **filters): - return self._count_by(Article.category_id, filters) - - def count_by_feed(self, **filters): - return self._count_by(Article.feed_id, filters) - - def count_by_user_id(self, **filters): - return dict( - db.session.query(Article.user_id, func.count(Article.id)) - .filter(*self._to_filters(**filters)) - .group_by(Article.user_id) - .all() - ) - - def create(self, **attrs): - # handling special denorm for article rights - assert "feed_id" in attrs, "must provide feed_id when creating article" - feed = FeedController(attrs.get("user_id", self.user_id)).get( - id=attrs["feed_id"] - ) - if "user_id" in attrs: - assert feed.user_id == attrs["user_id"] or self.user_id is None, ( - "no right on feed %r" % feed.id - ) - attrs["user_id"], attrs["category_id"] = feed.user_id, feed.category_id - - skipped, read, liked = process_filters(feed.filters, attrs) - if skipped: - return None - article = super().create(**attrs) - return article - - def update(self, filters, attrs): - user_id = attrs.get("user_id", self.user_id) - if "feed_id" in attrs: - feed = FeedController().get(id=attrs["feed_id"]) - assert feed.user_id == user_id, "no right on feed %r" % feed.id - attrs["category_id"] = feed.category_id - if attrs.get("category_id"): - cat = CategoryController().get(id=attrs["category_id"]) - assert self.user_id is None or cat.user_id == user_id, ( - "no right on cat %r" % cat.id - ) - return super().update(filters, attrs) - - def get_history(self, year=None, month=None): - """ - Sort articles by year and month. - """ - articles_counter = Counter() - articles = self.read() - if year is not None: - articles = articles.filter(sqlalchemy.extract("year", Article.date) == year) - if month is not None: - articles = articles.filter( - sqlalchemy.extract("month", Article.date) == month - ) - for article in articles.all(): - if year is not None: - articles_counter[article.date.month] += 1 - else: - articles_counter[article.date.year] += 1 - return articles_counter, articles - - def read_light(self, **filters): - return ( - super() - .read(**filters) - .with_entities( - Article.id, - Article.title, - Article.readed, - Article.like, - Article.feed_id, - Article.date, - Article.category_id, - ) - .order_by(Article.date.desc()) - ) - - def read_ordered(self, **filters): - return super().read(**filters).order_by(Article.date.desc()) diff --git a/newspipe/web/controllers/bookmark.py b/newspipe/web/controllers/bookmark.py deleted file mode 100644 index d1c1260c..00000000 --- a/newspipe/web/controllers/bookmark.py +++ /dev/null @@ -1,35 +0,0 @@ -import logging -import itertools -from datetime import datetime, timedelta - -from bootstrap import db -from web.models import Bookmark -from .abstract import AbstractController -from .tag import BookmarkTagController - -logger = logging.getLogger(__name__) - - -class BookmarkController(AbstractController): - _db_cls = Bookmark - - def count_by_href(self, **filters): - return self._count_by(Bookmark.href, filters) - - def update(self, filters, attrs): - BookmarkTagController(self.user_id).read( - **{"bookmark_id": filters["id"]} - ).delete() - - for tag in attrs["tags"]: - BookmarkTagController(self.user_id).create( - **{ - "text": tag.text, - "id": tag.id, - "bookmark_id": tag.bookmark_id, - "user_id": tag.user_id, - } - ) - - del attrs["tags"] - return super().update(filters, attrs) diff --git a/newspipe/web/controllers/category.py b/newspipe/web/controllers/category.py deleted file mode 100644 index ec54f5a3..00000000 --- a/newspipe/web/controllers/category.py +++ /dev/null @@ -1,13 +0,0 @@ -from .abstract import AbstractController -from web.models import Category -from .feed import FeedController - - -class CategoryController(AbstractController): - _db_cls = Category - - def delete(self, obj_id): - FeedController(self.user_id).update( - {"category_id": obj_id}, {"category_id": None} - ) - return super().delete(obj_id) diff --git a/newspipe/web/controllers/feed.py b/newspipe/web/controllers/feed.py deleted file mode 100644 index 19ba463f..00000000 --- a/newspipe/web/controllers/feed.py +++ /dev/null @@ -1,105 +0,0 @@ -import logging -import itertools -from datetime import datetime, timedelta - -import conf -from .abstract import AbstractController -from .icon import IconController -from web.models import User, Feed -from lib.utils import clear_string - -logger = logging.getLogger(__name__) -DEFAULT_LIMIT = 5 -DEFAULT_MAX_ERROR = conf.DEFAULT_MAX_ERROR - - -class FeedController(AbstractController): - _db_cls = Feed - - def list_late(self, max_last, max_error=DEFAULT_MAX_ERROR, limit=DEFAULT_LIMIT): - return [ - feed - for feed in self.read( - error_count__lt=max_error, enabled=True, last_retrieved__lt=max_last - ) - .join(User) - .filter(User.is_active == True) - .order_by("last_retrieved") - .limit(limit) - ] - - def list_fetchable(self, max_error=DEFAULT_MAX_ERROR, limit=DEFAULT_LIMIT): - now = datetime.now() - max_last = now - timedelta(minutes=60) - feeds = self.list_late(max_last, max_error, limit) - if feeds: - self.update( - {"id__in": [feed.id for feed in feeds]}, {"last_retrieved": now} - ) - return feeds - - def get_duplicates(self, feed_id): - """ - Compare a list of documents by pair. - Pairs of duplicates are sorted by "retrieved date". - """ - feed = self.get(id=feed_id) - duplicates = [] - for pair in itertools.combinations(feed.articles[:1000], 2): - date1, date2 = pair[0].date, pair[1].date - if clear_string(pair[0].title) == clear_string(pair[1].title) and ( - date1 - date2 - ) < timedelta(days=1): - if pair[0].retrieved_date < pair[1].retrieved_date: - duplicates.append((pair[0], pair[1])) - else: - duplicates.append((pair[1], pair[0])) - return feed, duplicates - - def get_inactives(self, nb_days): - today = datetime.now() - inactives = [] - for feed in self.read(): - try: - last_post = feed.articles[0].date - except IndexError: - continue - except Exception as e: - logger.exception(e) - continue - elapsed = today - last_post - if elapsed > timedelta(days=nb_days): - inactives.append((feed, elapsed)) - inactives.sort(key=lambda tup: tup[1], reverse=True) - return inactives - - def count_by_category(self, **filters): - return self._count_by(Feed.category_id, filters) - - def count_by_link(self, **filters): - return self._count_by(Feed.link, filters) - - def _ensure_icon(self, attrs): - if not attrs.get("icon_url"): - return - icon_contr = IconController() - if not icon_contr.read(url=attrs["icon_url"]).count(): - icon_contr.create(**{"url": attrs["icon_url"]}) - - def create(self, **attrs): - self._ensure_icon(attrs) - return super().create(**attrs) - - def update(self, filters, attrs): - from .article import ArticleController - - self._ensure_icon(attrs) - if "category_id" in attrs and attrs["category_id"] == 0: - del attrs["category_id"] - elif "category_id" in attrs: - art_contr = ArticleController(self.user_id) - for feed in self.read(**filters): - art_contr.update( - {"feed_id": feed.id}, {"category_id": attrs["category_id"]} - ) - return super().update(filters, attrs) diff --git a/newspipe/web/controllers/icon.py b/newspipe/web/controllers/icon.py deleted file mode 100644 index de86b52f..00000000 --- a/newspipe/web/controllers/icon.py +++ /dev/null @@ -1,27 +0,0 @@ -import base64 -import requests -from web.models import Icon -from .abstract import AbstractController - - -class IconController(AbstractController): - _db_cls = Icon - _user_id_key = None - - def _build_from_url(self, attrs): - if "url" in attrs and "content" not in attrs: - resp = requests.get(attrs["url"], verify=False) - attrs.update( - { - "url": resp.url, - "mimetype": resp.headers.get("content-type", None), - "content": base64.b64encode(resp.content).decode("utf8"), - } - ) - return attrs - - def create(self, **attrs): - return super().create(**self._build_from_url(attrs)) - - def update(self, filters, attrs): - return super().update(filters, self._build_from_url(attrs)) diff --git a/newspipe/web/controllers/tag.py b/newspipe/web/controllers/tag.py deleted file mode 100644 index 35fd5613..00000000 --- a/newspipe/web/controllers/tag.py +++ /dev/null @@ -1,22 +0,0 @@ -import logging -import itertools -from datetime import datetime, timedelta - -from bootstrap import db -from .abstract import AbstractController -from web.models.tag import BookmarkTag - -logger = logging.getLogger(__name__) - - -class BookmarkTagController(AbstractController): - _db_cls = BookmarkTag - - def count_by_href(self, **filters): - return self._count_by(BookmarkTag.text, filters) - - def create(self, **attrs): - return super().create(**attrs) - - def update(self, filters, attrs): - return super().update(filters, attrs) diff --git a/newspipe/web/controllers/user.py b/newspipe/web/controllers/user.py deleted file mode 100644 index 71eb7d08..00000000 --- a/newspipe/web/controllers/user.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging -from werkzeug.security import generate_password_hash, check_password_hash -from .abstract import AbstractController -from web.models import User - -logger = logging.getLogger(__name__) - - -class UserController(AbstractController): - _db_cls = User - _user_id_key = "id" - - def _handle_password(self, attrs): - if attrs.get("password"): - attrs["pwdhash"] = generate_password_hash(attrs.pop("password")) - elif "password" in attrs: - del attrs["password"] - - def check_password(self, user, password): - return check_password_hash(user.pwdhash, password) - - def create(self, **attrs): - self._handle_password(attrs) - return super().create(**attrs) - - def update(self, filters, attrs): - self._handle_password(attrs) - return super().update(filters, attrs) diff --git a/newspipe/web/forms.py b/newspipe/web/forms.py index e1921210..540b2723 100644 --- a/newspipe/web/forms.py +++ b/newspipe/web/forms.py @@ -43,9 +43,9 @@ from wtforms import ( ) from wtforms.fields.html5 import EmailField, URLField -from lib import misc_utils -from web.controllers import UserController -from web.models import User +from newspipe.lib import misc_utils +from newspipe.controllers import UserController +from newspipe.models import User class SignupForm(FlaskForm): diff --git a/newspipe/web/lib/user_utils.py b/newspipe/web/lib/user_utils.py index 84b1c75c..95b436a9 100644 --- a/newspipe/web/lib/user_utils.py +++ b/newspipe/web/lib/user_utils.py @@ -1,6 +1,6 @@ from itsdangerous import URLSafeTimedSerializer -import conf -from bootstrap import application + +from newspipe.bootstrap import application def generate_confirmation_token(nickname): @@ -14,7 +14,7 @@ def confirm_token(token): nickname = serializer.loads( token, salt=application.config["SECURITY_PASSWORD_SALT"], - max_age=conf.TOKEN_VALIDITY_PERIOD, + max_age=application.config['TOKEN_VALIDITY_PERIOD'], ) except: return False diff --git a/newspipe/web/lib/view_utils.py b/newspipe/web/lib/view_utils.py index 218ebb4c..c6e722d3 100644 --- a/newspipe/web/lib/view_utils.py +++ b/newspipe/web/lib/view_utils.py @@ -1,6 +1,6 @@ from functools import wraps from flask import request, Response, make_response -from lib.utils import to_hash +from newspipe.lib.utils import to_hash def etag_match(func): diff --git a/newspipe/web/models/__init__.py b/newspipe/web/models/__init__.py deleted file mode 100644 index a58a7ad5..00000000 --- a/newspipe/web/models/__init__.py +++ /dev/null @@ -1,98 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -# Newspipe - A Web based news aggregator. -# Copyright (C) 2010-2020 Cédric Bonhomme - https://www.cedricbonhomme.org -# -# For more information: https://git.sr.ht/~cedric/newspipe -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# 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/>. - -__author__ = "Cedric Bonhomme" -__version__ = "$Revision: 0.4 $" -__date__ = "$Date: 2013/11/05 $" -__revision__ = "$Date: 2014/04/12 $" -__copyright__ = "Copyright (c) Cedric Bonhomme" -__license__ = "GPLv3" - -from .feed import Feed -from .role import Role -from .user import User -from .article import Article -from .icon import Icon -from .category import Category -from .tag import BookmarkTag -from .tag import ArticleTag -from .bookmark import Bookmark - -__all__ = [ - "Feed", - "Role", - "User", - "Article", - "Icon", - "Category", - "Bookmark", - "ArticleTag", - "BookmarkTag", -] - -import os - -from sqlalchemy.engine import reflection -from sqlalchemy.schema import ( - MetaData, - Table, - DropTable, - ForeignKeyConstraint, - DropConstraint, -) - - -def db_empty(db): - "Will drop every datas stocked in db." - # From http://www.sqlalchemy.org/trac/wiki/UsageRecipes/DropEverything - conn = db.engine.connect() - - # the transaction only applies if the DB supports - # transactional DDL, i.e. Postgresql, MS SQL Server - trans = conn.begin() - - inspector = reflection.Inspector.from_engine(db.engine) - - # gather all data first before dropping anything. - # some DBs lock after things have been dropped in - # a transaction. - metadata = MetaData() - - tbs = [] - all_fks = [] - - for table_name in inspector.get_table_names(): - fks = [] - for fk in inspector.get_foreign_keys(table_name): - if not fk["name"]: - continue - fks.append(ForeignKeyConstraint((), (), name=fk["name"])) - t = Table(table_name, metadata, *fks) - tbs.append(t) - all_fks.extend(fks) - - for fkc in all_fks: - conn.execute(DropConstraint(fkc)) - - for table in tbs: - conn.execute(DropTable(table)) - - trans.commit() diff --git a/newspipe/web/models/article.py b/newspipe/web/models/article.py deleted file mode 100644 index 4e623ba2..00000000 --- a/newspipe/web/models/article.py +++ /dev/null @@ -1,101 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -# Newspipe - A Web based news aggregator. -# Copyright (C) 2010-2020 Cédric Bonhomme - https://www.cedricbonhomme.org -# -# For more information: https://git.sr.ht/~cedric/newspipe -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# 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/>. - -__author__ = "Cedric Bonhomme" -__version__ = "$Revision: 0.5 $" -__date__ = "$Date: 2013/11/05 $" -__revision__ = "$Date: 2016/10/04 $" -__copyright__ = "Copyright (c) Cedric Bonhomme" -__license__ = "GPLv3" - -from bootstrap import db -from datetime import datetime -from sqlalchemy import Index -from sqlalchemy.ext.associationproxy import association_proxy - -from web.models.right_mixin import RightMixin - - -class Article(db.Model, RightMixin): - "Represent an article from a feed." - id = db.Column(db.Integer(), primary_key=True) - entry_id = db.Column(db.String(), nullable=False) - link = db.Column(db.String()) - title = db.Column(db.String()) - content = db.Column(db.String()) - readed = db.Column(db.Boolean(), default=False) - like = db.Column(db.Boolean(), default=False) - date = db.Column(db.DateTime(), default=datetime.utcnow) - updated_date = db.Column(db.DateTime(), default=datetime.utcnow) - retrieved_date = db.Column(db.DateTime(), default=datetime.utcnow) - - # foreign keys - user_id = db.Column(db.Integer(), db.ForeignKey("user.id")) - feed_id = db.Column(db.Integer(), db.ForeignKey("feed.id")) - category_id = db.Column(db.Integer(), db.ForeignKey("category.id")) - - # relationships - tag_objs = db.relationship( - "ArticleTag", - back_populates="article", - cascade="all,delete-orphan", - lazy=False, - foreign_keys="[ArticleTag.article_id]", - ) - tags = association_proxy("tag_objs", "text") - - # indexes - # __table_args__ = ( - # Index('user_id'), - # Index('user_id', 'category_id'), - # Index('user_id', 'feed_id'), - # Index('ix_article_uid_fid_eid', user_id, feed_id, entry_id) - # ) - - # api whitelists - @staticmethod - def _fields_base_write(): - return {"readed", "like", "feed_id", "category_id"} - - @staticmethod - def _fields_base_read(): - return { - "id", - "entry_id", - "link", - "title", - "content", - "date", - "retrieved_date", - "user_id", - "tags", - } - - @staticmethod - def _fields_api_write(): - return {"tags"} - - def __repr__(self): - return ( - "<Article(id=%d, entry_id=%s, title=%r, " - "date=%r, retrieved_date=%r)>" - % (self.id, self.entry_id, self.title, self.date, self.retrieved_date) - ) diff --git a/newspipe/web/models/bookmark.py b/newspipe/web/models/bookmark.py deleted file mode 100644 index 11b2db53..00000000 --- a/newspipe/web/models/bookmark.py +++ /dev/null @@ -1,72 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -# Newspipe - A Web based news aggregator. -# Copyright (C) 2010-2020 Cédric Bonhomme - https://www.cedricbonhomme.org -# -# For more information: https://git.sr.ht/~cedric/newspipe -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# 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/>. - -__author__ = "Cedric Bonhomme" -__version__ = "$Revision: 0.1 $" -__date__ = "$Date: 2016/12/07 $" -__revision__ = "$Date: 2016/12/07 $" -__copyright__ = "Copyright (c) Cedric Bonhomme" -__license__ = "GPLv3" - -from bootstrap import db -from datetime import datetime -from sqlalchemy import desc -from sqlalchemy.orm import validates -from sqlalchemy.ext.associationproxy import association_proxy - -from web.models.tag import BookmarkTag -from web.models.right_mixin import RightMixin - - -class Bookmark(db.Model, RightMixin): - """ - Represent a bookmark. - """ - - id = db.Column(db.Integer(), primary_key=True) - href = db.Column(db.String(), default="") - title = db.Column(db.String(), default="") - description = db.Column(db.String(), default="") - shared = db.Column(db.Boolean(), default=False) - to_read = db.Column(db.Boolean(), default=False) - time = db.Column(db.DateTime(), default=datetime.utcnow) - user_id = db.Column(db.Integer(), db.ForeignKey("user.id")) - - # relationships - tags = db.relationship( - BookmarkTag, - backref="of_bookmark", - lazy="dynamic", - cascade="all,delete-orphan", - order_by=desc(BookmarkTag.text), - ) - tags_proxy = association_proxy("tags", "text") - - @validates("description") - def validates_title(self, key, value): - return str(value).strip() - - @validates("extended") - def validates_description(self, key, value): - return str(value).strip() - - def __repr__(self): - return "<Bookmark %r>" % (self.href) diff --git a/newspipe/web/models/category.py b/newspipe/web/models/category.py deleted file mode 100644 index bb47ce45..00000000 --- a/newspipe/web/models/category.py +++ /dev/null @@ -1,28 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -from bootstrap import db -from sqlalchemy import Index -from web.models.right_mixin import RightMixin - - -class Category(db.Model, RightMixin): - id = db.Column(db.Integer(), primary_key=True) - name = db.Column(db.String()) - - # relationships - user_id = db.Column(db.Integer, db.ForeignKey("user.id")) - feeds = db.relationship("Feed", cascade="all,delete-orphan") - articles = db.relationship("Article", cascade="all,delete-orphan") - - # index - idx_category_uid = Index("user_id") - - # api whitelists - @staticmethod - def _fields_base_read(): - return {"id", "user_id"} - - @staticmethod - def _fields_base_write(): - return {"name"} diff --git a/newspipe/web/models/feed.py b/newspipe/web/models/feed.py deleted file mode 100644 index 70a5d521..00000000 --- a/newspipe/web/models/feed.py +++ /dev/null @@ -1,105 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -# newspipe - A Web based news aggregator. -# Copyright (C) 2010-2020 Cédric Bonhomme - https://www.cedricbonhomme.org -# -# For more information: https://git.sr.ht/~cedric/newspipe -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# 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/>. - -__author__ = "Cedric Bonhomme" -__version__ = "$Revision: 0.4 $" -__date__ = "$Date: 2013/11/05 $" -__revision__ = "$Date: 2014/04/12 $" -__copyright__ = "Copyright (c) Cedric Bonhomme" -__license__ = "GPLv3" - -from bootstrap import db -from datetime import datetime -from sqlalchemy import desc, Index -from sqlalchemy.orm import validates -from web.models.right_mixin import RightMixin -from web.models.article import Article - - -class Feed(db.Model, RightMixin): - """ - Represent a feed. - """ - - id = db.Column(db.Integer(), primary_key=True) - title = db.Column(db.String(), default="") - description = db.Column(db.String(), default="FR") - link = db.Column(db.String(), nullable=False) - site_link = db.Column(db.String(), default="") - enabled = db.Column(db.Boolean(), default=True) - created_date = db.Column(db.DateTime(), default=datetime.utcnow) - filters = db.Column(db.PickleType, default=[]) - private = db.Column(db.Boolean(), default=False) - - # cache handling - etag = db.Column(db.String(), default="") - last_modified = db.Column(db.String(), default="") - last_retrieved = db.Column(db.DateTime(), default=datetime(1970, 1, 1)) - - # error logging - last_error = db.Column(db.String(), default="") - error_count = db.Column(db.Integer(), default=0) - - # relationship - icon_url = db.Column(db.String(), db.ForeignKey("icon.url"), default=None) - user_id = db.Column(db.Integer(), db.ForeignKey("user.id")) - category_id = db.Column(db.Integer(), db.ForeignKey("category.id")) - articles = db.relationship( - Article, - backref="source", - lazy="dynamic", - cascade="all,delete-orphan", - order_by=desc(Article.date), - ) - - # index - idx_feed_uid_cid = Index("user_id", "category_id") - idx_feed_uid = Index("user_id") - - # api whitelists - @staticmethod - def _fields_base_write(): - return { - "title", - "description", - "link", - "site_link", - "enabled", - "filters", - "last_error", - "error_count", - "category_id", - } - - @staticmethod - def _fields_base_read(): - return {"id", "user_id", "icon_url", "last_retrieved"} - - @validates("title") - def validates_title(self, key, value): - return str(value).strip() - - @validates("description") - def validates_description(self, key, value): - return str(value).strip() - - def __repr__(self): - return "<Feed %r>" % (self.title) diff --git a/newspipe/web/models/icon.py b/newspipe/web/models/icon.py deleted file mode 100644 index adc9cf69..00000000 --- a/newspipe/web/models/icon.py +++ /dev/null @@ -1,10 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -from bootstrap import db - - -class Icon(db.Model): - url = db.Column(db.String(), primary_key=True) - content = db.Column(db.String(), default=None) - mimetype = db.Column(db.String(), default="application/image") diff --git a/newspipe/web/models/right_mixin.py b/newspipe/web/models/right_mixin.py deleted file mode 100644 index 670beafa..00000000 --- a/newspipe/web/models/right_mixin.py +++ /dev/null @@ -1,64 +0,0 @@ -from sqlalchemy.ext.associationproxy import _AssociationList - - -class RightMixin: - @staticmethod - def _fields_base_write(): - return set() - - @staticmethod - def _fields_base_read(): - return set(["id"]) - - @staticmethod - def _fields_api_write(): - return set([]) - - @staticmethod - def _fields_api_read(): - return set(["id"]) - - @classmethod - def fields_base_write(cls): - return cls._fields_base_write() - - @classmethod - def fields_base_read(cls): - return cls._fields_base_write().union(cls._fields_base_read()) - - @classmethod - def fields_api_write(cls): - return cls.fields_base_write().union(cls._fields_api_write()) - - @classmethod - def fields_api_read(cls): - return cls.fields_base_read().union(cls._fields_api_read()) - - def __getitem__(self, key): - if not hasattr(self, "__dump__"): - self.__dump__ = {} - return self.__dump__.get(key) - - def __setitem__(self, key, value): - if not hasattr(self, "__dump__"): - self.__dump__ = {} - self.__dump__[key] = value - - def dump(self, role="admin"): - if role == "admin": - dico = { - k: getattr(self, k) - for k in set(self.__table__.columns.keys()) - .union(self.fields_api_read()) - .union(self.fields_base_read()) - } - elif role == "api": - dico = {k: getattr(self, k) for k in self.fields_api_read()} - else: - dico = {k: getattr(self, k) for k in self.fields_base_read()} - if hasattr(self, "__dump__"): - dico.update(self.__dump__) - for key, value in dico.items(): # preventing association proxy to die - if isinstance(value, _AssociationList): - dico[key] = list(value) - return dico diff --git a/newspipe/web/models/role.py b/newspipe/web/models/role.py deleted file mode 100644 index 4a0bd42d..00000000 --- a/newspipe/web/models/role.py +++ /dev/null @@ -1,40 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -# newspipe - A Web based news aggregator. -# Copyright (C) 2010-2020 Cédric Bonhomme - https://www.cedricbonhomme.org -# -# For more information: https://git.sr.ht/~cedric/newspipe -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# 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/>. - -__author__ = "Cedric Bonhomme" -__version__ = "$Revision: 0.4 $" -__date__ = "$Date: 2013/11/05 $" -__revision__ = "$Date: 2014/04/12 $" -__copyright__ = "Copyright (c) Cedric Bonhomme" -__license__ = "GPLv3" - -from bootstrap import db - - -class Role(db.Model): - """ - Represent a role. - """ - - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(), unique=True) - - user_id = db.Column(db.Integer, db.ForeignKey("user.id")) diff --git a/newspipe/web/models/tag.py b/newspipe/web/models/tag.py deleted file mode 100644 index 9bd8afc5..00000000 --- a/newspipe/web/models/tag.py +++ /dev/null @@ -1,44 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -from bootstrap import db - - -class ArticleTag(db.Model): - text = db.Column(db.String, primary_key=True, unique=False) - - # foreign keys - article_id = db.Column( - db.Integer, db.ForeignKey("article.id", ondelete="CASCADE"), primary_key=True - ) - - # relationships - article = db.relationship( - "Article", back_populates="tag_objs", foreign_keys=[article_id] - ) - - def __init__(self, text): - self.text = text - - -class BookmarkTag(db.Model): - id = db.Column(db.Integer, primary_key=True) - text = db.Column(db.String, unique=False) - - # foreign keys - user_id = db.Column(db.Integer, db.ForeignKey("user.id", ondelete="CASCADE")) - bookmark_id = db.Column( - db.Integer, db.ForeignKey("bookmark.id", ondelete="CASCADE") - ) - - # relationships - bookmark = db.relationship( - "Bookmark", - back_populates="tags", - cascade="all,delete", - foreign_keys=[bookmark_id], - ) - - # def __init__(self, text, user_id): - # self.text = text - # self.user_id = user_id diff --git a/newspipe/web/models/user.py b/newspipe/web/models/user.py deleted file mode 100644 index f96c8ccb..00000000 --- a/newspipe/web/models/user.py +++ /dev/null @@ -1,114 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -# newspipe - A Web based news aggregator. -# Copyright (C) 2010-2020 Cédric Bonhomme - https://www.cedricbonhomme.org -# -# For more information: https://git.sr.ht/~cedric/newspipe -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# 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/>. - -__author__ = "Cedric Bonhomme" -__version__ = "$Revision: 0.4 $" -__date__ = "$Date: 2013/11/05 $" -__revision__ = "$Date: 2014/04/12 $" -__copyright__ = "Copyright (c) Cedric Bonhomme" -__license__ = "GPLv3" - -import re -import random -import hashlib -from datetime import datetime -from werkzeug.security import check_password_hash -from flask_login import UserMixin -from sqlalchemy.orm import validates - -from bootstrap import db -from web.models.right_mixin import RightMixin -from web.models.category import Category -from web.models.feed import Feed - - -class User(db.Model, UserMixin, RightMixin): - """ - Represent a user. - """ - - id = db.Column(db.Integer, primary_key=True) - nickname = db.Column(db.String(), unique=True) - pwdhash = db.Column(db.String()) - - automatic_crawling = db.Column(db.Boolean(), default=True) - - is_public_profile = db.Column(db.Boolean(), default=False) - bio = db.Column(db.String(5000), default="") - webpage = db.Column(db.String(), default="") - twitter = db.Column(db.String(), default="") - - date_created = db.Column(db.DateTime(), default=datetime.utcnow) - last_seen = db.Column(db.DateTime(), default=datetime.utcnow) - - # user rights - is_active = db.Column(db.Boolean(), default=False) - is_admin = db.Column(db.Boolean(), default=False) - is_api = db.Column(db.Boolean(), default=False) - - # relationships - categories = db.relationship( - "Category", - backref="user", - cascade="all, delete-orphan", - foreign_keys=[Category.user_id], - ) - feeds = db.relationship( - "Feed", - backref="user", - cascade="all, delete-orphan", - foreign_keys=[Feed.user_id], - ) - - @staticmethod - def _fields_base_write(): - return {"login", "password"} - - @staticmethod - def _fields_base_read(): - return {"date_created", "last_connection"} - - @staticmethod - def make_valid_nickname(nickname): - return re.sub("[^a-zA-Z0-9_\.]", "", nickname) - - @validates("bio") - def validates_bio(self, key, value): - assert len(value) <= 5000, AssertionError("maximum length for bio: 5000") - return value.strip() - - def get_id(self): - """ - Return the id of the user. - """ - return self.id - - def check_password(self, password): - """ - Check the password of the user. - """ - return check_password_hash(self.pwdhash, password) - - def __eq__(self, other): - return self.id == other.id - - def __repr__(self): - return "<User %r>" % (self.nickname) diff --git a/newspipe/web/static/css/custom.css b/newspipe/web/static/css/custom.css deleted file mode 100644 index c85cc8a1..00000000 --- a/newspipe/web/static/css/custom.css +++ /dev/null @@ -1,80 +0,0 @@ -html { - position: relative; - min-height: 100%; - font-size: 16px; -} -body { - /* Margin bottom by footer height */ - margin-bottom: 60px; - background-color: rgb(246, 248, 250); -} -img { - padding: 2px; -} -a { - color: #3572B0; -} - -#sidebar { - overflow: auto; - max-height: 700px; -} - -#sidebar .nav li { - list-style-type: none; - padding: 0; - line-height: 1.0; - flex-direction: row; - flex-wrap: nowrap; /* assumes you only want one row */ -} - -.navbar-dark .navbar-nav .nav-link { - color: - rgb(255, 255, 255, 1); -} - -.bg-newspipe-blue { - /* background-color: #0082c9; */ - background-image: linear-gradient(40deg, #0082c9 0%, #30b6ff 100%); -} -.btn-primary { - background-color: #0082c9; - opacity: 1; - color: white; -} - -.btn-outline-primary { - border-color: #0082c9; - color: #0082c9; -} - -/* Sticky footer */ -.footer { - position: absolute; - bottom: 0; - width: 100%; - /* Set the fixed height of the footer here */ - height: 60px; - line-height: 60px; /* Vertically center the text there */ - background-color: #006FBA; -} -.footer a{ - color: #FFFFFF; -} - -.input-group-inline { - min-width: 0; - width: 200px; - display: inline; -} - -.alert-message { - position: relative; - display: block; - z-index: 1001; -} - -/*customization for Flask-Paginate*/ -.pagination-page-info { - display: inline; -} diff --git a/newspipe/web/static/img/favicon.ico b/newspipe/web/static/img/favicon.ico Binary files differdeleted file mode 100644 index 5b056c1e..00000000 --- a/newspipe/web/static/img/favicon.ico +++ /dev/null diff --git a/newspipe/web/static/img/newspipe.png b/newspipe/web/static/img/newspipe.png Binary files differdeleted file mode 100644 index c3ba5029..00000000 --- a/newspipe/web/static/img/newspipe.png +++ /dev/null diff --git a/newspipe/web/static/img/newspipe.svg b/newspipe/web/static/img/newspipe.svg deleted file mode 100644 index be22ae42..00000000 --- a/newspipe/web/static/img/newspipe.svg +++ /dev/null @@ -1,84 +0,0 @@ -<?xml version="1.0" standalone="no"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" - "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> -<svg version="1.0" xmlns="http://www.w3.org/2000/svg" - width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000" - preserveAspectRatio="xMidYMid meet"> -<metadata> -Created by potrace 1.13, written by Peter Selinger 2001-2015 -</metadata> -<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)" -fill="#000000" stroke="none"> -<path d="M4215 3536 c-66 -13 -239 -51 -385 -84 -236 -54 -270 -60 -310 -51 --70 16 -207 13 -285 -6 -39 -10 -72 -21 -75 -25 -3 -3 -18 -14 -35 -23 -27 --14 -146 -17 -1170 -25 -1045 -8 -1145 -10 -1195 -26 -147 -47 -250 -133 -318 --264 -150 -287 -162 -659 -35 -1017 87 -244 211 -388 428 -498 117 -59 195 --79 336 -84 145 -6 235 8 383 61 51 18 493 235 982 482 812 410 895 450 954 -457 119 14 238 59 335 128 22 16 67 35 100 44 137 37 445 115 466 119 144 26 -243 92 298 199 22 43 71 225 71 265 0 19 -5 22 -38 22 -51 0 -84 36 -107 115 --22 77 -40 112 -79 153 -73 79 -147 92 -321 58z m151 -31 c32 -9 65 -29 93 --57 50 -49 103 -154 105 -206 l1 -36 -270 -73 c-148 -39 -275 -72 -280 -72 -6 --1 -23 28 -39 64 -36 82 -97 151 -188 211 -48 33 -66 49 -57 54 13 7 421 99 -524 118 28 6 53 10 56 11 3 0 28 -6 55 -14z m-810 -126 c161 -50 295 -142 342 --235 31 -62 36 -94 7 -44 -15 25 -41 60 -58 79 -70 73 -222 160 -347 197 -56 -17 -67 23 -44 23 18 1 63 -9 100 -20z m-102 -5 c127 -33 317 -142 387 -222 37 --41 79 -127 79 -159 0 -17 -6 -23 -23 -23 -18 0 -28 11 -45 48 -74 162 -272 -308 -479 352 -87 18 -88 19 -34 19 29 1 81 -6 115 -15z m-130 -108 c81 -23 -146 -60 201 -115 53 -53 102 -146 111 -211 5 -40 3 -50 -20 -76 -28 -34 -62 --47 -316 -114 -480 -128 -1189 -322 -1213 -332 -33 -13 -77 -63 -77 -86 0 -9 --13 -44 -29 -78 -45 -96 -82 -119 -282 -179 -13 -4 -12 8 8 86 30 121 38 250 -24 379 -41 350 -169 584 -377 686 l-78 39 579 7 c319 4 672 9 785 11 376 7 -619 1 684 -17z m261 -36 c82 -47 141 -105 174 -172 38 -76 39 -88 4 -88 -25 0 --34 11 -82 101 -51 94 -81 129 -141 171 -41 28 -11 20 45 -12z m-2401 -49 c33 --12 78 -32 100 -45 62 -37 141 -124 174 -191 31 -63 85 -223 97 -287 7 -36 7 --37 -32 -44 -21 -4 -189 -35 -373 -68 l-335 -61 -8 -50 c-9 -60 0 -178 19 --254 59 -225 201 -331 444 -331 136 0 258 34 488 136 78 35 145 64 147 64 8 0 --34 -100 -60 -144 -61 -104 -40 -94 -460 -207 -431 -115 -402 -113 -542 -43 --128 64 -237 182 -304 330 -104 229 -144 585 -91 798 74 295 264 434 577 422 -67 -3 120 -11 159 -25z m3506 -24 c0 -8 -9 -49 -20 -92 -32 -127 -90 -200 --195 -246 -42 -19 -507 -149 -532 -149 -8 0 -3 19 13 54 14 30 33 98 44 152 -12 62 25 104 38 116 12 13 107 42 283 88 145 38 278 74 294 79 45 14 75 13 75 --2z m-973 -339 c-3 -8 -6 -5 -6 6 -1 11 2 17 5 13 3 -3 4 -12 1 -19z m-107 --51 c0 -42 -65 -160 -113 -204 -48 -44 -359 -204 -952 -490 -235 -114 -312 --146 -408 -172 -65 -17 -120 -31 -122 -31 -2 0 9 28 24 63 42 93 59 151 80 -277 15 94 23 118 43 135 15 13 233 83 623 199 330 98 647 194 705 211 129 40 -120 40 120 12z m-2022 -293 c19 -134 11 -267 -20 -335 -33 -73 -80 -124 -142 --157 -46 -24 -62 -27 -152 -27 -96 0 -105 2 -170 35 -116 59 -172 156 -181 -317 -7 103 -1 128 34 141 16 6 143 32 283 57 140 25 269 49 285 54 17 5 35 8 -40 7 6 -2 16 -43 23 -92z"/> -<path d="M4193 3430 c-29 -4 -101 -25 -160 -46 l-108 -39 72 -3 c64 -3 89 2 -205 42 90 31 123 45 101 46 -17 0 -37 2 -45 4 -7 2 -37 0 -65 -4z"/> -<path d="M4265 3296 c-230 -66 -229 -65 -209 -87 47 -52 163 -43 328 27 l88 -37 -38 23 c-47 29 -67 29 -169 0z"/> -<path d="M2860 3197 l-95 -10 50 -23 c90 -42 143 -41 465 10 8 1 -9 10 -39 19 --62 19 -222 21 -381 4z"/> -<path d="M2140 3185 c-151 -7 -299 -14 -328 -14 l-52 -1 43 -52 c23 -29 52 --69 64 -88 32 -52 78 -198 104 -327 12 -62 27 -113 33 -113 6 0 202 37 435 82 -l424 83 -6 75 c-12 172 -92 292 -227 339 -79 27 -179 30 -490 16z"/> -<path d="M3335 3063 c-16 -2 -108 -15 -204 -28 -95 -14 -175 -25 -178 -25 -5 -0 2 -39 18 -101 l9 -36 207 39 c319 58 313 57 313 78 0 10 -16 32 -35 49 -36 -31 -52 34 -130 24z"/> -<path d="M812 3060 c-55 -12 -126 -47 -154 -77 l-21 -23 371 0 c205 0 372 4 -372 8 0 11 -36 49 -65 68 -19 12 -55 17 -147 19 -68 1 -166 5 -218 8 -52 3 --114 2 -138 -3z"/> -<path d="M1155 2803 c-263 -22 -572 -56 -579 -63 -5 -5 -11 -43 -14 -84 -4 --69 -3 -74 14 -71 11 3 219 34 464 71 245 36 447 68 449 70 8 8 -35 59 -60 71 --23 12 -170 15 -274 6z"/> -<path d="M647 2406 c-37 -7 -70 -15 -73 -19 -8 -8 15 -145 26 -152 9 -6 122 -15 133 24 8 8 -3 162 -12 160 -3 -1 -36 -7 -74 -13z"/> -<path d="M694 2067 l-61 -22 14 -41 c8 -23 29 -59 47 -80 l33 -38 64 28 c35 -16 65 30 67 31 2 2 -8 19 -22 40 -14 20 -31 52 -38 71 -14 39 -21 40 -104 11z"/> -<path d="M946 1792 c-27 -15 -51 -32 -53 -37 -3 -11 44 -43 92 -62 53 -20 126 --16 182 12 52 26 80 51 70 61 -10 10 -182 54 -212 54 -16 0 -52 -13 -79 -28z"/> -<path d="M4510 3024 c-25 -7 -117 -32 -204 -55 -170 -43 -196 -55 -214 -95 --11 -23 -17 -84 -8 -84 11 0 247 51 281 61 95 27 154 75 179 147 8 23 14 42 -13 41 -1 0 -22 -7 -47 -15z"/> -<path d="M3355 2631 c-60 -22 -223 -80 -360 -129 -138 -49 -341 -120 -452 --156 -173 -58 -208 -73 -246 -106 -25 -22 -52 -57 -60 -78 -15 -38 -33 -152 --24 -152 6 0 555 255 967 448 241 113 257 122 288 166 43 58 35 59 -113 7z"/> -<path d="M1293 2368 l-201 -40 15 -51 c8 -29 16 -53 17 -54 4 -4 151 25 219 -43 109 28 153 59 172 122 8 27 25 29 -222 -20z"/> -<path d="M1376 2149 c-76 -26 -141 -50 -144 -53 -10 -11 55 -29 118 -33 56 -4 -70 -1 100 19 33 22 75 88 68 107 -2 5 -65 -13 -142 -40z"/> -</g> -</svg> diff --git a/newspipe/web/static/img/pinboard.png b/newspipe/web/static/img/pinboard.png Binary files differdeleted file mode 100644 index 6dddc10b..00000000 --- a/newspipe/web/static/img/pinboard.png +++ /dev/null diff --git a/newspipe/web/static/img/reddit.png b/newspipe/web/static/img/reddit.png Binary files differdeleted file mode 100755 index 2d615f2a..00000000 --- a/newspipe/web/static/img/reddit.png +++ /dev/null diff --git a/newspipe/web/static/img/twitter.png b/newspipe/web/static/img/twitter.png Binary files differdeleted file mode 100644 index fc11c4ce..00000000 --- a/newspipe/web/static/img/twitter.png +++ /dev/null diff --git a/newspipe/web/static/js/articles.js b/newspipe/web/static/js/articles.js deleted file mode 100644 index de579532..00000000 --- a/newspipe/web/static/js/articles.js +++ /dev/null @@ -1,191 +0,0 @@ -/*! -* Newspipe - A Web based news aggregator. -* Copyright (C) 2010-2020 Cédric Bonhomme - https://cedricbonhomme.org -* -* For more information: https://git.sr.ht/~cedric/newspipe -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* 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/>. - */ - -API_ROOT = '/api/v2.0/' - -if (typeof jQuery === 'undefined') { throw new Error('Requires jQuery') } - -function change_unread_counter(feed_id, increment) { - var new_value = parseInt($("#unread-"+feed_id).text()) + increment; - $("#unread-"+feed_id).text(new_value); - $("#total-unread").text(parseInt($("#total-unread").text()) + increment); - if (new_value == 0) { - $("#unread-"+feed_id).hide(); - } else { - $("#unread-"+feed_id).show(); - } -} - -+function ($) { - - // Mark an article as read when it is opened in a new table - $('.open-article').on('click', function(e) { - var feed_id = $(this).parent().parent().attr("data-feed"); - var filter = $('#filters').attr("data-filter"); - if (filter == "unread") { - $(this).parent().parent().remove(); - change_unread_counter(feed_id, -1); - } - }); - - - - // Mark an article as read or unread. - $('.readed').on('click', function() { - var article_id = $(this).parent().parent().parent().attr("data-article"); - var feed_id = $(this).parent().parent().parent().attr("data-feed"); - var filter = $('#filters').attr("data-filter"); - - var data; - if ($(this).hasClass('fa-square-o')) { - data = JSON.stringify({ - readed: false - }) - if (filter == "read") { - $(this).parent().parent().parent().remove(); - } - else { - // here, filter == "all" - $(this).parent().parent().parent().children("td:nth-child(2)").css( "font-weight", "bold" ); - $(this).removeClass('fa-square-o').addClass('fa-check-square-o'); - } - change_unread_counter(feed_id, 1); - } - else { - data = JSON.stringify({readed: true}) - if (filter == "unread") { - $(this).parent().parent().parent().remove(); - } - else { - // here, filter == "all" - $(this).parent().parent().parent().children("td:nth-child(2)").css( "font-weight", "normal" ); - $(this).removeClass('fa-check-square-o').addClass('fa-square-o'); - } - change_unread_counter(feed_id, -1); - } - - // sends the updates to the server - $.ajax({ - type: 'PUT', - // Provide correct Content-Type, so that Flask will know how to process it. - contentType: 'application/json', - // Encode your data as JSON. - data: data, - // This is the type of data you're expecting back from the server. - url: API_ROOT + "article/" + article_id, - success: function (result) { - //console.log(result); - }, - error: function(XMLHttpRequest, textStatus, errorThrown){ - console.log(XMLHttpRequest.responseText); - } - }); - }); - - - - // Like or unlike an article - $('.like').on('click', function() { - var article_id = $(this).parent().parent().parent().attr("data-article"); - var data; - if ($(this).hasClass("glyphicon-star")) { - data = JSON.stringify({like: false}) - $(this).removeClass('glyphicon-star').addClass('glyphicon-star-empty'); - if(window.location.pathname.indexOf('/favorites') != -1) { - $(this).parent().parent().parent().remove(); - } - } - else { - data = JSON.stringify({like: true}) - $(this).removeClass('glyphicon-star-empty').addClass('glyphicon-star'); - } - - // sends the updates to the server - $.ajax({ - type: 'PUT', - // Provide correct Content-Type, so that Flask will know how to process it. - contentType: 'application/json', - // Encode your data as JSON. - data: data, - // This is the type of data you're expecting back from the server. - url: API_ROOT + "article/" + article_id, - success: function (result) { - //console.log(result); - }, - error: function(XMLHttpRequest, textStatus, errorThrown){ - console.log(XMLHttpRequest.responseText); - } - }); - }); - - - - // Delete an article - $('.delete').on('click', function() { - var feed_id = $(this).parent().parent().parent().attr("data-feed"); - var article_id = $(this).parent().parent().parent().attr("data-article"); - $(this).parent().parent().parent().remove(); - - // sends the updates to the server - $.ajax({ - type: 'DELETE', - url: API_ROOT + "article/" + article_id, - success: function (result) { - change_unread_counter(feed_id, -1); - }, - error: function(XMLHttpRequest, textStatus, errorThrown){ - console.log(XMLHttpRequest.responseText); - } - }); - }); - - - - // Delete all duplicate articles (used in the page /duplicates) - $('.delete-all').click(function(){ - var data = []; - - var columnNo = $(this).parent().index(); - $(this).closest("table") - .find("tr td:nth-child(" + (columnNo+1) + ")") - .each(function(line, column) { - data.push(parseInt(column.id)); - }).remove(); - - data = JSON.stringify(data); - - // sends the updates to the server - $.ajax({ - type: 'DELETE', - // Provide correct Content-Type, so that Flask will know how to process it. - contentType: 'application/json', - data: data, - url: API_ROOT + "articles", - success: function (result) { - //console.log(result); - }, - error: function(XMLHttpRequest, textStatus, errorThrown){ - console.log(XMLHttpRequest.responseText); - } - }); - - }); - -}(jQuery); diff --git a/newspipe/web/static/js/feed.js b/newspipe/web/static/js/feed.js deleted file mode 100644 index ceef58fc..00000000 --- a/newspipe/web/static/js/feed.js +++ /dev/null @@ -1,22 +0,0 @@ -$('.container').on('click', '#add-feed-filter-row', function() { - $('#filters-container').append( - '<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" selected>simple match</option>' - + ' <option value="regex">regex</option>' - + ' </select>' - + ' <input type="text" class="form-control" name="pattern" />' - + ' <select name="action_on" class="form-control">' - + ' <option value="match" selected>match</option>' - + ' <option value="no match">no match</option>' - + ' </select>' - + ' <select name="action" class="form-control">' - + ' <option value="mark as read" selected>mark as read</option>' - + ' <option value="mark as favorite">mark as favorite</option>' - + ' </select>' - + '</div>'); -}); -$('.container').on('click', '.del-feed-filter-row', function() { - $(this).parent().remove(); -}); diff --git a/newspipe/web/templates/about.html b/newspipe/web/templates/about.html deleted file mode 100644 index 43d8c73d..00000000 --- a/newspipe/web/templates/about.html +++ /dev/null @@ -1,26 +0,0 @@ -{% 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/web/templates/about_more.html b/newspipe/web/templates/about_more.html deleted file mode 100644 index 94a01c07..00000000 --- a/newspipe/web/templates/about_more.html +++ /dev/null @@ -1,11 +0,0 @@ -{% 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/web/templates/admin/create_user.html b/newspipe/web/templates/admin/create_user.html deleted file mode 100644 index 8844f987..00000000 --- a/newspipe/web/templates/admin/create_user.html +++ /dev/null @@ -1,26 +0,0 @@ -{% 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/web/templates/admin/dashboard.html b/newspipe/web/templates/admin/dashboard.html deleted file mode 100644 index 8bcc2db0..00000000 --- a/newspipe/web/templates/admin/dashboard.html +++ /dev/null @@ -1,68 +0,0 @@ -{% 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/web/templates/article.html b/newspipe/web/templates/article.html deleted file mode 100644 index 884bf677..00000000 --- a/newspipe/web/templates/article.html +++ /dev/null @@ -1,35 +0,0 @@ -{% 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/web/templates/article_pub.html b/newspipe/web/templates/article_pub.html deleted file mode 100644 index e810d18f..00000000 --- a/newspipe/web/templates/article_pub.html +++ /dev/null @@ -1,24 +0,0 @@ -{% 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/web/templates/bookmarks.html b/newspipe/web/templates/bookmarks.html deleted file mode 100644 index 0f6e02c4..00000000 --- a/newspipe/web/templates/bookmarks.html +++ /dev/null @@ -1,81 +0,0 @@ -{% 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/web/templates/categories.html b/newspipe/web/templates/categories.html deleted file mode 100644 index ea5388a3..00000000 --- a/newspipe/web/templates/categories.html +++ /dev/null @@ -1,36 +0,0 @@ -{% 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/web/templates/duplicates.html b/newspipe/web/templates/duplicates.html deleted file mode 100644 index 38dc52b1..00000000 --- a/newspipe/web/templates/duplicates.html +++ /dev/null @@ -1,30 +0,0 @@ -{% 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/web/templates/edit_bookmark.html b/newspipe/web/templates/edit_bookmark.html deleted file mode 100644 index ee0e0243..00000000 --- a/newspipe/web/templates/edit_bookmark.html +++ /dev/null @@ -1,84 +0,0 @@ -{% 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/web/templates/edit_category.html b/newspipe/web/templates/edit_category.html deleted file mode 100644 index 955d17b9..00000000 --- a/newspipe/web/templates/edit_category.html +++ /dev/null @@ -1,23 +0,0 @@ -{% 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/web/templates/edit_feed.html b/newspipe/web/templates/edit_feed.html deleted file mode 100644 index a439c78d..00000000 --- a/newspipe/web/templates/edit_feed.html +++ /dev/null @@ -1,98 +0,0 @@ -{% 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/web/templates/emails/account_activation.txt b/newspipe/web/templates/emails/account_activation.txt deleted file mode 100644 index c7d9c52e..00000000 --- a/newspipe/web/templates/emails/account_activation.txt +++ /dev/null @@ -1,9 +0,0 @@ -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/web/templates/emails/new_password.txt b/newspipe/web/templates/emails/new_password.txt deleted file mode 100644 index 1a04a36d..00000000 --- a/newspipe/web/templates/emails/new_password.txt +++ /dev/null @@ -1,8 +0,0 @@ -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/web/templates/errors/404.html b/newspipe/web/templates/errors/404.html deleted file mode 100644 index c64a2be8..00000000 --- a/newspipe/web/templates/errors/404.html +++ /dev/null @@ -1,12 +0,0 @@ -{% 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/web/templates/errors/500.html b/newspipe/web/templates/errors/500.html deleted file mode 100644 index 417fc0c7..00000000 --- a/newspipe/web/templates/errors/500.html +++ /dev/null @@ -1,12 +0,0 @@ -{% 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/web/templates/feed.html b/newspipe/web/templates/feed.html deleted file mode 100644 index 4246669b..00000000 --- a/newspipe/web/templates/feed.html +++ /dev/null @@ -1,76 +0,0 @@ -{% 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/web/templates/feed_list.html b/newspipe/web/templates/feed_list.html deleted file mode 100644 index 11bd65a6..00000000 --- a/newspipe/web/templates/feed_list.html +++ /dev/null @@ -1,55 +0,0 @@ -{% 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 >= conf.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/web/templates/feed_list_per_categories.html b/newspipe/web/templates/feed_list_per_categories.html deleted file mode 100644 index 34d10ddd..00000000 --- a/newspipe/web/templates/feed_list_per_categories.html +++ /dev/null @@ -1,52 +0,0 @@ -<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/web/templates/feed_list_simple.html b/newspipe/web/templates/feed_list_simple.html deleted file mode 100644 index 5f692a53..00000000 --- a/newspipe/web/templates/feed_list_simple.html +++ /dev/null @@ -1,35 +0,0 @@ -{% 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/web/templates/feeds.html b/newspipe/web/templates/feeds.html deleted file mode 100644 index 805e1b74..00000000 --- a/newspipe/web/templates/feeds.html +++ /dev/null @@ -1,7 +0,0 @@ -{% 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/web/templates/history.html b/newspipe/web/templates/history.html deleted file mode 100644 index ba567106..00000000 --- a/newspipe/web/templates/history.html +++ /dev/null @@ -1,26 +0,0 @@ -{% 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/web/templates/home.html b/newspipe/web/templates/home.html deleted file mode 100644 index 8da8dbe1..00000000 --- a/newspipe/web/templates/home.html +++ /dev/null @@ -1,135 +0,0 @@ -{% 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/web/templates/inactives.html b/newspipe/web/templates/inactives.html deleted file mode 100644 index e89a5fe1..00000000 --- a/newspipe/web/templates/inactives.html +++ /dev/null @@ -1,26 +0,0 @@ -{% 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/web/templates/layout.html b/newspipe/web/templates/layout.html deleted file mode 100644 index b40b540c..00000000 --- a/newspipe/web/templates/layout.html +++ /dev/null @@ -1,140 +0,0 @@ -<!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/web/templates/login.html b/newspipe/web/templates/login.html deleted file mode 100644 index 1253e2d3..00000000 --- a/newspipe/web/templates/login.html +++ /dev/null @@ -1,32 +0,0 @@ -{% 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/web/templates/management.html b/newspipe/web/templates/management.html deleted file mode 100644 index 22ebde8a..00000000 --- a/newspipe/web/templates/management.html +++ /dev/null @@ -1,99 +0,0 @@ -{% 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/web/templates/opml.xml b/newspipe/web/templates/opml.xml deleted file mode 100644 index 7159e279..00000000 --- a/newspipe/web/templates/opml.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?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/web/templates/popular.html b/newspipe/web/templates/popular.html deleted file mode 100644 index a80cee57..00000000 --- a/newspipe/web/templates/popular.html +++ /dev/null @@ -1,27 +0,0 @@ -{% 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/web/templates/profile.html b/newspipe/web/templates/profile.html deleted file mode 100644 index edbae368..00000000 --- a/newspipe/web/templates/profile.html +++ /dev/null @@ -1,68 +0,0 @@ -{% 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/web/templates/profile_public.html b/newspipe/web/templates/profile_public.html deleted file mode 100644 index 9ba3578c..00000000 --- a/newspipe/web/templates/profile_public.html +++ /dev/null @@ -1,45 +0,0 @@ -{% 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/web/templates/signup.html b/newspipe/web/templates/signup.html deleted file mode 100644 index 8d34b3bf..00000000 --- a/newspipe/web/templates/signup.html +++ /dev/null @@ -1,24 +0,0 @@ -{% 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/web/templates/user_stream.html b/newspipe/web/templates/user_stream.html deleted file mode 100644 index b05376a8..00000000 --- a/newspipe/web/templates/user_stream.html +++ /dev/null @@ -1,70 +0,0 @@ -{% 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 %} diff --git a/newspipe/web/views/__init__.py b/newspipe/web/views/__init__.py index a2f7bd1c..7d73ac8c 100644 --- a/newspipe/web/views/__init__.py +++ b/newspipe/web/views/__init__.py @@ -1,37 +1,9 @@ -from web.views.api import v2 -from web.views import views, home, session_mgmt -from web.views.article import article_bp, articles_bp -from web.views.feed import feed_bp, feeds_bp -from web.views.category import category_bp, categories_bp -from web.views.icon import icon_bp -from web.views.admin import admin_bp -from web.views.user import user_bp, users_bp -from web.views.bookmark import bookmark_bp, bookmarks_bp - -__all__ = [ - "views", - "home", - "session_mgmt", - "v2", - "article_bp", - "articles_bp", - "feed_bp", - "feeds_bp", - "category_bp", - "categories_bp", - "icon_bp", - "admin_bp", - "user_bp", - "users_bp", - "bookmark_bp", - "bookmarks_bp", -] - -import conf -from flask import request -from flask import g - - -@g.babel.localeselector -def get_locale(): - return request.accept_languages.best_match(conf.LANGUAGES.keys()) +from newspipe.web.views.api import v2 +from newspipe.web.views import views, home, session_mgmt +from newspipe.web.views.article import article_bp, articles_bp +from newspipe.web.views.feed import feed_bp, feeds_bp +from newspipe.web.views.category import category_bp, categories_bp +from newspipe.web.views.icon import icon_bp +from newspipe.web.views.admin import admin_bp +from newspipe.web.views.user import user_bp, users_bp +from newspipe.web.views.bookmark import bookmark_bp, bookmarks_bp diff --git a/newspipe/web/views/admin.py b/newspipe/web/views/admin.py index fe1b389b..7df683a7 100644 --- a/newspipe/web/views/admin.py +++ b/newspipe/web/views/admin.py @@ -3,10 +3,10 @@ from flask import Blueprint, render_template, redirect, flash, url_for from flask_babel import gettext, format_timedelta from flask_login import login_required, current_user -from lib.utils import redirect_url -from web.views.common import admin_permission -from web.controllers import UserController -from web.forms import InformationMessageForm, UserForm +from newspipe.lib.utils import redirect_url +from newspipe.controllers import UserController +from newspipe.web.views.common import admin_permission +from newspipe.web.forms import InformationMessageForm, UserForm admin_bp = Blueprint("admin", __name__, url_prefix="/admin") diff --git a/newspipe/web/views/api/v2/__init__.py b/newspipe/web/views/api/v2/__init__.py index ef587e72..d367da20 100644 --- a/newspipe/web/views/api/v2/__init__.py +++ b/newspipe/web/views/api/v2/__init__.py @@ -1,3 +1,3 @@ -from web.views.api.v2 import article, feed, category +from newspipe.web.views.api.v2 import article, feed, category __all__ = ["article", "feed", "category"] diff --git a/newspipe/web/views/api/v2/article.py b/newspipe/web/views/api/v2/article.py index 8da6c6dd..1dbf23b2 100644 --- a/newspipe/web/views/api/v2/article.py +++ b/newspipe/web/views/api/v2/article.py @@ -1,12 +1,12 @@ -from conf import API_ROOT +from newspipe.bootstrap import application import dateutil.parser from datetime import datetime from flask import current_app from flask_restful import Api -from web.views.common import api_permission -from web.controllers import ArticleController -from web.views.api.v2.common import ( +from newspipe.web.views.common import api_permission +from newspipe.controllers import ArticleController +from newspipe.web.views.api.v2.common import ( PyAggAbstractResource, PyAggResourceNew, PyAggResourceExisting, @@ -49,7 +49,7 @@ class ArticlesChallenge(PyAggAbstractResource): return result or None, 200 if result else 204 -api = Api(current_app, prefix=API_ROOT) +api = Api(current_app, prefix=application.config['API_ROOT']) api.add_resource(ArticleNewAPI, "/article", endpoint="article_new.json") api.add_resource(ArticleAPI, "/article/<int:obj_id>", endpoint="article.json") diff --git a/newspipe/web/views/api/v2/category.py b/newspipe/web/views/api/v2/category.py index a830624d..5f7ef354 100644 --- a/newspipe/web/views/api/v2/category.py +++ b/newspipe/web/views/api/v2/category.py @@ -1,9 +1,9 @@ -from conf import API_ROOT from flask import current_app from flask_restful import Api -from web.controllers.category import CategoryController -from web.views.api.v2.common import ( +from newspipe.bootstrap import application +from newspipe.controllers.category import CategoryController +from newspipe.web.views.api.v2.common import ( PyAggResourceNew, PyAggResourceExisting, PyAggResourceMulti, @@ -22,7 +22,7 @@ class CategoriesAPI(PyAggResourceMulti): controller_cls = CategoryController -api = Api(current_app, prefix=API_ROOT) +api = Api(current_app, prefix=application.config['API_ROOT']) api.add_resource(CategoryNewAPI, "/category", endpoint="category_new.json") api.add_resource(CategoryAPI, "/category/<int:obj_id>", endpoint="category.json") api.add_resource(CategoriesAPI, "/categories", endpoint="categories.json") diff --git a/newspipe/web/views/api/v2/common.py b/newspipe/web/views/api/v2/common.py index 81248422..3d90bf91 100644 --- a/newspipe/web/views/api/v2/common.py +++ b/newspipe/web/views/api/v2/common.py @@ -26,13 +26,13 @@ from flask import request from flask_restful import Resource, reqparse from flask_login import current_user -from web.views.common import ( +from newspipe.web.views.common import ( admin_permission, api_permission, login_user_bundle, jsonify, ) -from web.controllers import UserController +from newspipe.controllers import UserController logger = logging.getLogger(__name__) diff --git a/newspipe/web/views/api/v2/feed.py b/newspipe/web/views/api/v2/feed.py index 1e4fabf2..1081ed8c 100644 --- a/newspipe/web/views/api/v2/feed.py +++ b/newspipe/web/views/api/v2/feed.py @@ -1,11 +1,11 @@ -from conf import API_ROOT from flask import current_app from flask_restful import Api -from web.views.common import api_permission -from web.controllers.feed import FeedController, DEFAULT_MAX_ERROR, DEFAULT_LIMIT +from newspipe.bootstrap import application +from newspipe.web.views.common import api_permission +from newspipe.controllers.feed import FeedController, DEFAULT_MAX_ERROR, DEFAULT_LIMIT -from web.views.api.v2.common import ( +from newspipe.web.views.api.v2.common import ( PyAggAbstractResource, PyAggResourceNew, PyAggResourceExisting, @@ -39,7 +39,7 @@ class FetchableFeedAPI(PyAggAbstractResource): return result or None, 200 if result else 204 -api = Api(current_app, prefix=API_ROOT) +api = Api(current_app, prefix=application.config['API_ROOT']) api.add_resource(FeedNewAPI, "/feed", endpoint="feed_new.json") api.add_resource(FeedAPI, "/feed/<int:obj_id>", endpoint="feed.json") diff --git a/newspipe/web/views/article.py b/newspipe/web/views/article.py index c0c6f346..a49859ca 100644 --- a/newspipe/web/views/article.py +++ b/newspipe/web/views/article.py @@ -14,11 +14,11 @@ from flask_babel import gettext from flask_login import login_required, current_user -from bootstrap import db -from lib.utils import clear_string, redirect_url -from lib.data import export_json -from web.controllers import ArticleController, UserController, CategoryController -from web.lib.view_utils import etag_match +from newspipe.bootstrap import db +from newspipe.lib.utils import clear_string, redirect_url +from newspipe.lib.data import export_json +from newspipe.controllers import ArticleController, UserController, CategoryController +from newspipe.web.lib.view_utils import etag_match articles_bp = Blueprint("articles", __name__, url_prefix="/articles") article_bp = Blueprint("article", __name__, url_prefix="/article") diff --git a/newspipe/web/views/bookmark.py b/newspipe/web/views/bookmark.py index 2577f747..ea49b3c8 100644 --- a/newspipe/web/views/bookmark.py +++ b/newspipe/web/views/bookmark.py @@ -44,13 +44,12 @@ from flask_login import login_required, current_user from flask_paginate import Pagination, get_page_args from sqlalchemy import desc -import conf -from lib.utils import redirect_url -from lib.data import import_pinboard_json, export_bookmarks -from bootstrap import db -from web.forms import BookmarkForm -from web.controllers import BookmarkController, BookmarkTagController -from web.models import BookmarkTag +from newspipe.lib.utils import redirect_url +from newspipe.lib.data import import_pinboard_json, export_bookmarks +from newspipe.bootstrap import db +from newspipe.web.forms import BookmarkForm +from newspipe.controllers import BookmarkController, BookmarkTagController +from newspipe.models import BookmarkTag logger = logging.getLogger(__name__) bookmarks_bp = Blueprint("bookmarks", __name__, url_prefix="/bookmarks") diff --git a/newspipe/web/views/category.py b/newspipe/web/views/category.py index 1c897058..9f1a7e83 100644 --- a/newspipe/web/views/category.py +++ b/newspipe/web/views/category.py @@ -2,10 +2,10 @@ from flask import Blueprint, render_template, flash, redirect, url_for from flask_babel import gettext from flask_login import login_required, current_user -from web.forms import CategoryForm -from lib.utils import redirect_url -from web.lib.view_utils import etag_match -from web.controllers import ArticleController, FeedController, CategoryController +from newspipe.web.forms import CategoryForm +from newspipe.lib.utils import redirect_url +from newspipe.web.lib.view_utils import etag_match +from newspipe.controllers import ArticleController, FeedController, CategoryController categories_bp = Blueprint("categories", __name__, url_prefix="/categories") category_bp = Blueprint("category", __name__, url_prefix="/category") diff --git a/newspipe/web/views/common.py b/newspipe/web/views/common.py index c2d8e2df..8d9ecfa9 100644 --- a/newspipe/web/views/common.py +++ b/newspipe/web/views/common.py @@ -10,8 +10,8 @@ from flask_principal import ( session_identity_loader, identity_changed, ) -from web.controllers import UserController -from lib.utils import default_handler +from newspipe.controllers import UserController +from newspipe.lib.utils import default_handler admin_role = RoleNeed("admin") api_role = RoleNeed("api") diff --git a/newspipe/web/views/feed.py b/newspipe/web/views/feed.py index 592e3cbf..0be30668 100644 --- a/newspipe/web/views/feed.py +++ b/newspipe/web/views/feed.py @@ -17,12 +17,12 @@ from flask_babel import gettext from flask_login import login_required, current_user from flask_paginate import Pagination, get_page_args -import conf -from lib import misc_utils, utils -from lib.feed_utils import construct_feed_from -from web.lib.view_utils import etag_match -from web.forms import AddFeedForm -from web.controllers import ( +from newspipe.bootstrap import application +from newspipe.lib import misc_utils, utils +from newspipe.lib.feed_utils import construct_feed_from +from newspipe.web.lib.view_utils import etag_match +from newspipe.web.forms import AddFeedForm +from newspipe.controllers import ( UserController, CategoryController, FeedController, @@ -179,7 +179,7 @@ def bookmarklet(): ) feed = feed_contr.create(**feed) flash(gettext("Feed was successfully created."), "success") - if feed.enabled and conf.CRAWLING_METHOD == "default": + if feed.enabled and application.confg['CRAWLING_METHOD'] == "default": misc_utils.fetch(current_user.id, feed.id) flash(gettext("Downloading articles for the new feed..."), "info") return redirect(url_for("feed.form", feed_id=feed.id)) @@ -286,7 +286,7 @@ def process_form(feed_id=None): "success", ) - if conf.CRAWLING_METHOD == "default": + if application.confg['CRAWLING_METHOD'] == "default": misc_utils.fetch(current_user.id, new_feed.id) flash(gettext("Downloading articles for the new feed..."), "info") @@ -335,7 +335,7 @@ def export(): if not include_private: filter["private"] = False if not include_exceeded_error_count: - filter["error_count__lt"] = conf.DEFAULT_MAX_ERROR + filter["error_count__lt"] = application.confg['DEFAULT_MAX_ERROR'] user = UserController(current_user.id).get(id=current_user.id) feeds = FeedController(current_user.id).read(**filter) diff --git a/newspipe/web/views/home.py b/newspipe/web/views/home.py index 1cfa3601..66ce87ea 100644 --- a/newspipe/web/views/home.py +++ b/newspipe/web/views/home.py @@ -7,13 +7,13 @@ from flask_login import login_required, current_user from flask_babel import gettext, get_locale from babel.dates import format_datetime, format_timedelta -import conf -from lib.utils import redirect_url -from lib import misc_utils -from web.lib.view_utils import etag_match -from web.views.common import jsonify +from newspipe.bootstrap import application +from newspipe.lib.utils import redirect_url +from newspipe.lib import misc_utils +from newspipe.web.lib.view_utils import etag_match +from newspipe.web.views.common import jsonify -from web.controllers import FeedController, ArticleController, CategoryController +from newspipe.controllers import FeedController, ArticleController, CategoryController localize = pytz.utc.localize logger = logging.getLogger(__name__) @@ -181,7 +181,7 @@ def fetch(feed_id=None): Triggers the download of news. News are downloaded in a separated process. """ - if conf.CRAWLING_METHOD == "default" and current_user.is_admin: + if application.config['CRAWLING_METHOD'] == "default" and current_user.is_admin: misc_utils.fetch(current_user.id, feed_id) flash(gettext("Downloading articles..."), "info") else: diff --git a/newspipe/web/views/icon.py b/newspipe/web/views/icon.py index e1de6402..4cdcd4b0 100644 --- a/newspipe/web/views/icon.py +++ b/newspipe/web/views/icon.py @@ -1,7 +1,8 @@ import base64 from flask import Blueprint, Response, request -from web.controllers import IconController -from web.lib.view_utils import etag_match + +from newspipe.controllers import IconController +from newspipe.web.lib.view_utils import etag_match icon_bp = Blueprint("icon", __name__, url_prefix="/icon") diff --git a/newspipe/web/views/session_mgmt.py b/newspipe/web/views/session_mgmt.py index 809825d3..9bdb89cb 100644 --- a/newspipe/web/views/session_mgmt.py +++ b/newspipe/web/views/session_mgmt.py @@ -24,11 +24,11 @@ from flask_principal import ( session_identity_loader, ) -import conf -from web.views.common import admin_role, api_role, login_user_bundle -from web.controllers import UserController -from web.forms import SignupForm, SigninForm -from notifications import notifications +from newspipe.bootstrap import application +from newspipe.web.views.common import admin_role, api_role, login_user_bundle +from newspipe.controllers import UserController +from newspipe.web.forms import SignupForm, SigninForm +from newspipe.notifications import notifications Principal(current_app) # Create a permission with a single Need, in this case a RoleNeed. @@ -99,7 +99,7 @@ def logout(): @current_app.route("/signup", methods=["GET", "POST"]) def signup(): - if not conf.SELF_REGISTRATION: + if not application.config['SELF_REGISTRATION']: flash(gettext("Self-registration is disabled."), "warning") return redirect(url_for("home")) if current_user.is_authenticated: diff --git a/newspipe/web/views/user.py b/newspipe/web/views/user.py index 10974947..39eefb4c 100644 --- a/newspipe/web/views/user.py +++ b/newspipe/web/views/user.py @@ -6,12 +6,12 @@ from flask_babel import gettext from flask_login import login_required, current_user from flask_paginate import Pagination, get_page_args -import conf -from notifications import notifications -from lib import misc_utils -from lib.data import import_opml, import_json -from web.lib.user_utils import confirm_token -from web.controllers import ( +from newspipe.bootstrap import application +from newspipe.notifications import notifications +from newspipe.lib import misc_utils +from newspipe.lib.data import import_opml, import_json +from newspipe.web.lib.user_utils import confirm_token +from newspipe.controllers import ( UserController, FeedController, ArticleController, @@ -19,7 +19,7 @@ from web.controllers import ( BookmarkController, ) -from web.forms import ProfileForm +from newspipe.web.forms import ProfileForm users_bp = Blueprint("users", __name__, url_prefix="/users") user_bp = Blueprint("user", __name__, url_prefix="/user") @@ -115,7 +115,7 @@ def management(): else: try: nb = import_opml(current_user.nickname, data.read()) - if conf.CRAWLING_METHOD == "classic": + if application.config['CRAWLING_METHOD'] == "classic": misc_utils.fetch(current_user.id, None) flash(str(nb) + " " + gettext("feeds imported."), "success") flash(gettext("Downloading articles..."), "info") diff --git a/newspipe/web/views/views.py b/newspipe/web/views/views.py index e08270cd..6aa6ce54 100644 --- a/newspipe/web/views/views.py +++ b/newspipe/web/views/views.py @@ -6,11 +6,10 @@ from flask import request, render_template, flash, url_for, redirect, current_ap from flask_babel import gettext from sqlalchemy import desc -import conf -from web import __version__ -from conf import API_ROOT, ADMIN_EMAIL -from web.controllers import FeedController, UserController -from web.lib.view_utils import etag_match +from newspipe.bootstrap import application +from newspipe.web import __version__ +from newspipe.controllers import FeedController, UserController +from newspipe.web.lib.view_utils import etag_match logger = logging.getLogger(__name__) @@ -25,7 +24,7 @@ def authentication_required(error): @current_app.errorhandler(403) def authentication_failed(error): - if API_ROOT in request.url: + if application.conf['API_ROOT'] in request.url: return error flash(gettext("Forbidden."), "danger") return redirect(url_for("login")) @@ -71,7 +70,7 @@ def popular(): filters = {} filters["created_date__gt"] = not_added_before filters["private"] = False - filters["error_count__lt"] = conf.DEFAULT_MAX_ERROR + filters["error_count__lt"] = application.config['DEFAULT_MAX_ERROR'] feeds = FeedController().count_by_link(**filters) sorted_feeds = sorted(list(feeds.items()), key=operator.itemgetter(1), reverse=True) return render_template("popular.html", popular=sorted_feeds) @@ -80,7 +79,7 @@ def popular(): @current_app.route("/about", methods=["GET"]) @etag_match def about(): - return render_template("about.html", contact=ADMIN_EMAIL) + return render_template("about.html", contact=application.config['ADMIN_EMAIL']) @current_app.route("/about/more", methods=["GET"]) @@ -102,7 +101,7 @@ def about_more(): "about_more.html", newspipe_version=newspipe_version, version_url=version_url, - registration=[conf.SELF_REGISTRATION and "Open" or "Closed"][0], + registration=[application.config['SELF_REGISTRATION'] and "Open" or "Closed"][0], python_version="{}.{}.{}".format(*sys.version_info[:3]), nb_users=UserController().read().count(), ) |