You will need a user to add feeds now
This commit is contained in:
parent
61b9db1333
commit
2058054c99
5 changed files with 94 additions and 29 deletions
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
import datetime
|
||||
import logging
|
||||
from time import mktime, struct_time
|
||||
from typing import TYPE_CHECKING
|
||||
from urllib.parse import ParseResult, urlparse
|
||||
|
||||
import feedparser
|
||||
|
|
@ -11,6 +12,9 @@ from feedparser import FeedParserDict
|
|||
|
||||
from feeds.models import Author, Domain, Entry, Feed, Generator, Publisher
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from django.contrib.auth.models import AbstractBaseUser, AnonymousUser
|
||||
|
||||
logger: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
@ -205,8 +209,16 @@ def add_entry(feed: Feed, entry: FeedParserDict) -> Entry | None:
|
|||
return _entry
|
||||
|
||||
|
||||
def add_feed(url: str | None) -> None | Feed:
|
||||
"""Add a feed to the database."""
|
||||
def add_feed(url: str | None, user: AbstractBaseUser | AnonymousUser) -> Feed | None:
|
||||
"""Add a feed to the database.
|
||||
|
||||
Args:
|
||||
url: The URL of the feed.
|
||||
user: The user adding the feed.
|
||||
|
||||
Returns:
|
||||
The feed that was added.
|
||||
"""
|
||||
# Parse the feed.
|
||||
parsed_feed: dict | None = parse_feed(url=url)
|
||||
if not parsed_feed:
|
||||
|
|
@ -233,6 +245,7 @@ def add_feed(url: str | None) -> None | Feed:
|
|||
# Create the feed
|
||||
feed = Feed(
|
||||
feed_url=url,
|
||||
user=user,
|
||||
domain=domain,
|
||||
last_checked=timezone.now(),
|
||||
bozo=parsed_feed.get("bozo", 0),
|
||||
|
|
|
|||
21
feeds/migrations/0003_feed_user.py
Normal file
21
feeds/migrations/0003_feed_user.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 5.0.2 on 2024-02-23 05:38
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('feeds', '0002_alter_author_options_alter_domain_options_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='user',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
|
|
@ -7,7 +7,7 @@ from typing import Literal
|
|||
from django.db import models
|
||||
from django.db.models import JSONField
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Domain(models.Model):
|
||||
|
|
@ -129,6 +129,8 @@ class Feed(models.Model):
|
|||
|
||||
feed_url = models.URLField(unique=True)
|
||||
|
||||
# The user that added the feed
|
||||
user = models.ForeignKey("auth.User", on_delete=models.SET_NULL, null=True, blank=True)
|
||||
domain = models.ForeignKey(Domain, on_delete=models.CASCADE)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
modified_at = models.DateTimeField(auto_now=True)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import login
|
||||
|
|
@ -33,6 +33,7 @@ class IndexView(View):
|
|||
"keywords": "feed, rss, atom, archive, rss list",
|
||||
"author": "TheLovinator",
|
||||
"canonical": "https://feedvault.se/",
|
||||
"title": "FeedVault",
|
||||
}
|
||||
return HttpResponse(content=template.render(context=context, request=request))
|
||||
|
||||
|
|
@ -56,6 +57,7 @@ class FeedView(View):
|
|||
"keywords": "feed, rss, atom, archive, rss list",
|
||||
"author": f"{feed.author_detail.name if feed.author_detail else "FeedVault"}",
|
||||
"canonical": f"https://feedvault.se/feed/{feed_id}/",
|
||||
"title": f"{feed.title} - FeedVault",
|
||||
}
|
||||
|
||||
return render(request, "feed.html", context)
|
||||
|
|
@ -72,10 +74,12 @@ class FeedsView(ListView):
|
|||
def get_context_data(self, **kwargs) -> dict: # noqa: ANN003
|
||||
"""Get the context data."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["description"] = "Archive of all feeds"
|
||||
feed_amount: int = Feed.objects.count() or 0
|
||||
context["description"] = f"Archiving {feed_amount} feeds"
|
||||
context["keywords"] = "feed, rss, atom, archive, rss list"
|
||||
context["author"] = "TheLovinator"
|
||||
context["canonical"] = "https://feedvault.se/feeds/"
|
||||
context["title"] = "Feeds"
|
||||
return context
|
||||
|
||||
|
||||
|
|
@ -95,13 +99,19 @@ class AddView(View):
|
|||
|
||||
def post(self, request: HttpRequest) -> HttpResponse:
|
||||
"""Add a feed."""
|
||||
if not request.user.is_authenticated:
|
||||
return HttpResponse(content="Not logged in", status=401)
|
||||
|
||||
if not request.user.is_active:
|
||||
return HttpResponse(content="User is not active", status=403)
|
||||
|
||||
urls: str | None = request.POST.get("urls", None)
|
||||
if not urls:
|
||||
return HttpResponse(content="No urls", status=400)
|
||||
|
||||
# Split the urls by newline.
|
||||
for url in urls.split("\n"):
|
||||
feed: None | Feed = add_feed(url)
|
||||
feed: None | Feed = add_feed(url, request.user)
|
||||
if not feed:
|
||||
messages.error(request, f"{url} - Failed to add")
|
||||
continue
|
||||
|
|
@ -132,13 +142,19 @@ class UploadView(View):
|
|||
|
||||
def post(self, request: HttpRequest) -> HttpResponse:
|
||||
"""Upload a file."""
|
||||
if not request.user.is_authenticated:
|
||||
return HttpResponse(content="Not logged in", status=401)
|
||||
|
||||
if not request.user.is_active:
|
||||
return HttpResponse(content="User is not active", status=403)
|
||||
|
||||
file = request.FILES.get("file", None)
|
||||
if not file:
|
||||
return HttpResponse(content="No file", status=400)
|
||||
|
||||
# Split the urls by newline.
|
||||
for url in file.read().decode("utf-8").split("\n"):
|
||||
feed: None | Feed = add_feed(url)
|
||||
feed: None | Feed = add_feed(url, request.user)
|
||||
if not feed:
|
||||
messages.error(request, f"{url} - Failed to add")
|
||||
continue
|
||||
|
|
@ -172,6 +188,17 @@ class RegisterView(CreateView):
|
|||
form_class = UserCreationForm
|
||||
success_url = reverse_lazy("feeds:login")
|
||||
|
||||
# Add context data to the view
|
||||
def get_context_data(self, **kwargs) -> dict: # noqa: ANN003
|
||||
"""Get the context data."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["description"] = "Register a new account"
|
||||
context["keywords"] = "register, account, feed, rss, atom, archive, rss list"
|
||||
context["author"] = "TheLovinator"
|
||||
context["canonical"] = "https://feedvault.se/accounts/register/"
|
||||
context["title"] = "Register"
|
||||
return context
|
||||
|
||||
|
||||
class CustomLogoutView(LogoutView):
|
||||
"""Logout view."""
|
||||
|
|
@ -186,6 +213,17 @@ class CustomPasswordChangeView(SuccessMessageMixin, PasswordChangeView):
|
|||
success_url = reverse_lazy("feeds:index")
|
||||
success_message = "Your password was successfully updated!"
|
||||
|
||||
# Add context data to the view
|
||||
def get_context_data(self, **kwargs) -> dict: # noqa: ANN003
|
||||
"""Get the context data."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["description"] = "Change your password"
|
||||
context["keywords"] = "change, password, account, feed, rss, atom, archive, rss list"
|
||||
context["author"] = "TheLovinator"
|
||||
context["canonical"] = "https://feedvault.se/accounts/change-password/"
|
||||
context["title"] = "Change password"
|
||||
return context
|
||||
|
||||
|
||||
class ProfileView(View):
|
||||
"""Profile page."""
|
||||
|
|
@ -193,11 +231,16 @@ class ProfileView(View):
|
|||
def get(self, request: HttpRequest) -> HttpResponse:
|
||||
"""Load the profile page."""
|
||||
template = loader.get_template(template_name="accounts/profile.html")
|
||||
context = {
|
||||
"description": "FeedVault allows users to archive and search their favorite web feeds.",
|
||||
"keywords": "feed, rss, atom, archive, rss list",
|
||||
"author": "TheLovinator",
|
||||
"canonical": "https://feedvault.se/",
|
||||
|
||||
user_feeds = Feed.objects.filter(user=request.user).order_by("-created_at")[:100]
|
||||
|
||||
context: dict[str, str | Any] = {
|
||||
"description": f"Profile page for {request.user.get_username()}",
|
||||
"keywords": f"profile, account, {request.user.get_username()}",
|
||||
"author": f"{request.user.get_username()}",
|
||||
"canonical": "https://feedvault.se/accounts/profile/",
|
||||
"title": f"{request.user.get_username()}",
|
||||
"user_feeds": user_feeds,
|
||||
}
|
||||
return HttpResponse(content=template.render(context=context, request=request))
|
||||
|
||||
|
|
@ -213,5 +256,6 @@ class APIView(View):
|
|||
"keywords": "feed, rss, atom, archive, rss list",
|
||||
"author": "TheLovinator",
|
||||
"canonical": "https://feedvault.se/api/",
|
||||
"title": "API Documentation",
|
||||
}
|
||||
return HttpResponse(content=template.render(context=context, request=request))
|
||||
|
|
|
|||
|
|
@ -3,25 +3,10 @@
|
|||
<h2>{{ user.username }}</h2>
|
||||
<h3>Feeds</h3>
|
||||
<ul>
|
||||
{% for feed in feeds %}
|
||||
{% for feed in user_feeds %}
|
||||
<li>
|
||||
<a href='{% url "feeds:feed" feed.id %}'>{{ feed.title }}</a>
|
||||
<a href='{% url "feeds:feed" feed.id %}'>{{ feed.feed_url }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<h3>Subscriptions</h3>
|
||||
<ul>
|
||||
{% for subscription in subscriptions %}
|
||||
<li>
|
||||
<a href='{% url "feeds:feed" subscription.id %}'>{{ subscription.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<h3>Subscribers</h3>
|
||||
<p>
|
||||
<form action="{% url 'feeds:logout' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit">Logout</button>
|
||||
</form>
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue