aboutsummaryrefslogtreecommitdiff
path: root/newspipe/web
diff options
context:
space:
mode:
Diffstat (limited to 'newspipe/web')
-rw-r--r--newspipe/web/controllers/__init__.py18
-rw-r--r--newspipe/web/controllers/abstract.py177
-rw-r--r--newspipe/web/controllers/article.py107
-rw-r--r--newspipe/web/controllers/bookmark.py35
-rw-r--r--newspipe/web/controllers/category.py13
-rw-r--r--newspipe/web/controllers/feed.py105
-rw-r--r--newspipe/web/controllers/icon.py27
-rw-r--r--newspipe/web/controllers/tag.py22
-rw-r--r--newspipe/web/controllers/user.py28
-rw-r--r--newspipe/web/forms.py6
-rw-r--r--newspipe/web/lib/user_utils.py6
-rw-r--r--newspipe/web/lib/view_utils.py2
-rw-r--r--newspipe/web/models/__init__.py98
-rw-r--r--newspipe/web/models/article.py101
-rw-r--r--newspipe/web/models/bookmark.py72
-rw-r--r--newspipe/web/models/category.py28
-rw-r--r--newspipe/web/models/feed.py105
-rw-r--r--newspipe/web/models/icon.py10
-rw-r--r--newspipe/web/models/right_mixin.py64
-rw-r--r--newspipe/web/models/role.py40
-rw-r--r--newspipe/web/models/tag.py44
-rw-r--r--newspipe/web/models/user.py114
-rw-r--r--newspipe/web/static/css/custom.css80
-rw-r--r--newspipe/web/static/img/favicon.icobin1150 -> 0 bytes
-rw-r--r--newspipe/web/static/img/newspipe.pngbin1547 -> 0 bytes
-rw-r--r--newspipe/web/static/img/newspipe.svg84
-rw-r--r--newspipe/web/static/img/pinboard.pngbin597 -> 0 bytes
-rwxr-xr-xnewspipe/web/static/img/reddit.pngbin525 -> 0 bytes
-rw-r--r--newspipe/web/static/img/twitter.pngbin640 -> 0 bytes
-rw-r--r--newspipe/web/static/js/articles.js191
-rw-r--r--newspipe/web/static/js/feed.js22
-rw-r--r--newspipe/web/templates/about.html26
-rw-r--r--newspipe/web/templates/about_more.html11
-rw-r--r--newspipe/web/templates/admin/create_user.html26
-rw-r--r--newspipe/web/templates/admin/dashboard.html68
-rw-r--r--newspipe/web/templates/article.html35
-rw-r--r--newspipe/web/templates/article_pub.html24
-rw-r--r--newspipe/web/templates/bookmarks.html81
-rw-r--r--newspipe/web/templates/categories.html36
-rw-r--r--newspipe/web/templates/duplicates.html30
-rw-r--r--newspipe/web/templates/edit_bookmark.html84
-rw-r--r--newspipe/web/templates/edit_category.html23
-rw-r--r--newspipe/web/templates/edit_feed.html98
-rw-r--r--newspipe/web/templates/emails/account_activation.txt9
-rw-r--r--newspipe/web/templates/emails/new_password.txt8
-rw-r--r--newspipe/web/templates/errors/404.html12
-rw-r--r--newspipe/web/templates/errors/500.html12
-rw-r--r--newspipe/web/templates/feed.html76
-rw-r--r--newspipe/web/templates/feed_list.html55
-rw-r--r--newspipe/web/templates/feed_list_per_categories.html52
-rw-r--r--newspipe/web/templates/feed_list_simple.html35
-rw-r--r--newspipe/web/templates/feeds.html7
-rw-r--r--newspipe/web/templates/history.html26
-rw-r--r--newspipe/web/templates/home.html135
-rw-r--r--newspipe/web/templates/inactives.html26
-rw-r--r--newspipe/web/templates/layout.html140
-rw-r--r--newspipe/web/templates/login.html32
-rw-r--r--newspipe/web/templates/management.html99
-rw-r--r--newspipe/web/templates/opml.xml13
-rw-r--r--newspipe/web/templates/popular.html27
-rw-r--r--newspipe/web/templates/profile.html68
-rw-r--r--newspipe/web/templates/profile_public.html45
-rw-r--r--newspipe/web/templates/signup.html24
-rw-r--r--newspipe/web/templates/user_stream.html70
-rw-r--r--newspipe/web/views/__init__.py46
-rw-r--r--newspipe/web/views/admin.py8
-rw-r--r--newspipe/web/views/api/v2/__init__.py2
-rw-r--r--newspipe/web/views/api/v2/article.py10
-rw-r--r--newspipe/web/views/api/v2/category.py8
-rw-r--r--newspipe/web/views/api/v2/common.py4
-rw-r--r--newspipe/web/views/api/v2/feed.py10
-rw-r--r--newspipe/web/views/article.py10
-rw-r--r--newspipe/web/views/bookmark.py13
-rw-r--r--newspipe/web/views/category.py8
-rw-r--r--newspipe/web/views/common.py4
-rw-r--r--newspipe/web/views/feed.py18
-rw-r--r--newspipe/web/views/home.py14
-rw-r--r--newspipe/web/views/icon.py5
-rw-r--r--newspipe/web/views/session_mgmt.py12
-rw-r--r--newspipe/web/views/user.py16
-rw-r--r--newspipe/web/views/views.py17
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
deleted file mode 100644
index 5b056c1e..00000000
--- a/newspipe/web/static/img/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/newspipe/web/static/img/newspipe.png b/newspipe/web/static/img/newspipe.png
deleted file mode 100644
index c3ba5029..00000000
--- a/newspipe/web/static/img/newspipe.png
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index 6dddc10b..00000000
--- a/newspipe/web/static/img/pinboard.png
+++ /dev/null
Binary files differ
diff --git a/newspipe/web/static/img/reddit.png b/newspipe/web/static/img/reddit.png
deleted file mode 100755
index 2d615f2a..00000000
--- a/newspipe/web/static/img/reddit.png
+++ /dev/null
Binary files differ
diff --git a/newspipe/web/static/img/twitter.png b/newspipe/web/static/img/twitter.png
deleted file mode 100644
index fc11c4ce..00000000
--- a/newspipe/web/static/img/twitter.png
+++ /dev/null
Binary files differ
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 %}&nbsp;(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>&nbsp;⸱&nbsp;
- <a class="text-muted" href="{{ url_for('bookmarks.list_') + 'private' }}">private</a>&nbsp;⸱&nbsp;
- <a class="text-muted" href="{{ url_for('bookmarks.list_') + 'public' }}">public</a>&nbsp;⸱&nbsp;
- <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>&nbsp;&nbsp;{{ tag }}
- {% endif %}
- {% if query %}
- {% if tag %}<br />{% endif %}
- <i class="fa fa-search" aria-hidden="true"></i>&nbsp;&nbsp;{{ 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 }}&nbsp;</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 &middot; 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>&nbsp;<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>&nbsp;<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" />&nbsp;{% 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" />&nbsp;{% 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" />&nbsp;{% 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>&nbsp{{ _('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 %}&nbsp;{{ 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>&nbsp;{{ _('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="/">🗞&nbsp;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>&nbsp;{{ _('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>&nbsp;{{ _('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>&nbsp;{{ _('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>&nbsp;{{ _('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">&times;</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>&nbsp;&#8231;&nbsp;<a href="{{ url_for('popular', nb_days=365) }}">last year</a>&nbsp;&#8231;&nbsp;<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>&nbsp;
- {{ _('Member since') }}: {{ user.date_created | datetime }}
- </p>
- <p>
- <span class="fa fa-clock-o" aria-hidden="true"></span>&nbsp;
- {{ _('Last seen') }}: {{ user.last_seen | datetime }}
- </p>
- {% if user.webpage %}
- <p>
- <span class="fa fa-home" aria-hidden="true"></span>&nbsp;
- {{ _('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>&nbsp;
- {{ _('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(),
)
bgstack15