Extension page should be 404 for everyone unless publicly listed #167

Merged
Anna Sirota merged 5 commits from extension-detail-page-listed-only into main 2024-06-05 12:12:14 +02:00
4 changed files with 100 additions and 51 deletions

View File

@ -5,7 +5,7 @@ from django.urls import reverse
from common.tests.factories.extensions import create_version, create_approved_version
from common.tests.factories.teams import TeamFactory
from common.tests.factories.users import UserFactory
from common.tests.factories.users import UserFactory, create_moderator
from extensions.models import Extension, Version
from files.models import File
from teams.models import Team, TeamsUsers
@ -90,6 +90,54 @@ class PublicViewsTest(_BaseTestCase):
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'extensions/home.html')
def test_no_one_can_view_extension_page_when_not_listed_404(self):
extension = create_version(extension__is_listed=False).extension
moderator = create_moderator()
staff = UserFactory(is_staff=True)
superuser = UserFactory(is_superuser=True, is_staff=True)
author = extension.authors.first()
url = extension.get_absolute_url()
response = self.client.get(url)
for account, role in (
(None, 'anonymous'),
(moderator, 'moderator'),
(staff, 'staff'),
(superuser, 'superuser'),
(author, 'author'),
):
with self.subTest(account=account, role=role):
self.client.logout()
if account:
self.client.force_login(account)
self.assertEqual(response.status_code, 404)
def test_anyone_can_view_extension_page_when_listed(self):
extension = create_approved_version().extension
moderator = create_moderator()
staff = UserFactory(is_staff=True)
superuser = UserFactory(is_superuser=True, is_staff=True)
author = extension.authors.first()
url = extension.get_absolute_url()
response = self.client.get(url)
for account, role in (
(None, 'anonymous'),
(moderator, 'moderator'),
(staff, 'staff'),
(superuser, 'superuser'),
(author, 'author'),
):
with self.subTest(role=role):
self.client.logout()
if account:
self.client.force_login(account)
self.assertEqual(response.status_code, 200)
class ApiViewsTest(_BaseTestCase):
def test_blender_version_filter(self):

View File

@ -28,6 +28,14 @@ urlpatterns = [
path('search/', public.SearchView.as_view(), name='search'),
path('tag/<slug:tag_slug>/', public.SearchView.as_view(), name='by-tag'),
path('team/<slug:team_slug>/', public.SearchView.as_view(), name='by-team'),
re_path(
rf'^(?P<type_slug>{EXTENSION_SLUGS_PATH})/',
include(
[
path('<slug:slug>/', public.ExtensionDetailView.as_view(), name='detail'),
]
),
),
re_path(
rf'^(?P<type_slug>{EXTENSION_SLUGS_PATH})/$',
public.SearchView.as_view(),
@ -84,7 +92,6 @@ urlpatterns = [
name='version-download',
),
path('<slug:slug>/versions/', manage.VersionsView.as_view(), name='versions'),
path('<slug:slug>/', manage.ExtensionDetailView.as_view(), name='detail'),
],
),
),

View File

@ -3,7 +3,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.db import transaction
from django.shortcuts import get_object_or_404, redirect, reverse
from django.views.generic import DetailView, ListView
from django.views.generic import ListView
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
@ -22,52 +22,6 @@ from extensions.forms import (
from extensions.models import Extension, Version
from files.forms import FileForm
from files.models import File
from stats.models import ExtensionView
import ratings.models
class ExtensionDetailView(ExtensionQuerysetMixin, DetailView):
model = Extension
context_object_name = 'extension'
template_name = 'extensions/detail.html'
def get_queryset(self):
"""Allow logged in users to view unlisted add-ons in certain conditions.
* maintainers should be able to preview their yet unlisted add-ons;
* staff should be able to preview yet unlisted add-ons;
"""
return self.get_extension_queryset().prefetch_related(
'authors',
'ratings',
'ratings__user',
'versions',
'versions__file',
'versions__file__validation',
'versions__permissions',
'versions__platforms',
)
def get_object(self, queryset=None):
"""Record a page view when returning the Extension object."""
obj = super().get_object(queryset=queryset)
if obj.is_listed and (
self.request.user.is_anonymous or not obj.has_maintainer(self.request.user)
):
ExtensionView.create_from_request(self.request, object_id=obj.pk)
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.user.is_authenticated:
context['my_rating'] = ratings.models.Rating.get_for(
self.request.user.pk, self.object.pk
)
extension = context['object']
# Add the image for "og:image" meta to the context
if extension.featured_image and extension.featured_image.is_listed:
context['default_image_path'] = extension.featured_image.thumbnail_1080p_url
return context
class VersionsView(ExtensionQuerysetMixin, ListView):

View File

@ -6,8 +6,7 @@ from django.db import connection
from django.db.models import Count, Q
from django.shortcuts import get_object_or_404, redirect
from django.utils.translation import gettext_lazy as _
from django.views.generic.list import ListView
from django.views.generic import DetailView, ListView
from extensions.models import Extension, Version, Tag
from constants.base import (
@ -15,6 +14,8 @@ from constants.base import (
EXTENSION_TYPE_PLURAL,
EXTENSION_TYPE_CHOICES,
)
from stats.models import ExtensionView
import ratings.models
from stats.models import ExtensionDownload, VersionDownload
import teams.models
@ -219,3 +220,42 @@ class SearchView(ListedExtensionsView):
context['total_count'] = super().get_queryset().filter(type=tag_type_id).count()
return context
class ExtensionDetailView(DetailView):
queryset = Extension.objects.listed.prefetch_related(
'authors',
'latest_version__file',
'latest_version__tags',
'preview_set',
'preview_set__file',
'ratings',
'ratings__user',
'team',
'versions',
'versions__file',
'versions__file__validation',
'versions__permissions',
'versions__platforms',
).distinct()
context_object_name = 'extension'
template_name = 'extensions/detail.html'
def get_object(self, queryset=None):
"""Record a page view when returning the Extension object."""
obj = super().get_object(queryset=queryset)
if self.request.user.is_anonymous or not obj.has_maintainer(self.request.user):
ExtensionView.create_from_request(self.request, object_id=obj.pk)
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.user.is_authenticated:
context['my_rating'] = ratings.models.Rating.get_for(
self.request.user.pk, self.object.pk
)
extension = context['object']
# Add the image for "og:image" meta to the context
if extension.featured_image and extension.featured_image.is_listed:
context['default_image_path'] = extension.featured_image.thumbnail_1080p_url
return context