added sharing links and appropriate tests

This commit is contained in:
vabene1111 2020-06-16 11:23:58 +02:00
parent 17946c8dac
commit dee7249347
10 changed files with 118 additions and 7 deletions

View File

@ -3,10 +3,14 @@ Source: https://djangosnippets.org/snippets/1703/
"""
from django.contrib import messages
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.utils.translation import gettext as _
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy, reverse
from cookbook.models import ShareLink
def get_allowed_groups(groups_required):
groups_allowed = tuple(groups_required)
@ -66,3 +70,11 @@ class OwnerRequiredMixin(object):
return HttpResponseRedirect(reverse('index'))
return super(OwnerRequiredMixin, self).dispatch(request, *args, **kwargs)
def share_link_valid(recipe, share):
print(share, recipe)
try:
return True if ShareLink.objects.filter(recipe=recipe, uuid=share).exists() else False
except ValidationError:
return False

View File

@ -0,0 +1,27 @@
# Generated by Django 3.0.7 on 2020-06-16 08:57
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('cookbook', '0053_auto_20200611_2217'),
]
operations = [
migrations.CreateModel(
name='ShareLink',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.UUIDField(default=uuid.UUID('dbbf5150-0795-4305-b9bd-3952dfa2264b'))),
('created_at', models.DateTimeField(auto_now_add=True)),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('recipe', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.Recipe')),
],
),
]

View File

@ -1,5 +1,5 @@
import re
import uuid
from annoying.fields import AutoOneToOneField
from django.contrib import auth
from django.contrib.auth.models import User
@ -242,6 +242,13 @@ class MealPlan(models.Model):
return f'{self.get_label()} - {self.date} - {self.meal_type.name}'
class ShareLink(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
uuid = models.UUIDField(default=uuid.uuid4())
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class CookLog(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)

View File

@ -53,6 +53,9 @@
data-toggle="tooltip"
data-placement="top" title="{% trans 'Export recipe' %}"><i
class="fas fa-file-export"></i></a>
<a class="btn btn-primary" href="{% url 'new_share_link' recipe.pk %}" target="_blank" rel="noopener noreferrer"
data-toggle="tooltip"
data-placement="top" title="{% trans 'Create share link' %}"><i class="fas fa-share-alt"></i></a>
</div>
</div>

View File

@ -40,6 +40,8 @@ def markdown(value):
@register.simple_tag
def recipe_rating(recipe, user):
if not user.is_authenticated:
return ''
rating = recipe.cooklog_set.filter(created_by=user).aggregate(Avg('rating'))
if rating['rating__avg']:
@ -57,6 +59,8 @@ def recipe_rating(recipe, user):
@register.simple_tag
def recipe_last(recipe, user):
if not user.is_authenticated:
return ''
last = recipe.cooklog_set.filter(created_by=user).last()
if last:
return last.created_at

View File

@ -11,6 +11,7 @@ class TestBase(TestCase):
guest_client_1 = None
guest_client_2 = None
superuser_client = None
anonymous_client = None
def create_login_user(self, name, group):
client = Client()

View File

@ -0,0 +1,44 @@
import uuid
from django.contrib import auth
from django.urls import reverse
from cookbook.helper.permission_helper import share_link_valid
from cookbook.models import Recipe, ShareLink
from cookbook.tests.views.test_views import TestViews
class TestViewsGeneral(TestViews):
def test_share(self):
internal_recipe = Recipe.objects.create(
name='Test',
internal=True,
created_by=auth.get_user(self.user_client_1)
)
url = reverse('view_recipe', kwargs={'pk': internal_recipe.pk})
r = self.user_client_1.get(url)
self.assertEqual(r.status_code, 200)
r = self.anonymous_client.get(url)
self.assertEqual(r.status_code, 302)
url = reverse('new_share_link', kwargs={'pk': internal_recipe.pk})
r = self.user_client_1.get(url)
self.assertEqual(r.status_code, 302)
share = ShareLink.objects.filter(recipe=internal_recipe).first()
self.assertIsNotNone(share)
self.assertTrue(share_link_valid(internal_recipe, share.uuid))
url = reverse('view_recipe', kwargs={'pk': internal_recipe.pk, 'share': share.uuid})
r = self.anonymous_client.get(url)
self.assertEqual(r.status_code, 200)
url = reverse('view_recipe', kwargs={'pk': (internal_recipe.pk + 1), 'share': share.uuid})
r = self.anonymous_client.get(url)
self.assertEqual(r.status_code, 404)
url = reverse('view_recipe', kwargs={'pk': internal_recipe.pk, 'share': uuid.uuid4()})
r = self.anonymous_client.get(url)
self.assertEqual(r.status_code, 302)

View File

@ -30,8 +30,10 @@ urlpatterns = [
path('export/', import_export.export_recipe, name='view_export'),
path('view/recipe/<int:pk>', views.recipe_view, name='view_recipe'),
path('view/recipe/<int:pk>/<slug:share>', views.recipe_view, name='view_recipe'),
path('new/recipe_import/<int:import_id>/', new.create_new_external_recipe, name='new_recipe_import'),
path('new/recipe-import/<int:import_id>/', new.create_new_external_recipe, name='new_recipe_import'),
path('new/share-link/<int:pk>/', new.share_link, name='new_share_link'),
path('edit/recipe/<int:pk>/', edit.switch_recipe, name='edit_recipe'),
path('edit/recipe/internal/<int:pk>/', edit.internal_recipe_update, name='edit_internal_recipe'), # for internal use only

View File

@ -3,7 +3,7 @@ from datetime import datetime
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.shortcuts import render, redirect
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy, reverse
from django.utils.translation import gettext as _
from django.views.generic import CreateView
@ -11,7 +11,7 @@ from django.views.generic import CreateView
from cookbook.forms import ImportRecipeForm, RecipeImport, KeywordForm, Storage, StorageForm, InternalRecipeForm, \
RecipeBookForm, MealPlanForm
from cookbook.helper.permission_helper import GroupRequiredMixin, group_required
from cookbook.models import Keyword, Recipe, RecipeBook, MealPlan
from cookbook.models import Keyword, Recipe, RecipeBook, MealPlan, ShareLink
class RecipeCreate(GroupRequiredMixin, CreateView):
@ -36,6 +36,13 @@ class RecipeCreate(GroupRequiredMixin, CreateView):
return context
@group_required('user')
def share_link(request, pk):
recipe = get_object_or_404(Recipe, pk=pk)
link = ShareLink.objects.create(recipe=recipe, created_by=request.user)
return HttpResponseRedirect(reverse('view_recipe', kwargs={'pk': pk, 'share': link.uuid}))
class KeywordCreate(GroupRequiredMixin, CreateView):
groups_required = ['user']
template_name = "generic/new_template.html"

View File

@ -18,7 +18,7 @@ from django.conf import settings
from cookbook.filters import RecipeFilter
from cookbook.forms import *
from cookbook.helper.permission_helper import group_required
from cookbook.helper.permission_helper import group_required, share_link_valid
from cookbook.tables import RecipeTable, RecipeTableSmall, CookLogTable, ViewLogTable
from recipes.version import *
@ -70,9 +70,13 @@ def search(request):
return render(request, 'index.html')
@group_required('guest')
def recipe_view(request, pk):
def recipe_view(request, pk, share=None):
recipe = get_object_or_404(Recipe, pk=pk)
if not request.user.is_authenticated and not share_link_valid(recipe, share):
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to view this page!'))
return HttpResponseRedirect(reverse('index'))
ingredients = RecipeIngredient.objects.filter(recipe=recipe)
comments = Comment.objects.filter(recipe=recipe)