Merge remote-tracking branch 'origin/develop' into feature/50-Linktypes

This commit is contained in:
Marcus Scholz 2021-10-02 02:47:42 +02:00
commit 4dbd8ae82d
10 changed files with 216 additions and 21 deletions

View File

@ -13,6 +13,7 @@ twine = "*"
pandoc = "*"
pylint-django = "*"
setuptools = "*"
django-nose = "*"
[packages]
django = "*"

View File

@ -49,7 +49,15 @@ INSTALLED_APPS = [
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles'
'django.contrib.staticfiles',
'django_nose'
]
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
NOSE_ARGS = [
'--with-coverage',
'--cover-package=lostplaces',
]
MIDDLEWARE = [

View File

@ -5,7 +5,7 @@ from django.conf import settings
settings.THUMBNAIL_ALIASES = {
'': {
'thumbnail': {'size': (300, 200), 'sharpen': True, 'crop': True},
'thumbnail': {'size': (300, 200), 'sharpen': True, 'crop': True, 'upscale': True},
'hero': {'size': (700, 466), 'sharpen': True, 'crop': True},
'large': {'size': (1920, 1920), 'sharpen': True, 'crop': False},
},

View File

@ -4,4 +4,5 @@
from django.apps import AppConfig
class LostplacesAppConfig(AppConfig):
default_auto_field = 'django.db.models.AutoField'
name = 'lostplaces'

View File

@ -82,6 +82,18 @@ class Explorer(models.Model):
choices=EXPLORER_LEVELS
)
def get_places_eligible_to_see(self):
if self.user.is_superuser:
return Place.objects.all()
return Place.objects.all().filter(level__lte=self.level) | self.places.all()
def is_eligible_to_see(self, place):
return (
self.user.is_superuser or
place.submitted_by == self or
place in self.get_places_eligible_to_see()
)
def __str__(self):
return self.user.username

View File

@ -37,9 +37,36 @@ class TestPlaceListView(GlobalTemplateTestCaseMixin, ViewTestCase):
def setUpTestData(cls):
user = User.objects.create_user(
username='testpeter',
password='Develop123',
)
user.explorer.level = 3
user.explorer.save()
# default level should be 1, not setting required
other_user = User.objects.create_user(
username='blubberbernd',
password='Develop123'
)
superuser = User.objects.create_user(
username='toor',
password='Develop123'
)
superuser.is_superuser = True
superuser.save()
Place.objects.create(
name='Im a own place',
submitted_when=timezone.now(),
submitted_by=other_user.explorer,
location='Test %d town' % 5,
latitude=50.5 + 5/10,
longitude=7.0 - 5/10,
description='This is just a test, do not worry %d' % 5,
level=3
)
for i in range(12):
place = Place.objects.create(
name='Im a place %d' % i,
@ -48,7 +75,8 @@ class TestPlaceListView(GlobalTemplateTestCaseMixin, ViewTestCase):
location='Test %d town' % i,
latitude=50.5 + i/10,
longitude=7.0 - i/10,
description='This is just a test, do not worry %d' % i
description='This is just a test, do not worry %d' % i,
level=3
)
place.tags.add('I a tag', 'testlocation')
place.save()
@ -90,6 +118,42 @@ class TestPlaceListView(GlobalTemplateTestCaseMixin, ViewTestCase):
),
msg='Expecting the place list to be paginated like [first] [previous] [item] at least 2 times [next] [last]'
)
def test_not_eligible_to_see_because_of_low_level(self):
self.client.login(username='blubberbernd', password='Develop123')
response = self.client.get(reverse('place_list'))
self.assertFalse(
'Im a place' in response.content.decode(),
msg='Expecting the user to not see any places'
)
def test_not_eligible_to_see_because_of_low_level_superuser(self):
self.client.login(username='toor', password='Develop123')
response = self.client.get(reverse('place_list'))
self.assertTrue(
'Im a place' in response.content.decode(),
msg='Expecting the superuser to see all places'
)
def test_not_eligible_to_see_because_of_low_level_own_place(self):
self.client.login(username='blubberbernd', password='Develop123')
response = self.client.get(reverse('place_list'))
self.assertTrue(
'Im a own place' in response.content.decode(),
msg='Expecting the user to see it\'s own places'
)
def test_eligible_to_see(self):
self.client.login(username='testpeter', password='Develop123')
response = self.client.get(reverse('place_list'))
self.assertTrue(
'Im a own place' in response.content.decode(),
msg='Expecting the user to see places where their level is high enough'
)
class TestPlaceCreateView(ViewTestCase):
view = PlaceCreateView
@ -308,6 +372,8 @@ class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixi
username='testpeter',
password='Develop123'
)
user.explorer.level = 3
user.explorer.save()
place = Place.objects.create(
name='Im a place',
@ -316,11 +382,72 @@ class PlaceDetailViewTestCase(TaggableViewTestCaseMixin, MapableViewTestCaseMixi
location='Testtown',
latitude=50.5,
longitude=7.0,
description='This is just a test, do not worry'
description='This is just a test, do not worry',
level=3
)
place.tags.add('I a tag', 'testlocation')
place.save()
other_user = User.objects.create_user(
username='blubberbernd',
password='Develop123'
)
superuser = User.objects.create_user(
username='toor',
password='Develop123'
)
superuser.is_superuser = True
superuser.save()
Place.objects.create(
name='Im a own place',
submitted_when=timezone.now(),
submitted_by=other_user.explorer,
location='Test %d town' % 5,
latitude=50.5 + 5/10,
longitude=7.0 - 5/10,
description='This is just a test, do not worry %d' % 5,
level=3
)
def test_not_eligible_to_see_because_of_low_level(self):
self.client.login(username='blubberbernd', password='Develop123')
response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
self.assertFalse(
'Im a place' in response.content.decode(),
msg='Expecting the user to not see the places'
)
def test_not_eligible_to_see_because_of_low_level_superuser(self):
self.client.login(username='toor', password='Develop123')
response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
self.assertTrue(
'Im a place' in response.content.decode(),
msg='Expecting the superuser to see all places'
)
def test_not_eligible_to_see_because_of_low_level_own_place(self):
self.client.login(username='blubberbernd', password='Develop123')
response = self.client.get(reverse('place_detail', kwargs={'pk': 2}))
self.assertTrue(
'Im a own place' in response.content.decode(),
msg='Expecting the user to see it\'s own places'
)
def test_eligible_to_see(self):
self.client.login(username='testpeter', password='Develop123')
response = self.client.get(reverse('place_detail', kwargs={'pk': 2}))
self.assertTrue(
'Im a own place' in response.content.decode(),
msg='Expecting the user to see places where their level is high enough'
)
def test_not_authenticated(self):
response = self.client.get(reverse('place_detail', kwargs={'pk': 1}))
self.assertHttpRedirect(response)

View File

@ -4,6 +4,7 @@
from django.views import View
from django.views.generic.edit import CreateView
from django.views.generic.detail import SingleObjectMixin
from django.views.generic import ListView
from django.contrib import messages
from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
@ -26,7 +27,8 @@ class IsAuthenticatedMixin(LoginRequiredMixin, View):
permission_denied_message = _('Please login to proceed')
def handle_no_permission(self):
messages.error(self.request, self.permission_denied_message)
if not self.request.user.is_authenticated:
messages.error(self.request, self.permission_denied_message)
return super().handle_no_permission()
class IsPlaceSubmitterMixin(UserPassesTestMixin, View):
@ -60,6 +62,23 @@ class IsPlaceSubmitterMixin(UserPassesTestMixin, View):
messages.error(self.request, self.place_submitter_error_message)
return False
class IsEligibleToSeePlaceMixin(UserPassesTestMixin):
not_eligible_to_see_message = None
def get_place(self):
pass
def test_func(self):
if not hasattr(self.request, 'user'):
return False
if self.request.user.explorer.is_eligible_to_see(self.get_place()):
return True
if self.not_eligible_to_see_message:
messages.error(self.request, self.not_eligible_to_see_message)
return False
class PlaceAssetCreateView(IsAuthenticatedMixin, SuccessMessageMixin, CreateView):
"""
Abstract View for creating a place asset (i.e. PlaceImage)
@ -113,3 +132,10 @@ class PlaceAssetDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, SingleOb
self.get_object().delete()
messages.success(self.request, self.success_message)
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': place_id}))
class LevelCapPlaceListView(ListView):
model = Place
def get_queryset(self):
return self.request.user.explorer.get_places_eligible_to_see()

View File

@ -18,7 +18,10 @@ class MultiplePlaceImageUploadMixin:
submitted_by=submitted_by
)
place_image.save()
if place.hero is None:
place.hero = place.placeimages.all()[0]
place.save()
class PlaceImageCreateView(MultiplePlaceImageUploadMixin, PlaceAssetCreateView):
model = PlaceImage
form_class = PlaceImageForm

View File

@ -6,7 +6,6 @@ from django.db.models.functions import Lower
from django.views import View
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic.detail import SingleObjectMixin
from django.views.generic import ListView
from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
@ -16,16 +15,20 @@ from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy, reverse
from lostplaces.models import Place, PlaceImage
from lostplaces.views.base_views import IsAuthenticatedMixin, IsPlaceSubmitterMixin
from lostplaces.views.base_views import (
IsAuthenticatedMixin,
IsPlaceSubmitterMixin,
LevelCapPlaceListView,
IsEligibleToSeePlaceMixin
)
from lostplaces.views.place_image_views import MultiplePlaceImageUploadMixin
from lostplaces.forms import PlaceForm, PlaceImageForm, TagSubmitForm
from lostplaces.common import redirect_referer_or
from taggit.models import Tag
class PlaceListView(IsAuthenticatedMixin, ListView):
class PlaceListView(IsAuthenticatedMixin, LevelCapPlaceListView):
paginate_by = 5
model = Place
template_name = 'place/place_list.html'
ordering = [Lower('name')]
@ -37,9 +40,15 @@ class PlaceListView(IsAuthenticatedMixin, ListView):
}
return context
class PlaceDetailView(IsAuthenticatedMixin, View):
def get(self, request, pk):
place = get_object_or_404(Place, pk=pk)
class PlaceDetailView(IsAuthenticatedMixin, IsEligibleToSeePlaceMixin, View):
not_eligible_to_see_message = _('You\'r not allowed to see this place')
def get_place(self):
return get_object_or_404(Place, pk=self.kwargs['pk'])
def get(self, request, pk):
place = self.get_place()
context = {
'place': place,
'mapping_config': {
@ -129,10 +138,14 @@ class PlaceDeleteView(IsAuthenticatedMixin, IsPlaceSubmitterMixin, DeleteView):
def get_place(self):
return self.get_object()
class PlaceFavoriteView(IsAuthenticatedMixin, View):
class PlaceFavoriteView(IsAuthenticatedMixin, IsEligibleToSeePlaceMixin, View):
not_eligible_to_see_message = _('You\'r not allowed to favorite this place')
def get_place(self):
return get_object_or_404(Place, pk=self.kwargs['place_id'])
def get(self, request, place_id):
place = get_object_or_404(Place, id=place_id)
place = self.get_place()
if request.user is not None:
request.user.explorer.favorite_places.add(place)
request.user.explorer.save()
@ -140,7 +153,7 @@ class PlaceFavoriteView(IsAuthenticatedMixin, View):
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': place.pk}))
class PlaceUnfavoriteView(IsAuthenticatedMixin, View):
def get(self, request, place_id):
place = get_object_or_404(Place, id=place_id)
if request.user is not None:
@ -149,10 +162,14 @@ class PlaceUnfavoriteView(IsAuthenticatedMixin, View):
return redirect_referer_or(request, reverse('place_detail', kwargs={'pk': place.pk}))
class PlaceVisitCreateView(IsAuthenticatedMixin, View):
class PlaceVisitCreateView(IsAuthenticatedMixin, IsEligibleToSeePlaceMixin, View):
not_eligible_to_see_message = _('You\'r not allowed to visit this place :P (Now please stop trying out URL\'s)')
def get_place(self):
return get_object_or_404(Place, pk=self.kwargs['place_id'])
def get(self, request, place_id):
place = get_object_or_404(Place, id=place_id)
place = self.get_place()
if request.user is not None:
request.user.explorer.visited_places.add(place)
request.user.explorer.save()

View File

@ -31,7 +31,7 @@ class SignUpView(SuccessMessageMixin, CreateView):
class HomeView(IsAuthenticatedMixin, View):
def get(self, request, *args, **kwargs):
place_list = Place.objects.all().order_by('-submitted_when')[:10]
place_list = request.user.explorer.get_places_eligible_to_see()
context = {
'place_list': place_list,
'mapping_config': {