Merge branch 'main' into partially-read-shelf
This commit is contained in:
commit
e9dfa42e11
209 changed files with 14317 additions and 4796 deletions
|
@ -2,6 +2,7 @@
|
|||
# site admin
|
||||
from .admin.announcements import Announcements, Announcement
|
||||
from .admin.announcements import EditAnnouncement, delete_announcement
|
||||
from .admin.automod import AutoMod, automod_delete, run_automod
|
||||
from .admin.dashboard import Dashboard
|
||||
from .admin.federation import Federation, FederatedServer
|
||||
from .admin.federation import AddFederatedServer, ImportServerBlocklist
|
||||
|
@ -113,6 +114,7 @@ from .reading import ReadingStatus
|
|||
from .report import Report
|
||||
from .rss_feed import RssFeed
|
||||
from .search import Search
|
||||
from .setup import InstanceConfig, CreateAdmin
|
||||
from .status import CreateStatus, EditStatus, DeleteStatus, update_progress
|
||||
from .status import edit_readthrough
|
||||
from .updates import get_notification_count, get_unread_status_string
|
||||
|
|
64
bookwyrm/views/admin/automod.py
Normal file
64
bookwyrm/views/admin/automod.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
""" moderation via flagged posts and users """
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from bookwyrm import forms, models
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
@method_decorator(
|
||||
permission_required("bookwyrm.moderate_user", raise_exception=True),
|
||||
name="dispatch",
|
||||
)
|
||||
@method_decorator(
|
||||
permission_required("bookwyrm.moderate_post", raise_exception=True),
|
||||
name="dispatch",
|
||||
)
|
||||
# pylint: disable=no-self-use
|
||||
class AutoMod(View):
|
||||
"""Manage automated flagging"""
|
||||
|
||||
def get(self, request):
|
||||
"""view rules"""
|
||||
data = {"rules": models.AutoMod.objects.all(), "form": forms.AutoModRuleForm()}
|
||||
return TemplateResponse(request, "settings/automod/rules.html", data)
|
||||
|
||||
def post(self, request):
|
||||
"""add rule"""
|
||||
form = forms.AutoModRuleForm(request.POST)
|
||||
success = form.is_valid()
|
||||
if success:
|
||||
form.save()
|
||||
form = forms.AutoModRuleForm()
|
||||
|
||||
data = {
|
||||
"rules": models.AutoMod.objects.all(),
|
||||
"form": form,
|
||||
"success": success,
|
||||
}
|
||||
return TemplateResponse(request, "settings/automod/rules.html", data)
|
||||
|
||||
|
||||
@require_POST
|
||||
@permission_required("bookwyrm.moderate_user", raise_exception=True)
|
||||
@permission_required("bookwyrm.moderate_post", raise_exception=True)
|
||||
# pylint: disable=unused-argument
|
||||
def automod_delete(request, rule_id):
|
||||
"""Remove a rule"""
|
||||
rule = get_object_or_404(models.AutoMod, id=rule_id)
|
||||
rule.delete()
|
||||
return redirect("settings-automod")
|
||||
|
||||
|
||||
@require_POST
|
||||
@permission_required("bookwyrm.moderate_user", raise_exception=True)
|
||||
@permission_required("bookwyrm.moderate_post", raise_exception=True)
|
||||
# pylint: disable=unused-argument
|
||||
def run_automod(request):
|
||||
"""run scan"""
|
||||
models.automod_task.delay()
|
||||
return redirect("settings-automod")
|
|
@ -1,6 +1,7 @@
|
|||
""" instance overview """
|
||||
from datetime import timedelta
|
||||
from dateutil.parser import parse
|
||||
from packaging import version
|
||||
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.db.models import Q
|
||||
|
@ -9,7 +10,9 @@ from django.utils import timezone
|
|||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm import models, settings
|
||||
from bookwyrm.connectors.abstract_connector import get_data
|
||||
from bookwyrm.connectors.connector_manager import ConnectorException
|
||||
|
||||
|
||||
# pylint: disable= no-self-use
|
||||
|
@ -107,6 +110,19 @@ class Dashboard(View):
|
|||
"register_stats": register_chart.get_chart(start, end, interval),
|
||||
"works_stats": works_chart.get_chart(start, end, interval),
|
||||
}
|
||||
|
||||
# check version
|
||||
try:
|
||||
release = get_data(settings.RELEASE_API, timeout=3)
|
||||
available_version = release.get("tag_name", None)
|
||||
if available_version and version.parse(available_version) > version.parse(
|
||||
settings.VERSION
|
||||
):
|
||||
data["current_version"] = settings.VERSION
|
||||
data["available_version"] = available_version
|
||||
except ConnectorException:
|
||||
pass
|
||||
|
||||
return TemplateResponse(request, "settings/dashboard/dashboard.html", data)
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
""" manage site settings """
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.shortcuts import redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
|
@ -32,7 +31,8 @@ class Site(View):
|
|||
return TemplateResponse(request, "settings/site.html", data)
|
||||
form.save()
|
||||
|
||||
return redirect("settings-site")
|
||||
data = {"site_form": forms.SiteForm(instance=site), "success": True}
|
||||
return TemplateResponse(request, "settings/site.html", data)
|
||||
|
||||
|
||||
@login_required
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
""" the good people stuff! the authors! """
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Q
|
||||
from django.db.models import Avg, Q
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
|
@ -28,7 +28,8 @@ class Author(View):
|
|||
|
||||
books = (
|
||||
models.Work.objects.filter(Q(authors=author) | Q(editions__authors=author))
|
||||
.order_by("-published_date")
|
||||
.annotate(Avg("editions__review__rating"))
|
||||
.order_by("editions__review__rating__avg")
|
||||
.distinct()
|
||||
)
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
""" non-interactive pages """
|
||||
from django.shortcuts import redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.views import View
|
||||
|
||||
from bookwyrm import forms
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.views.feed import Feed
|
||||
|
||||
|
||||
|
@ -15,6 +16,11 @@ class Home(View):
|
|||
if request.user.is_authenticated:
|
||||
feed_view = Feed.as_view()
|
||||
return feed_view(request, "home")
|
||||
site = models.SiteSettings.objects.get()
|
||||
|
||||
if site.install_mode:
|
||||
return redirect("setup")
|
||||
|
||||
landing_view = Landing.as_view()
|
||||
return landing_view(request)
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@ class Register(View):
|
|||
def post(self, request):
|
||||
"""join the server"""
|
||||
settings = models.SiteSettings.get()
|
||||
# no registration allowed when the site is being installed
|
||||
if settings.install_mode:
|
||||
raise PermissionDenied()
|
||||
|
||||
if not settings.allow_registration:
|
||||
invite_code = request.POST.get("invite_code")
|
||||
|
||||
|
@ -38,9 +42,16 @@ class Register(View):
|
|||
invite = None
|
||||
|
||||
form = forms.RegisterForm(request.POST)
|
||||
errors = False
|
||||
if not form.is_valid():
|
||||
errors = True
|
||||
data = {
|
||||
"login_form": forms.LoginForm(),
|
||||
"register_form": form,
|
||||
"invite": invite,
|
||||
"valid": invite.valid() if invite else True,
|
||||
}
|
||||
if invite:
|
||||
return TemplateResponse(request, "landing/invite.html", data)
|
||||
return TemplateResponse(request, "landing/login.html", data)
|
||||
|
||||
localname = form.data["localname"].strip()
|
||||
email = form.data["email"]
|
||||
|
@ -52,22 +63,6 @@ class Register(View):
|
|||
# treat this like a successful registration, but don't do anything
|
||||
return redirect("confirm-email")
|
||||
|
||||
# check localname and email uniqueness
|
||||
if models.User.objects.filter(localname=localname).first():
|
||||
form.errors["localname"] = ["User with this username already exists"]
|
||||
errors = True
|
||||
|
||||
if errors:
|
||||
data = {
|
||||
"login_form": forms.LoginForm(),
|
||||
"register_form": form,
|
||||
"invite": invite,
|
||||
"valid": invite.valid() if invite else True,
|
||||
}
|
||||
if invite:
|
||||
return TemplateResponse(request, "landing/invite.html", data)
|
||||
return TemplateResponse(request, "landing/login.html", data)
|
||||
|
||||
username = f"{localname}@{DOMAIN}"
|
||||
user = models.User.objects.create_user(
|
||||
username,
|
||||
|
|
99
bookwyrm/views/setup.py
Normal file
99
bookwyrm/views/setup.py
Normal file
|
@ -0,0 +1,99 @@
|
|||
""" Installation wizard 🧙 """
|
||||
import re
|
||||
|
||||
from django.contrib.auth import login
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db import transaction
|
||||
from django.shortcuts import redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.views import View
|
||||
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm import settings
|
||||
from bookwyrm.utils import regex
|
||||
|
||||
|
||||
# pylint: disable= no-self-use
|
||||
class InstanceConfig(View):
|
||||
"""make sure the instance looks correct before adding any data"""
|
||||
|
||||
def get(self, request):
|
||||
"""Check out this cool instance"""
|
||||
# only allow this view when an instance is being configured
|
||||
site = models.SiteSettings.objects.get()
|
||||
if not site.install_mode:
|
||||
raise PermissionDenied()
|
||||
|
||||
# check for possible problems with the instance configuration
|
||||
warnings = {}
|
||||
warnings["debug"] = settings.DEBUG
|
||||
warnings["invalid_domain"] = not re.match(rf"^{regex.DOMAIN}$", settings.DOMAIN)
|
||||
warnings["protocol"] = not settings.DEBUG and not settings.USE_HTTPS
|
||||
|
||||
# pylint: disable=line-too-long
|
||||
data = {
|
||||
"warnings": warnings,
|
||||
"info": {
|
||||
"domain": settings.DOMAIN,
|
||||
"version": settings.VERSION,
|
||||
"use_https": settings.USE_HTTPS,
|
||||
"language": settings.LANGUAGE_CODE,
|
||||
"use_s3": settings.USE_S3,
|
||||
"email_sender": f"{settings.EMAIL_SENDER_NAME}@{settings.EMAIL_SENDER_DOMAIN}",
|
||||
"preview_images": settings.ENABLE_PREVIEW_IMAGES,
|
||||
"thumbnails": settings.ENABLE_THUMBNAIL_GENERATION,
|
||||
},
|
||||
}
|
||||
return TemplateResponse(request, "setup/config.html", data)
|
||||
|
||||
|
||||
class CreateAdmin(View):
|
||||
"""manage things like the instance name"""
|
||||
|
||||
def get(self, request):
|
||||
"""Create admin user form"""
|
||||
# only allow this view when an instance is being configured
|
||||
site = models.SiteSettings.objects.get()
|
||||
if not site.install_mode:
|
||||
raise PermissionDenied()
|
||||
|
||||
data = {"register_form": forms.RegisterForm()}
|
||||
return TemplateResponse(request, "setup/admin.html", data)
|
||||
|
||||
@transaction.atomic
|
||||
def post(self, request):
|
||||
"""Create that user"""
|
||||
site = models.SiteSettings.objects.get()
|
||||
# you can't create an admin user if you're in config mode
|
||||
if not site.install_mode:
|
||||
raise PermissionDenied()
|
||||
|
||||
form = forms.RegisterForm(request.POST)
|
||||
if not form.is_valid():
|
||||
data = {"register_form": form}
|
||||
return TemplateResponse(request, "setup/admin.html", data)
|
||||
|
||||
localname = form.data["localname"].strip()
|
||||
username = f"{localname}@{settings.DOMAIN}"
|
||||
|
||||
user = models.User.objects.create_superuser(
|
||||
username,
|
||||
form.data["email"],
|
||||
form.data["password"],
|
||||
localname=localname,
|
||||
local=True,
|
||||
deactivation_reason=None,
|
||||
is_active=True,
|
||||
)
|
||||
# Set "admin" role
|
||||
try:
|
||||
user.groups.set(Group.objects.filter(name__in=["admin", "moderator"]))
|
||||
except Group.DoesNotExist:
|
||||
# this should only happen in tests
|
||||
pass
|
||||
|
||||
login(request, user)
|
||||
site.install_mode = False
|
||||
site.save()
|
||||
return redirect("settings-site")
|
Loading…
Add table
Add a link
Reference in a new issue