extensions-website/extensions/views/mixins.py
Anna Sirota caae613747 Make it possible to fully delete unlisted/unrated extensions and versions (#81)
* removes all soft-deletion;
* shows a "Delete extension" button on the draft page in case it can be deleted;
* shows a "Delete version" button on the version page in case it can be deleted;
* a version can be deleted if
  * its file isn't approved, and it doesn't have any ratings;
* an extension can be deleted if
  * it's not listed, and doesn't have any ratings or abuse reports;
  * all it's versions can also be deleted;
* changes default `File.status` from `APPROVED` to `AWAITING_REVIEW`
  With version's file status being `APPROVED` by default, a version can never be deleted, even when the extension is still a draft.
  This change doesn't affect the approval process because
   * when an extension is approved its latest version becomes approved automatically (no change here);
   * when a new version is uploaded to an approved extension, it's approved automatically (this is new).

This allows authors to delete their drafts, freeing the extension slug and making it possible to re-upload the same file.
This also makes it possible to easily fix mistakes during the drafting of a new extension (e.g. delete a version and re-upload it without bumping a version for each typo/mistake in packaging and so on).
(see #78 and #63)

Reviewed-on: #81
2024-04-19 11:00:13 +02:00

79 lines
2.6 KiB
Python

from django.contrib.auth.mixins import UserPassesTestMixin
from django.shortcuts import get_object_or_404, redirect
from extensions.models import Extension
from files.models import File
class OwnsFileMixin(UserPassesTestMixin):
def dispatch(self, *args, **kwargs):
self.file = get_object_or_404(File, pk=self.kwargs['pk'])
self.extension = self._get_extension()
return super().dispatch(*args, **kwargs)
def test_func(self) -> bool:
return self.file.user == self.request.user
class ExtensionQuerysetMixin:
"""Add reusable methods to class-based views handling Extensions."""
def get_extension_queryset(self):
"""Return queryset with unlisted add-ons for logged in users under certain conditions."""
if self.request.user.is_staff:
return Extension.objects.all()
if self.request.user.is_authenticated:
return Extension.objects.listed_or_authored_by(user_id=self.request.user.pk)
return Extension.objects.listed
class MaintainedExtensionMixin:
"""Fetch an extension by slug if current user is a maintainer."""
def dispatch(self, *args, **kwargs):
self.extension = get_object_or_404(
Extension.objects.authored_by(user_id=self.request.user.pk),
slug=self.kwargs['slug'],
)
return super().dispatch(*args, **kwargs)
class ListedExtensionMixin:
"""Fetch a publicly listed extension by slug in the URL before dispatching the view."""
def dispatch(self, *args, **kwargs):
self.extension = get_object_or_404(Extension.objects.listed, slug=self.kwargs['slug'])
return super().dispatch(*args, **kwargs)
class ExtensionMixin:
"""Fetch an extension by slug in the URL before dispatching the view."""
def dispatch(self, *args, **kwargs):
self.extension = get_object_or_404(Extension, slug=self.kwargs['slug'])
return super().dispatch(*args, **kwargs)
class DraftVersionMixin:
"""Fetch the version object which is being edited as a draft."""
def dispatch(self, *args, **kwargs):
self.version = self.extension.versions.first()
return super().dispatch(*args, **kwargs)
class DraftMixin:
"""If the extension is incomplete, returns the FinalizeDraftView"""
def dispatch(self, request, *args, **kwargs):
extension = (
Extension.objects.listed_or_authored_by(user_id=self.request.user.pk)
.filter(status=Extension.STATUSES.INCOMPLETE)
.first()
)
if not extension:
return super().dispatch(request, *args, **kwargs)
return redirect(extension.get_draft_url())