mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 04:10:06 -05:00
Merge branch 'develop' into url_import_recipes
# Conflicts: # cookbook/helper/recipe_url_import.py # cookbook/tests/api/test_api_keyword.py # cookbook/tests/other/test_edits_recipe.py # cookbook/views/api.py # requirements.txt
This commit is contained in:
@@ -4,6 +4,7 @@ import re
|
||||
import uuid
|
||||
|
||||
import requests
|
||||
from PIL import Image
|
||||
from annoying.decorators import ajax_request
|
||||
from annoying.functions import get_object_or_None
|
||||
from django.contrib import messages
|
||||
@@ -13,16 +14,11 @@ from django.core.exceptions import FieldError, ValidationError
|
||||
from django.core.files import File
|
||||
from django.db.models import Q
|
||||
from django.http import FileResponse, HttpResponse, JsonResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.utils import timezone
|
||||
from django.utils.formats import date_format
|
||||
from django.shortcuts import redirect, get_object_or_404
|
||||
from django.utils.translation import gettext as _
|
||||
from icalendar import Calendar, Event
|
||||
from PIL import Image
|
||||
from rest_framework import decorators, permissions, viewsets
|
||||
from rest_framework.exceptions import APIException, PermissionDenied
|
||||
from rest_framework.mixins import (ListModelMixin, RetrieveModelMixin,
|
||||
UpdateModelMixin, CreateModelMixin)
|
||||
from rest_framework import decorators, viewsets, status
|
||||
from rest_framework.exceptions import APIException, PermissionDenied, NotFound, MethodNotAllowed
|
||||
from rest_framework.parsers import MultiPartParser
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ViewSetMixin
|
||||
@@ -37,7 +33,7 @@ from cookbook.models import (CookLog, Food, Ingredient, Keyword, MealPlan,
|
||||
MealType, Recipe, RecipeBook, ShoppingList,
|
||||
ShoppingListEntry, ShoppingListRecipe, Step,
|
||||
Storage, Sync, SyncLog, Unit, UserPreference,
|
||||
ViewLog, RecipeBookEntry, Supermarket)
|
||||
ViewLog, RecipeBookEntry, Supermarket, ImportLog)
|
||||
from cookbook.provider.dropbox import Dropbox
|
||||
from cookbook.provider.local import Local
|
||||
from cookbook.provider.nextcloud import Nextcloud
|
||||
@@ -52,7 +48,7 @@ from cookbook.serializer import (FoodSerializer, IngredientSerializer,
|
||||
StorageSerializer, SyncLogSerializer,
|
||||
SyncSerializer, UnitSerializer,
|
||||
UserNameSerializer, UserPreferenceSerializer,
|
||||
ViewLogSerializer, CookLogSerializer, RecipeBookEntrySerializer, RecipeOverviewSerializer, SupermarketSerializer)
|
||||
ViewLogSerializer, CookLogSerializer, RecipeBookEntrySerializer, RecipeOverviewSerializer, SupermarketSerializer, ImportLogSerializer)
|
||||
from recipes.settings import DEMO
|
||||
from recipe_scrapers import scrape_me, WebsiteNotImplementedError, NoSchemaFoundInWildMode
|
||||
|
||||
@@ -71,12 +67,14 @@ class StandardFilterMixin(ViewSetMixin):
|
||||
queryset = queryset.filter(updated_at__gte=updated_at)
|
||||
except FieldError:
|
||||
pass
|
||||
except ValidationError:
|
||||
raise APIException(_('Parameter updated_at incorrectly formatted'))
|
||||
|
||||
limit = self.request.query_params.get('limit', None)
|
||||
random = self.request.query_params.get('random', False)
|
||||
if limit is not None:
|
||||
if random:
|
||||
queryset = queryset.random(int(limit))
|
||||
queryset = queryset.order_by("?")[:int(limit)]
|
||||
else:
|
||||
queryset = queryset[:int(limit)]
|
||||
return queryset
|
||||
@@ -89,65 +87,69 @@ class UserNameViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
|
||||
- **filter_list**: array of user id's to get names for
|
||||
"""
|
||||
queryset = User.objects.all()
|
||||
queryset = User.objects
|
||||
serializer_class = UserNameSerializer
|
||||
permission_classes = [CustomIsGuest]
|
||||
http_method_names = ['get']
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = self.queryset
|
||||
queryset = self.queryset.filter(userpreference__space=self.request.space)
|
||||
try:
|
||||
filter_list = self.request.query_params.get('filter_list', None)
|
||||
if filter_list is not None:
|
||||
queryset = queryset.filter(pk__in=json.loads(filter_list))
|
||||
except ValueError:
|
||||
raise APIException(
|
||||
_('Parameter filter_list incorrectly formatted')
|
||||
)
|
||||
raise APIException('Parameter filter_list incorrectly formatted')
|
||||
|
||||
return queryset
|
||||
|
||||
|
||||
class UserPreferenceViewSet(viewsets.ModelViewSet):
|
||||
queryset = UserPreference.objects.all()
|
||||
queryset = UserPreference.objects
|
||||
serializer_class = UserPreferenceSerializer
|
||||
permission_classes = [CustomIsOwner, ]
|
||||
|
||||
def perform_create(self, serializer):
|
||||
if UserPreference.objects.filter(user=self.request.user).exists():
|
||||
raise APIException(_('Preference for given user already exists'))
|
||||
serializer.save(user=self.request.user)
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_superuser:
|
||||
return self.queryset
|
||||
return self.queryset.filter(user=self.request.user)
|
||||
|
||||
|
||||
class StorageViewSet(viewsets.ModelViewSet):
|
||||
# TODO handle delete protect error and adjust test
|
||||
queryset = Storage.objects.all()
|
||||
queryset = Storage.objects
|
||||
serializer_class = StorageSerializer
|
||||
permission_classes = [CustomIsAdmin, ]
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(space=self.request.space)
|
||||
|
||||
|
||||
class SyncViewSet(viewsets.ModelViewSet):
|
||||
queryset = Sync.objects.all()
|
||||
queryset = Sync.objects
|
||||
serializer_class = SyncSerializer
|
||||
permission_classes = [CustomIsAdmin, ]
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(space=self.request.space)
|
||||
|
||||
|
||||
class SyncLogViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = SyncLog.objects.all()
|
||||
queryset = SyncLog.objects
|
||||
serializer_class = SyncLogSerializer
|
||||
permission_classes = [CustomIsAdmin, ]
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(sync__space=self.request.space)
|
||||
|
||||
|
||||
class SupermarketViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
queryset = Supermarket.objects.all()
|
||||
queryset = Supermarket.objects
|
||||
serializer_class = SupermarketSerializer
|
||||
permission_classes = [CustomIsUser]
|
||||
|
||||
def get_queryset(self):
|
||||
self.queryset = self.queryset.filter(space=self.request.space)
|
||||
return super().get_queryset()
|
||||
|
||||
|
||||
class KeywordViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
"""
|
||||
@@ -158,44 +160,52 @@ class KeywordViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
in the keyword name (case in-sensitive)
|
||||
- **limit**: limits the amount of returned results
|
||||
"""
|
||||
queryset = Keyword.objects.all()
|
||||
queryset = Keyword.objects
|
||||
serializer_class = KeywordSerializer
|
||||
permission_classes = [CustomIsUser]
|
||||
|
||||
def get_queryset(self):
|
||||
self.queryset = self.queryset.filter(space=self.request.space)
|
||||
return super().get_queryset()
|
||||
|
||||
|
||||
class UnitViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
queryset = Unit.objects.all()
|
||||
queryset = Unit.objects
|
||||
serializer_class = UnitSerializer
|
||||
permission_classes = [CustomIsUser]
|
||||
|
||||
def get_queryset(self):
|
||||
self.queryset = self.queryset.filter(space=self.request.space)
|
||||
return super().get_queryset()
|
||||
|
||||
|
||||
class FoodViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
queryset = Food.objects.all()
|
||||
queryset = Food.objects
|
||||
serializer_class = FoodSerializer
|
||||
permission_classes = [CustomIsUser]
|
||||
|
||||
def get_queryset(self):
|
||||
self.queryset = self.queryset.filter(space=self.request.space)
|
||||
return super().get_queryset()
|
||||
|
||||
|
||||
class RecipeBookViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
queryset = RecipeBook.objects.all()
|
||||
queryset = RecipeBook.objects
|
||||
serializer_class = RecipeBookSerializer
|
||||
permission_classes = [CustomIsOwner]
|
||||
|
||||
def get_queryset(self):
|
||||
self.queryset = super(RecipeBookViewSet, self).get_queryset()
|
||||
if self.request.user.is_superuser:
|
||||
return self.queryset
|
||||
return self.queryset.filter(created_by=self.request.user)
|
||||
self.queryset = self.queryset.filter(created_by=self.request.user).filter(space=self.request.space)
|
||||
return super().get_queryset()
|
||||
|
||||
|
||||
class RecipeBookEntryViewSet(viewsets.ModelViewSet, viewsets.GenericViewSet):
|
||||
queryset = RecipeBookEntry.objects.all()
|
||||
queryset = RecipeBookEntry.objects
|
||||
serializer_class = RecipeBookEntrySerializer
|
||||
permission_classes = [CustomIsOwner]
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_superuser:
|
||||
return self.queryset
|
||||
return self.queryset.filter(created_by=self.request.user)
|
||||
return self.queryset.filter(book__created_by=self.request.user).filter(book__space=self.request.space)
|
||||
|
||||
|
||||
class MealPlanViewSet(viewsets.ModelViewSet):
|
||||
@@ -207,15 +217,15 @@ class MealPlanViewSet(viewsets.ModelViewSet):
|
||||
- **to_date**: filter upward to (inclusive) certain date
|
||||
|
||||
"""
|
||||
queryset = MealPlan.objects.all()
|
||||
queryset = MealPlan.objects
|
||||
serializer_class = MealPlanSerializer
|
||||
permission_classes = [CustomIsOwner]
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = MealPlan.objects.filter(
|
||||
queryset = self.queryset.filter(
|
||||
Q(created_by=self.request.user) |
|
||||
Q(shared=self.request.user)
|
||||
).distinct().all()
|
||||
).filter(space=self.request.space).distinct().all()
|
||||
|
||||
from_date = self.request.query_params.get('from_date', None)
|
||||
if from_date is not None:
|
||||
@@ -232,26 +242,32 @@ class MealTypeViewSet(viewsets.ModelViewSet):
|
||||
returns list of meal types created by the
|
||||
requesting user ordered by the order field.
|
||||
"""
|
||||
queryset = MealType.objects.order_by('order').all()
|
||||
queryset = MealType.objects
|
||||
serializer_class = MealTypeSerializer
|
||||
permission_classes = [CustomIsOwner]
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = MealType.objects.order_by('order', 'id').filter(created_by=self.request.user).all()
|
||||
queryset = self.queryset.order_by('order', 'id').filter(created_by=self.request.user).filter(space=self.request.space).all()
|
||||
return queryset
|
||||
|
||||
|
||||
class IngredientViewSet(viewsets.ModelViewSet):
|
||||
queryset = Ingredient.objects.all()
|
||||
queryset = Ingredient.objects
|
||||
serializer_class = IngredientSerializer
|
||||
permission_classes = [CustomIsUser]
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(step__recipe__space=self.request.space)
|
||||
|
||||
|
||||
class StepViewSet(viewsets.ModelViewSet):
|
||||
queryset = Step.objects.all()
|
||||
queryset = Step.objects
|
||||
serializer_class = StepSerializer
|
||||
permission_classes = [CustomIsUser]
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(recipe__space=self.request.space)
|
||||
|
||||
|
||||
class RecipeViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
"""
|
||||
@@ -262,12 +278,13 @@ class RecipeViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
in the recipe name (case in-sensitive)
|
||||
- **limit**: limits the amount of returned results
|
||||
"""
|
||||
queryset = Recipe.objects.all()
|
||||
queryset = Recipe.objects
|
||||
serializer_class = RecipeSerializer
|
||||
# TODO split read and write permission for meal plan guest
|
||||
permission_classes = [CustomIsShare | CustomIsGuest]
|
||||
|
||||
def get_queryset(self):
|
||||
self.queryset = self.queryset.filter(space=self.request.space)
|
||||
|
||||
internal = self.request.query_params.get('internal', None)
|
||||
if internal:
|
||||
@@ -290,6 +307,10 @@ class RecipeViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
)
|
||||
def image(self, request, pk):
|
||||
obj = self.get_object()
|
||||
|
||||
if obj.get_space() != request.space:
|
||||
raise PermissionDenied(detail='You do not have the required permission to perform this action', code=403)
|
||||
|
||||
serializer = self.serializer_class(
|
||||
obj, data=request.data, partial=True
|
||||
)
|
||||
@@ -316,34 +337,30 @@ class RecipeViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
|
||||
|
||||
class ShoppingListRecipeViewSet(viewsets.ModelViewSet):
|
||||
queryset = ShoppingListRecipe.objects.all()
|
||||
queryset = ShoppingListRecipe.objects
|
||||
serializer_class = ShoppingListRecipeSerializer
|
||||
permission_classes = [CustomIsOwner, ]
|
||||
permission_classes = [CustomIsOwner | CustomIsShared]
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(shoppinglist__created_by=self.request.user).all()
|
||||
return self.queryset.filter(Q(shoppinglist__created_by=self.request.user) | Q(shoppinglist__shared=self.request.user)).filter(shoppinglist__space=self.request.space).all()
|
||||
|
||||
|
||||
class ShoppingListEntryViewSet(viewsets.ModelViewSet):
|
||||
queryset = ShoppingListEntry.objects.all()
|
||||
queryset = ShoppingListEntry.objects
|
||||
serializer_class = ShoppingListEntrySerializer
|
||||
permission_classes = [CustomIsOwner, ]
|
||||
permission_classes = [CustomIsOwner | CustomIsShared]
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(shoppinglist__created_by=self.request.user).all()
|
||||
return self.queryset.filter(Q(shoppinglist__created_by=self.request.user) | Q(shoppinglist__shared=self.request.user)).filter(shoppinglist__space=self.request.space).all()
|
||||
|
||||
|
||||
class ShoppingListViewSet(viewsets.ModelViewSet):
|
||||
queryset = ShoppingList.objects.all()
|
||||
queryset = ShoppingList.objects
|
||||
serializer_class = ShoppingListSerializer
|
||||
permission_classes = [CustomIsOwner | CustomIsShared]
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_superuser:
|
||||
return self.queryset
|
||||
return self.queryset.filter(
|
||||
Q(created_by=self.request.user) | Q(shared=self.request.user)
|
||||
).all()
|
||||
return self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(space=self.request.space).distinct()
|
||||
|
||||
def get_serializer_class(self):
|
||||
autosync = self.request.query_params.get('autosync', None)
|
||||
@@ -353,22 +370,38 @@ class ShoppingListViewSet(viewsets.ModelViewSet):
|
||||
|
||||
|
||||
class ViewLogViewSet(viewsets.ModelViewSet):
|
||||
queryset = ViewLog.objects.all()
|
||||
queryset = ViewLog.objects
|
||||
serializer_class = ViewLogSerializer
|
||||
permission_classes = [CustomIsOwner]
|
||||
|
||||
def get_queryset(self):
|
||||
return CookLog.objects.filter(created_by=self.request.user).all()[:5]
|
||||
self.queryset = self.queryset.filter(created_by=self.request.user).filter(space=self.request.space).all()
|
||||
if self.request.method == 'GET':
|
||||
return self.queryset[:5]
|
||||
else:
|
||||
return self.queryset
|
||||
|
||||
|
||||
class CookLogViewSet(viewsets.ModelViewSet):
|
||||
queryset = CookLog.objects.all()
|
||||
queryset = CookLog.objects
|
||||
serializer_class = CookLogSerializer
|
||||
permission_classes = [CustomIsOwner]
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = CookLog.objects.filter(created_by=self.request.user).all()[:5]
|
||||
return queryset
|
||||
self.queryset = self.queryset.filter(created_by=self.request.user).filter(space=self.request.space).all()
|
||||
if self.request.method == 'GET':
|
||||
return self.queryset[:5]
|
||||
else:
|
||||
return self.queryset
|
||||
|
||||
|
||||
class ImportLogViewSet(viewsets.ModelViewSet):
|
||||
queryset = ImportLog.objects
|
||||
serializer_class = ImportLogSerializer
|
||||
permission_classes = [CustomIsUser]
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(space=self.request.space).all()
|
||||
|
||||
|
||||
# -------------- non django rest api views --------------------
|
||||
@@ -394,7 +427,7 @@ def update_recipe_links(recipe):
|
||||
|
||||
@group_required('user')
|
||||
def get_external_file_link(request, recipe_id):
|
||||
recipe = Recipe.objects.get(id=recipe_id)
|
||||
recipe = get_object_or_404(Recipe, pk=recipe_id, space=request.space)
|
||||
if not recipe.link:
|
||||
update_recipe_links(recipe)
|
||||
|
||||
@@ -403,7 +436,7 @@ def get_external_file_link(request, recipe_id):
|
||||
|
||||
@group_required('guest')
|
||||
def get_recipe_file(request, recipe_id):
|
||||
recipe = Recipe.objects.get(id=recipe_id)
|
||||
recipe = get_object_or_404(Recipe, pk=recipe_id, space=request.space)
|
||||
if recipe.storage:
|
||||
return FileResponse(get_recipe_provider(recipe).get_file(recipe))
|
||||
else:
|
||||
@@ -418,7 +451,7 @@ def sync_all(request):
|
||||
)
|
||||
return redirect('index')
|
||||
|
||||
monitors = Sync.objects.filter(active=True)
|
||||
monitors = Sync.objects.filter(active=True).filter(space=request.user.userpreference.space)
|
||||
|
||||
error = False
|
||||
for monitor in monitors:
|
||||
@@ -470,7 +503,7 @@ def log_cooking(request, recipe_id):
|
||||
def get_plan_ical(request, from_date, to_date):
|
||||
queryset = MealPlan.objects.filter(
|
||||
Q(created_by=request.user) | Q(shared=request.user)
|
||||
).distinct().all()
|
||||
).filter(space=request.user.userpreference.space).distinct().all()
|
||||
|
||||
if from_date is not None:
|
||||
queryset = queryset.filter(date__gte=from_date)
|
||||
@@ -548,24 +581,25 @@ def recipe_from_url_old(request):
|
||||
},
|
||||
status=400
|
||||
)
|
||||
return get_from_html(response.text, url)
|
||||
return get_from_html(response.text, url, request.space)
|
||||
|
||||
|
||||
@group_required('admin')
|
||||
def get_backup(request):
|
||||
if not request.user.is_superuser:
|
||||
return HttpResponse('', status=403)
|
||||
@group_required('user')
|
||||
def recipe_from_json(request):
|
||||
mjson = request.POST['json']
|
||||
|
||||
buf = io.StringIO()
|
||||
management.call_command(
|
||||
'dumpdata', exclude=['contenttypes', 'auth'], stdout=buf
|
||||
md_json = json.loads(mjson)
|
||||
if ('@type' in md_json
|
||||
and md_json['@type'] == 'Recipe'):
|
||||
return JsonResponse(find_recipe_json(md_json, '', request.space))
|
||||
return JsonResponse(
|
||||
{
|
||||
'error': True,
|
||||
'msg': _('Could not parse correctly...')
|
||||
},
|
||||
status=400
|
||||
)
|
||||
|
||||
response = FileResponse(buf.getvalue())
|
||||
response["Content-Disposition"] = f'attachment; filename=backup{date_format(timezone.now(), format="SHORT_DATETIME_FORMAT", use_l10n=True)}.json' # noqa: E501
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def ingredient_from_string(request):
|
||||
|
||||
@@ -27,32 +27,24 @@ from cookbook.tables import SyncTable
|
||||
def sync(request):
|
||||
if request.method == "POST":
|
||||
if not has_group_permission(request.user, ['admin']):
|
||||
messages.add_message(
|
||||
request, messages.ERROR,
|
||||
_('You do not have the required permissions to view this page!') # noqa: E501
|
||||
)
|
||||
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to view this page!'))
|
||||
return HttpResponseRedirect(reverse('data_sync'))
|
||||
form = SyncForm(request.POST)
|
||||
form = SyncForm(request.POST, space=request.space)
|
||||
if form.is_valid():
|
||||
new_path = Sync()
|
||||
new_path.path = form.cleaned_data['path']
|
||||
new_path.storage = form.cleaned_data['storage']
|
||||
new_path.last_checked = datetime.now()
|
||||
new_path.space = request.space
|
||||
new_path.save()
|
||||
return redirect('data_sync')
|
||||
else:
|
||||
form = SyncForm()
|
||||
form = SyncForm(space=request.space)
|
||||
|
||||
monitored_paths = SyncTable(Sync.objects.all())
|
||||
RequestConfig(
|
||||
request, paginate={'per_page': 25}
|
||||
).configure(monitored_paths)
|
||||
monitored_paths = SyncTable(Sync.objects.filter(space=request.space).all())
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(monitored_paths)
|
||||
|
||||
return render(
|
||||
request,
|
||||
'batch/monitor.html',
|
||||
{'form': form, 'monitored_paths': monitored_paths}
|
||||
)
|
||||
return render(request, 'batch/monitor.html', {'form': form, 'monitored_paths': monitored_paths})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
@@ -62,14 +54,15 @@ def sync_wait(request):
|
||||
|
||||
@group_required('user')
|
||||
def batch_import(request):
|
||||
imports = RecipeImport.objects.all()
|
||||
imports = RecipeImport.objects.filter(space=request.space).all()
|
||||
for new_recipe in imports:
|
||||
recipe = Recipe(
|
||||
name=new_recipe.name,
|
||||
file_path=new_recipe.file_path,
|
||||
storage=new_recipe.storage,
|
||||
file_uid=new_recipe.file_uid,
|
||||
created_by=request.user
|
||||
created_by=request.user,
|
||||
space=request.space
|
||||
)
|
||||
recipe.save()
|
||||
new_recipe.delete()
|
||||
@@ -80,12 +73,12 @@ def batch_import(request):
|
||||
@group_required('user')
|
||||
def batch_edit(request):
|
||||
if request.method == "POST":
|
||||
form = BatchEditForm(request.POST)
|
||||
form = BatchEditForm(request.POST, space=request.space)
|
||||
if form.is_valid():
|
||||
word = form.cleaned_data['search']
|
||||
keywords = form.cleaned_data['keywords']
|
||||
|
||||
recipes = Recipe.objects.filter(name__icontains=word)
|
||||
recipes = Recipe.objects.filter(name__icontains=word, space=request.space)
|
||||
count = 0
|
||||
for recipe in recipes:
|
||||
edit = False
|
||||
@@ -107,7 +100,7 @@ def batch_edit(request):
|
||||
|
||||
return redirect('data_batch_edit')
|
||||
else:
|
||||
form = BatchEditForm()
|
||||
form = BatchEditForm(space=request.space)
|
||||
|
||||
return render(request, 'batch/edit.html', {'form': form})
|
||||
|
||||
@@ -125,6 +118,7 @@ def import_url(request):
|
||||
servings=data['servings'],
|
||||
internal=True,
|
||||
created_by=request.user,
|
||||
space=request.space,
|
||||
)
|
||||
|
||||
step = Step.objects.create(
|
||||
@@ -134,10 +128,10 @@ def import_url(request):
|
||||
recipe.steps.add(step)
|
||||
|
||||
for kw in data['keywords']:
|
||||
if k := Keyword.objects.filter(name=kw['text']).first():
|
||||
if kw['id'] != "null" and (k := Keyword.objects.filter(id=kw['id'], space=request.space).first()):
|
||||
recipe.keywords.add(k)
|
||||
elif data['all_keywords']:
|
||||
k = Keyword.objects.create(name=kw['text'])
|
||||
k = Keyword.objects.create(name=kw['text'], space=request.space)
|
||||
recipe.keywords.add(k)
|
||||
|
||||
for ing in data['recipeIngredient']:
|
||||
@@ -145,12 +139,12 @@ def import_url(request):
|
||||
|
||||
if ing['ingredient']['text'] != '':
|
||||
ingredient.food, f_created = Food.objects.get_or_create(
|
||||
name=ing['ingredient']['text']
|
||||
name=ing['ingredient']['text'], space=request.space
|
||||
)
|
||||
|
||||
if ing['unit'] and ing['unit']['text'] != '':
|
||||
ingredient.unit, u_created = Unit.objects.get_or_create(
|
||||
name=ing['unit']['text']
|
||||
name=ing['unit']['text'], space=request.space
|
||||
)
|
||||
|
||||
# TODO properly handle no_amount recipes
|
||||
@@ -201,16 +195,16 @@ class Object(object):
|
||||
@group_required('user')
|
||||
def statistics(request):
|
||||
counts = Object()
|
||||
counts.recipes = Recipe.objects.count()
|
||||
counts.keywords = Keyword.objects.count()
|
||||
counts.recipe_import = RecipeImport.objects.count()
|
||||
counts.units = Unit.objects.count()
|
||||
counts.ingredients = Food.objects.count()
|
||||
counts.comments = Comment.objects.count()
|
||||
counts.recipes = Recipe.objects.filter(space=request.space).count()
|
||||
counts.keywords = Keyword.objects.filter(space=request.space).count()
|
||||
counts.recipe_import = RecipeImport.objects.filter(space=request.space).count()
|
||||
counts.units = Unit.objects.filter(space=request.space).count()
|
||||
counts.ingredients = Food.objects.filter(space=request.space).count()
|
||||
counts.comments = Comment.objects.filter(recipe__space=request.space).count()
|
||||
|
||||
counts.recipes_internal = Recipe.objects.filter(internal=True).count()
|
||||
counts.recipes_internal = Recipe.objects.filter(internal=True, space=request.space).count()
|
||||
counts.recipes_external = counts.recipes - counts.recipes_internal
|
||||
|
||||
counts.recipes_no_keyword = Recipe.objects.filter(keywords=None).count()
|
||||
counts.recipes_no_keyword = Recipe.objects.filter(keywords=None, space=request.space).count()
|
||||
|
||||
return render(request, 'stats.html', {'counts': counts})
|
||||
|
||||
@@ -31,7 +31,7 @@ class RecipeDelete(GroupRequiredMixin, DeleteView):
|
||||
|
||||
@group_required('user')
|
||||
def delete_recipe_source(request, pk):
|
||||
recipe = get_object_or_404(Recipe, pk=pk)
|
||||
recipe = get_object_or_404(Recipe, pk=pk, space=request.space)
|
||||
|
||||
if recipe.storage.method == Storage.DROPBOX:
|
||||
# TODO central location to handle storage type switches
|
||||
@@ -130,25 +130,12 @@ class RecipeBookDelete(OwnerRequiredMixin, DeleteView):
|
||||
return context
|
||||
|
||||
|
||||
class RecipeBookEntryDelete(GroupRequiredMixin, DeleteView):
|
||||
class RecipeBookEntryDelete(OwnerRequiredMixin, DeleteView):
|
||||
groups_required = ['user']
|
||||
template_name = "generic/delete_template.html"
|
||||
model = RecipeBookEntry
|
||||
success_url = reverse_lazy('view_books')
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
obj = self.get_object()
|
||||
if not (obj.book.created_by == request.user
|
||||
or request.user.is_superuser):
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('You cannot interact with this object as it is not owned by you!') # noqa: E501
|
||||
)
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
return super(RecipeBookEntryDelete, self) \
|
||||
.dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(RecipeBookEntryDelete, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Bookmarks")
|
||||
|
||||
@@ -3,9 +3,11 @@ import os
|
||||
from django.contrib import messages
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.urls import reverse
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import UpdateView
|
||||
from django.views.generic.edit import FormMixin
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from cookbook.forms import (CommentForm, ExternalRecipeForm, FoodForm,
|
||||
FoodMergeForm, KeywordForm, MealPlanForm,
|
||||
@@ -24,7 +26,7 @@ from cookbook.provider.nextcloud import Nextcloud
|
||||
|
||||
@group_required('guest')
|
||||
def switch_recipe(request, pk):
|
||||
recipe = get_object_or_404(Recipe, pk=pk)
|
||||
recipe = get_object_or_404(Recipe, pk=pk, space=request.space)
|
||||
if recipe.internal:
|
||||
return HttpResponseRedirect(reverse('edit_internal_recipe', args=[pk]))
|
||||
else:
|
||||
@@ -33,7 +35,7 @@ def switch_recipe(request, pk):
|
||||
|
||||
@group_required('user')
|
||||
def convert_recipe(request, pk):
|
||||
recipe = get_object_or_404(Recipe, pk=pk)
|
||||
recipe = get_object_or_404(Recipe, pk=pk, space=request.space)
|
||||
if not recipe.internal:
|
||||
recipe.internal = True
|
||||
recipe.save()
|
||||
@@ -43,14 +45,22 @@ def convert_recipe(request, pk):
|
||||
|
||||
@group_required('user')
|
||||
def internal_recipe_update(request, pk):
|
||||
recipe_instance = get_object_or_404(Recipe, pk=pk)
|
||||
recipe_instance = get_object_or_404(Recipe, pk=pk, space=request.space)
|
||||
|
||||
return render(
|
||||
request, 'forms/edit_internal_recipe.html', {'recipe': recipe_instance}
|
||||
)
|
||||
|
||||
|
||||
class SyncUpdate(GroupRequiredMixin, UpdateView):
|
||||
class SpaceFormMixing(FormMixin):
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs.update({'space': self.request.space})
|
||||
return kwargs
|
||||
|
||||
|
||||
class SyncUpdate(GroupRequiredMixin, UpdateView, SpaceFormMixing):
|
||||
groups_required = ['admin']
|
||||
template_name = "generic/edit_template.html"
|
||||
model = Sync
|
||||
@@ -62,7 +72,7 @@ class SyncUpdate(GroupRequiredMixin, UpdateView):
|
||||
return reverse('edit_sync', kwargs={'pk': self.object.pk})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(SyncUpdate, self).get_context_data(**kwargs)
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _("Sync")
|
||||
return context
|
||||
|
||||
@@ -79,12 +89,12 @@ class KeywordUpdate(GroupRequiredMixin, UpdateView):
|
||||
return reverse('edit_keyword', kwargs={'pk': self.object.pk})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(KeywordUpdate, self).get_context_data(**kwargs)
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _("Keyword")
|
||||
return context
|
||||
|
||||
|
||||
class FoodUpdate(GroupRequiredMixin, UpdateView):
|
||||
class FoodUpdate(GroupRequiredMixin, UpdateView, SpaceFormMixing):
|
||||
groups_required = ['user']
|
||||
template_name = "generic/edit_template.html"
|
||||
model = Food
|
||||
@@ -103,12 +113,10 @@ class FoodUpdate(GroupRequiredMixin, UpdateView):
|
||||
|
||||
@group_required('admin')
|
||||
def edit_storage(request, pk):
|
||||
instance = get_object_or_404(Storage, pk=pk)
|
||||
instance = get_object_or_404(Storage, pk=pk, space=request.space)
|
||||
|
||||
if not (instance.created_by == request.user or request.user.is_superuser):
|
||||
messages.add_message(
|
||||
request, messages.ERROR, _('You cannot edit this storage!')
|
||||
)
|
||||
messages.add_message(request, messages.ERROR, _('You cannot edit this storage!'))
|
||||
return HttpResponseRedirect(reverse('list_storage'))
|
||||
|
||||
if request.method == "POST":
|
||||
@@ -183,7 +191,7 @@ class ImportUpdate(GroupRequiredMixin, UpdateView):
|
||||
return context
|
||||
|
||||
|
||||
class RecipeBookUpdate(OwnerRequiredMixin, UpdateView):
|
||||
class RecipeBookUpdate(OwnerRequiredMixin, UpdateView, SpaceFormMixing):
|
||||
template_name = "generic/edit_template.html"
|
||||
model = RecipeBook
|
||||
form_class = RecipeBookForm
|
||||
@@ -197,7 +205,7 @@ class RecipeBookUpdate(OwnerRequiredMixin, UpdateView):
|
||||
return context
|
||||
|
||||
|
||||
class MealPlanUpdate(OwnerRequiredMixin, UpdateView):
|
||||
class MealPlanUpdate(OwnerRequiredMixin, UpdateView, SpaceFormMixing):
|
||||
template_name = "generic/edit_template.html"
|
||||
model = MealPlan
|
||||
form_class = MealPlanForm
|
||||
@@ -217,7 +225,7 @@ class MealPlanUpdate(OwnerRequiredMixin, UpdateView):
|
||||
return context
|
||||
|
||||
|
||||
class ExternalRecipeUpdate(GroupRequiredMixin, UpdateView):
|
||||
class ExternalRecipeUpdate(GroupRequiredMixin, UpdateView, SpaceFormMixing):
|
||||
groups_required = ['user']
|
||||
model = Recipe
|
||||
form_class = ExternalRecipeForm
|
||||
@@ -225,10 +233,10 @@ class ExternalRecipeUpdate(GroupRequiredMixin, UpdateView):
|
||||
|
||||
def form_valid(self, form):
|
||||
self.object = form.save(commit=False)
|
||||
old_recipe = Recipe.objects.get(pk=self.object.pk)
|
||||
old_recipe = Recipe.objects.get(pk=self.object.pk, space=self.request.space)
|
||||
if not old_recipe.name == self.object.name:
|
||||
# TODO central location to handle storage type switches
|
||||
if self.object.storage.method == Storage.DROPBOX:
|
||||
# TODO central location to handle storage type switches
|
||||
Dropbox.rename_file(old_recipe, self.object.name)
|
||||
if self.object.storage.method == Storage.NEXTCLOUD:
|
||||
Nextcloud.rename_file(old_recipe, self.object.name)
|
||||
@@ -241,24 +249,18 @@ class ExternalRecipeUpdate(GroupRequiredMixin, UpdateView):
|
||||
os.path.splitext(self.object.file_path)[1]
|
||||
)
|
||||
|
||||
messages.add_message(
|
||||
self.request, messages.SUCCESS, _('Changes saved!')
|
||||
)
|
||||
messages.add_message(self.request, messages.SUCCESS, _('Changes saved!'))
|
||||
return super(ExternalRecipeUpdate, self).form_valid(form)
|
||||
|
||||
def form_invalid(self, form):
|
||||
messages.add_message(
|
||||
self.request,
|
||||
messages.ERROR,
|
||||
_('Error saving changes!')
|
||||
)
|
||||
messages.add_message(self.request, messages.ERROR, _('Error saving changes!'))
|
||||
return super(ExternalRecipeUpdate, self).form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('edit_recipe', kwargs={'pk': self.object.pk})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ExternalRecipeUpdate, self).get_context_data(**kwargs)
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = _("Recipe")
|
||||
context['view_url'] = reverse('view_recipe', args=[self.object.pk])
|
||||
if self.object.storage:
|
||||
@@ -272,60 +274,43 @@ class ExternalRecipeUpdate(GroupRequiredMixin, UpdateView):
|
||||
def edit_ingredients(request):
|
||||
if request.method == "POST":
|
||||
success = False
|
||||
units_form = UnitMergeForm(request.POST, prefix=UnitMergeForm.prefix)
|
||||
units_form = UnitMergeForm(request.POST, prefix=UnitMergeForm.prefix, space=request.space)
|
||||
if units_form.is_valid():
|
||||
new_unit = units_form.cleaned_data['new_unit']
|
||||
old_unit = units_form.cleaned_data['old_unit']
|
||||
if new_unit != old_unit:
|
||||
recipe_ingredients = Ingredient.objects \
|
||||
.filter(unit=old_unit).all()
|
||||
recipe_ingredients = Ingredient.objects.filter(unit=old_unit, step__recipe__space=request.space).all()
|
||||
for i in recipe_ingredients:
|
||||
i.unit = new_unit
|
||||
i.save()
|
||||
|
||||
old_unit.delete()
|
||||
success = True
|
||||
messages.add_message(
|
||||
request, messages.SUCCESS, _('Units merged!')
|
||||
)
|
||||
messages.add_message(request, messages.SUCCESS, _('Units merged!'))
|
||||
else:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('Cannot merge with the same object!')
|
||||
)
|
||||
messages.add_message(request, messages.ERROR, _('Cannot merge with the same object!'))
|
||||
|
||||
food_form = FoodMergeForm(request.POST, prefix=FoodMergeForm.prefix)
|
||||
food_form = FoodMergeForm(request.POST, prefix=FoodMergeForm.prefix, space=request.space)
|
||||
if food_form.is_valid():
|
||||
new_food = food_form.cleaned_data['new_food']
|
||||
old_food = food_form.cleaned_data['old_food']
|
||||
if new_food != old_food:
|
||||
ingredients = Ingredient.objects.filter(food=old_food).all()
|
||||
ingredients = Ingredient.objects.filter(food=old_food, step__recipe__space=request.space).all()
|
||||
for i in ingredients:
|
||||
i.food = new_food
|
||||
i.save()
|
||||
|
||||
old_food.delete()
|
||||
success = True
|
||||
messages.add_message(
|
||||
request, messages.SUCCESS, _('Foods merged!')
|
||||
)
|
||||
messages.add_message(request, messages.SUCCESS, _('Foods merged!'))
|
||||
else:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('Cannot merge with the same object!')
|
||||
)
|
||||
messages.add_message(request, messages.ERROR, _('Cannot merge with the same object!'))
|
||||
|
||||
if success:
|
||||
units_form = UnitMergeForm()
|
||||
food_form = FoodMergeForm()
|
||||
units_form = UnitMergeForm(space=request.space)
|
||||
food_form = FoodMergeForm(space=request.space)
|
||||
else:
|
||||
units_form = UnitMergeForm()
|
||||
food_form = FoodMergeForm()
|
||||
units_form = UnitMergeForm(space=request.space)
|
||||
food_form = FoodMergeForm(space=request.space)
|
||||
|
||||
return render(
|
||||
request,
|
||||
'forms/ingredients.html',
|
||||
{'units_form': units_form, 'food_form': food_form}
|
||||
)
|
||||
return render(request, 'forms/ingredients.html', {'units_form': units_form, 'food_form': food_form})
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import re
|
||||
import threading
|
||||
from io import BytesIO
|
||||
|
||||
from django.contrib import messages
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from cookbook.forms import ExportForm, ImportForm, ImportExportBase
|
||||
@@ -12,22 +16,22 @@ from cookbook.integration.mealie import Mealie
|
||||
from cookbook.integration.nextcloud_cookbook import NextcloudCookbook
|
||||
from cookbook.integration.paprika import Paprika
|
||||
from cookbook.integration.safron import Safron
|
||||
from cookbook.models import Recipe
|
||||
from cookbook.models import Recipe, ImportLog
|
||||
|
||||
|
||||
def get_integration(request, export_type):
|
||||
if export_type == ImportExportBase.DEFAULT:
|
||||
return Default(request)
|
||||
return Default(request, export_type)
|
||||
if export_type == ImportExportBase.PAPRIKA:
|
||||
return Paprika(request)
|
||||
return Paprika(request, export_type)
|
||||
if export_type == ImportExportBase.NEXTCLOUD:
|
||||
return NextcloudCookbook(request)
|
||||
return NextcloudCookbook(request, export_type)
|
||||
if export_type == ImportExportBase.MEALIE:
|
||||
return Mealie(request)
|
||||
return Mealie(request, export_type)
|
||||
if export_type == ImportExportBase.CHOWDOWN:
|
||||
return Chowdown(request)
|
||||
return Chowdown(request, export_type)
|
||||
if export_type == ImportExportBase.SAFRON:
|
||||
return Safron(request)
|
||||
return Safron(request, export_type)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
@@ -37,7 +41,16 @@ def import_recipe(request):
|
||||
if form.is_valid():
|
||||
try:
|
||||
integration = get_integration(request, form.cleaned_data['type'])
|
||||
return integration.do_import(request.FILES.getlist('files'))
|
||||
|
||||
il = ImportLog.objects.create(type=form.cleaned_data['type'], created_by=request.user, space=request.space)
|
||||
files = []
|
||||
for f in request.FILES.getlist('files'):
|
||||
files.append({'file': BytesIO(f.read()), 'name': f.name})
|
||||
t = threading.Thread(target=integration.do_import, args=[files, il])
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
|
||||
return HttpResponseRedirect(reverse('view_import_response', args=[il.pk]))
|
||||
except NotImplementedError:
|
||||
messages.add_message(request, messages.ERROR, _('Importing is not implemented for this provider'))
|
||||
else:
|
||||
@@ -49,7 +62,7 @@ def import_recipe(request):
|
||||
@group_required('user')
|
||||
def export_recipe(request):
|
||||
if request.method == "POST":
|
||||
form = ExportForm(request.POST)
|
||||
form = ExportForm(request.POST, space=request.space)
|
||||
if form.is_valid():
|
||||
try:
|
||||
integration = get_integration(request, form.cleaned_data['type'])
|
||||
@@ -58,11 +71,16 @@ def export_recipe(request):
|
||||
messages.add_message(request, messages.ERROR, _('Exporting is not implemented for this provider'))
|
||||
|
||||
else:
|
||||
form = ExportForm()
|
||||
form = ExportForm(space=request.space)
|
||||
recipe = request.GET.get('r')
|
||||
if recipe:
|
||||
if re.match(r'^([0-9])+$', recipe):
|
||||
if recipe := Recipe.objects.filter(pk=int(recipe)).first():
|
||||
form = ExportForm(initial={'recipes': recipe})
|
||||
if recipe := Recipe.objects.filter(pk=int(recipe), space=request.space).first():
|
||||
form = ExportForm(initial={'recipes': recipe}, space=request.space)
|
||||
|
||||
return render(request, 'export.html', {'form': form})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def import_response(request, pk):
|
||||
return render(request, 'import_response.html', {'pk': pk})
|
||||
|
||||
@@ -6,7 +6,7 @@ from django.shortcuts import render
|
||||
from django.utils.translation import gettext as _
|
||||
from django_tables2 import RequestConfig
|
||||
|
||||
from cookbook.filters import IngredientFilter, ShoppingListFilter
|
||||
from cookbook.filters import FoodFilter, ShoppingListFilter
|
||||
from cookbook.helper.permission_helper import group_required
|
||||
from cookbook.models import (Food, InviteLink, Keyword, RecipeImport,
|
||||
ShoppingList, Storage, SyncLog)
|
||||
@@ -17,7 +17,7 @@ from cookbook.tables import (ImportLogTable, IngredientTable, InviteLinkTable,
|
||||
|
||||
@group_required('user')
|
||||
def keyword(request):
|
||||
table = KeywordTable(Keyword.objects.all())
|
||||
table = KeywordTable(Keyword.objects.filter(space=request.space).all())
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
return render(
|
||||
@@ -30,7 +30,7 @@ def keyword(request):
|
||||
@group_required('admin')
|
||||
def sync_log(request):
|
||||
table = ImportLogTable(
|
||||
SyncLog.objects.all().order_by('-created_at')
|
||||
SyncLog.objects.filter(space=request.space).all().order_by('-created_at')
|
||||
)
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
@@ -43,7 +43,7 @@ def sync_log(request):
|
||||
|
||||
@group_required('user')
|
||||
def recipe_import(request):
|
||||
table = RecipeImportTable(RecipeImport.objects.all())
|
||||
table = RecipeImportTable(RecipeImport.objects.filter(space=request.space).all())
|
||||
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
@@ -56,10 +56,7 @@ def recipe_import(request):
|
||||
|
||||
@group_required('user')
|
||||
def food(request):
|
||||
f = IngredientFilter(
|
||||
request.GET,
|
||||
queryset=Food.objects.all().order_by('pk')
|
||||
)
|
||||
f = FoodFilter(request.GET, queryset=Food.objects.filter(space=request.space).all().order_by('pk'))
|
||||
|
||||
table = IngredientTable(f.qs)
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
@@ -73,12 +70,10 @@ def food(request):
|
||||
|
||||
@group_required('user')
|
||||
def shopping_list(request):
|
||||
f = ShoppingListFilter(
|
||||
request.GET,
|
||||
queryset=ShoppingList.objects.filter(
|
||||
Q(created_by=request.user) |
|
||||
Q(shared=request.user)
|
||||
).all().order_by('finished', 'created_at'))
|
||||
f = ShoppingListFilter(request.GET, queryset=ShoppingList.objects.filter(
|
||||
Q(created_by=request.user) |
|
||||
Q(shared=request.user), space=request.space
|
||||
).all().order_by('finished', 'created_at'))
|
||||
|
||||
table = ShoppingListTable(f.qs)
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
@@ -97,7 +92,7 @@ def shopping_list(request):
|
||||
|
||||
@group_required('admin')
|
||||
def storage(request):
|
||||
table = StorageTable(Storage.objects.all())
|
||||
table = StorageTable(Storage.objects.filter(space=request.space).all())
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
return render(
|
||||
@@ -113,18 +108,11 @@ def storage(request):
|
||||
|
||||
@group_required('admin')
|
||||
def invite_link(request):
|
||||
table = InviteLinkTable(
|
||||
InviteLink.objects.filter(
|
||||
valid_until__gte=datetime.today(), used_by=None
|
||||
).all())
|
||||
table = InviteLinkTable(InviteLink.objects.filter(valid_until__gte=datetime.today(), used_by=None, space=request.space).all())
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
return render(
|
||||
request,
|
||||
'generic/list_template.html',
|
||||
{
|
||||
'title': _("Invite Links"),
|
||||
'table': table,
|
||||
'create_url': 'new_invite_link'
|
||||
}
|
||||
)
|
||||
return render(request, 'generic/list_template.html', {
|
||||
'title': _("Invite Links"),
|
||||
'table': table,
|
||||
'create_url': 'new_invite_link'
|
||||
})
|
||||
|
||||
@@ -14,6 +14,7 @@ from cookbook.helper.permission_helper import (GroupRequiredMixin,
|
||||
group_required)
|
||||
from cookbook.models import (InviteLink, Keyword, MealPlan, MealType, Recipe,
|
||||
RecipeBook, RecipeImport, ShareLink, Step)
|
||||
from cookbook.views.edit import SpaceFormMixing
|
||||
|
||||
|
||||
class RecipeCreate(GroupRequiredMixin, CreateView):
|
||||
@@ -25,12 +26,11 @@ class RecipeCreate(GroupRequiredMixin, CreateView):
|
||||
def form_valid(self, form):
|
||||
obj = form.save(commit=False)
|
||||
obj.created_by = self.request.user
|
||||
obj.space = self.request.space
|
||||
obj.internal = True
|
||||
obj.save()
|
||||
obj.steps.add(Step.objects.create())
|
||||
return HttpResponseRedirect(
|
||||
reverse('edit_recipe', kwargs={'pk': obj.pk})
|
||||
)
|
||||
return HttpResponseRedirect(reverse('edit_recipe', kwargs={'pk': obj.pk}))
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('edit_recipe', kwargs={'pk': self.object.pk})
|
||||
@@ -43,11 +43,9 @@ class RecipeCreate(GroupRequiredMixin, CreateView):
|
||||
|
||||
@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})
|
||||
)
|
||||
recipe = get_object_or_404(Recipe, pk=pk, space=request.space)
|
||||
link = ShareLink.objects.create(recipe=recipe, created_by=request.user, space=request.space)
|
||||
return HttpResponseRedirect(reverse('view_recipe', kwargs={'pk': pk, 'share': link.uuid}))
|
||||
|
||||
|
||||
class KeywordCreate(GroupRequiredMixin, CreateView):
|
||||
@@ -57,6 +55,12 @@ class KeywordCreate(GroupRequiredMixin, CreateView):
|
||||
form_class = KeywordForm
|
||||
success_url = reverse_lazy('list_keyword')
|
||||
|
||||
def form_valid(self, form):
|
||||
obj = form.save(commit=False)
|
||||
obj.space = self.request.space
|
||||
obj.save()
|
||||
return HttpResponseRedirect(reverse('edit_keyword', kwargs={'pk': obj.pk}))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(KeywordCreate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Keyword")
|
||||
@@ -73,10 +77,9 @@ class StorageCreate(GroupRequiredMixin, CreateView):
|
||||
def form_valid(self, form):
|
||||
obj = form.save(commit=False)
|
||||
obj.created_by = self.request.user
|
||||
obj.space = self.request.space
|
||||
obj.save()
|
||||
return HttpResponseRedirect(
|
||||
reverse('edit_storage', kwargs={'pk': obj.pk})
|
||||
)
|
||||
return HttpResponseRedirect(reverse('edit_storage', kwargs={'pk': obj.pk}))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(StorageCreate, self).get_context_data(**kwargs)
|
||||
@@ -87,10 +90,11 @@ class StorageCreate(GroupRequiredMixin, CreateView):
|
||||
@group_required('user')
|
||||
def create_new_external_recipe(request, import_id):
|
||||
if request.method == "POST":
|
||||
form = ImportRecipeForm(request.POST)
|
||||
form = ImportRecipeForm(request.POST, space=request.space)
|
||||
if form.is_valid():
|
||||
new_recipe = RecipeImport.objects.get(id=import_id)
|
||||
new_recipe = get_object_or_404(RecipeImport, pk=import_id, space=request.space)
|
||||
recipe = Recipe()
|
||||
recipe.space = request.space
|
||||
recipe.storage = new_recipe.storage
|
||||
recipe.name = form.cleaned_data['name']
|
||||
recipe.file_path = form.cleaned_data['file_path']
|
||||
@@ -99,34 +103,29 @@ def create_new_external_recipe(request, import_id):
|
||||
|
||||
recipe.save()
|
||||
|
||||
recipe.keywords.set(form.cleaned_data['keywords'])
|
||||
if form.cleaned_data['keywords']:
|
||||
recipe.keywords.set(form.cleaned_data['keywords'])
|
||||
|
||||
RecipeImport.objects.get(id=import_id).delete()
|
||||
new_recipe.delete()
|
||||
|
||||
messages.add_message(
|
||||
request, messages.SUCCESS, _('Imported new recipe!')
|
||||
)
|
||||
messages.add_message(request, messages.SUCCESS, _('Imported new recipe!'))
|
||||
return redirect('list_recipe_import')
|
||||
else:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('There was an error importing this recipe!')
|
||||
)
|
||||
messages.add_message(request, messages.ERROR, _('There was an error importing this recipe!'))
|
||||
else:
|
||||
new_recipe = RecipeImport.objects.get(id=import_id)
|
||||
new_recipe = get_object_or_404(RecipeImport, pk=import_id, space=request.space)
|
||||
form = ImportRecipeForm(
|
||||
initial={
|
||||
'file_path': new_recipe.file_path,
|
||||
'name': new_recipe.name,
|
||||
'file_uid': new_recipe.file_uid
|
||||
}
|
||||
}, space=request.space
|
||||
)
|
||||
|
||||
return render(request, 'forms/edit_import_recipe.html', {'form': form})
|
||||
|
||||
|
||||
class RecipeBookCreate(GroupRequiredMixin, CreateView):
|
||||
class RecipeBookCreate(GroupRequiredMixin, CreateView, SpaceFormMixing):
|
||||
groups_required = ['user']
|
||||
template_name = "generic/new_template.html"
|
||||
model = RecipeBook
|
||||
@@ -136,6 +135,7 @@ class RecipeBookCreate(GroupRequiredMixin, CreateView):
|
||||
def form_valid(self, form):
|
||||
obj = form.save(commit=False)
|
||||
obj.created_by = self.request.user
|
||||
obj.space = self.request.space
|
||||
obj.save()
|
||||
return HttpResponseRedirect(reverse('view_books'))
|
||||
|
||||
@@ -145,7 +145,7 @@ class RecipeBookCreate(GroupRequiredMixin, CreateView):
|
||||
return context
|
||||
|
||||
|
||||
class MealPlanCreate(GroupRequiredMixin, CreateView):
|
||||
class MealPlanCreate(GroupRequiredMixin, CreateView, SpaceFormMixing):
|
||||
groups_required = ['user']
|
||||
template_name = "generic/new_template.html"
|
||||
model = MealPlan
|
||||
@@ -154,9 +154,7 @@ class MealPlanCreate(GroupRequiredMixin, CreateView):
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = self.form_class(**self.get_form_kwargs())
|
||||
form.fields['meal_type'].queryset = MealType.objects.filter(
|
||||
created_by=self.request.user
|
||||
).all()
|
||||
form.fields['meal_type'].queryset = MealType.objects.filter(created_by=self.request.user, space=self.request.space).all()
|
||||
return form
|
||||
|
||||
def get_initial(self):
|
||||
@@ -181,6 +179,7 @@ class MealPlanCreate(GroupRequiredMixin, CreateView):
|
||||
def form_valid(self, form):
|
||||
obj = form.save(commit=False)
|
||||
obj.created_by = self.request.user
|
||||
obj.space = self.request.space
|
||||
obj.save()
|
||||
return HttpResponseRedirect(reverse('view_plan'))
|
||||
|
||||
@@ -191,8 +190,8 @@ class MealPlanCreate(GroupRequiredMixin, CreateView):
|
||||
recipe = self.request.GET.get('recipe')
|
||||
if recipe:
|
||||
if re.match(r'^([0-9])+$', recipe):
|
||||
if Recipe.objects.filter(pk=int(recipe)).exists():
|
||||
context['default_recipe'] = Recipe.objects.get(pk=int(recipe)) # noqa: E501
|
||||
if Recipe.objects.filter(pk=int(recipe), space=self.request.space).exists():
|
||||
context['default_recipe'] = Recipe.objects.get(pk=int(recipe), space=self.request.space)
|
||||
|
||||
return context
|
||||
|
||||
@@ -206,6 +205,7 @@ class InviteLinkCreate(GroupRequiredMixin, CreateView):
|
||||
def form_valid(self, form):
|
||||
obj = form.save(commit=False)
|
||||
obj.created_by = self.request.user
|
||||
obj.space = self.request.space
|
||||
obj.save()
|
||||
return HttpResponseRedirect(reverse('list_invite_link'))
|
||||
|
||||
@@ -213,3 +213,8 @@ class InviteLinkCreate(GroupRequiredMixin, CreateView):
|
||||
context = super(InviteLinkCreate, self).get_context_data(**kwargs)
|
||||
context['title'] = _("Invite Link")
|
||||
return context
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs.update({'user': self.request.user})
|
||||
return kwargs
|
||||
|
||||
@@ -7,6 +7,7 @@ from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import update_session_auth_hash
|
||||
from django.contrib.auth.forms import PasswordChangeForm
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.password_validation import validate_password
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError
|
||||
@@ -16,6 +17,7 @@ from django.shortcuts import get_object_or_404, render, redirect
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext as _
|
||||
from django_scopes import scopes_disabled, scope
|
||||
from django_tables2 import RequestConfig
|
||||
from rest_framework.authtoken.models import Token
|
||||
|
||||
@@ -25,7 +27,7 @@ from cookbook.forms import (CommentForm, Recipe, RecipeBookEntryForm, User,
|
||||
UserPreferenceForm)
|
||||
from cookbook.helper.permission_helper import group_required, share_link_valid, has_group_permission
|
||||
from cookbook.models import (Comment, CookLog, InviteLink, MealPlan,
|
||||
RecipeBook, RecipeBookEntry, ViewLog, ShoppingList)
|
||||
RecipeBook, RecipeBookEntry, ViewLog, ShoppingList, Space)
|
||||
from cookbook.tables import (CookLogTable, RecipeTable, RecipeTableSmall,
|
||||
ViewLogTable)
|
||||
from recipes.settings import DEMO
|
||||
@@ -33,10 +35,12 @@ from recipes.version import BUILD_REF, VERSION_NUMBER
|
||||
|
||||
|
||||
def index(request):
|
||||
if not request.user.is_authenticated:
|
||||
if User.objects.count() < 1 and 'django.contrib.auth.backends.RemoteUserBackend' not in settings.AUTHENTICATION_BACKENDS:
|
||||
return HttpResponseRedirect(reverse_lazy('view_setup'))
|
||||
return HttpResponseRedirect(reverse_lazy('view_search'))
|
||||
with scopes_disabled():
|
||||
if not request.user.is_authenticated:
|
||||
if User.objects.count() < 1 and 'django.contrib.auth.backends.RemoteUserBackend' not in settings.AUTHENTICATION_BACKENDS:
|
||||
return HttpResponseRedirect(reverse_lazy('view_setup'))
|
||||
return HttpResponseRedirect(reverse_lazy('view_search'))
|
||||
|
||||
try:
|
||||
page_map = {
|
||||
UserPreference.SEARCH: reverse_lazy('view_search'),
|
||||
@@ -46,15 +50,12 @@ def index(request):
|
||||
|
||||
return HttpResponseRedirect(page_map.get(request.user.userpreference.default_page))
|
||||
except UserPreference.DoesNotExist:
|
||||
return HttpResponseRedirect(reverse('view_no_group') + '?next=' + request.path)
|
||||
return HttpResponseRedirect(reverse('view_search'))
|
||||
|
||||
|
||||
def search(request):
|
||||
if has_group_permission(request.user, ('guest',)):
|
||||
f = RecipeFilter(
|
||||
request.GET,
|
||||
queryset=Recipe.objects.all().order_by('name')
|
||||
)
|
||||
f = RecipeFilter(request.GET, queryset=Recipe.objects.filter(space=request.user.userpreference.space).all().order_by('name'), space=request.space)
|
||||
|
||||
if request.user.userpreference.search_style == UserPreference.LARGE:
|
||||
table = RecipeTable(f.qs)
|
||||
@@ -63,10 +64,7 @@ def search(request):
|
||||
RequestConfig(request, paginate={'per_page': 25}).configure(table)
|
||||
|
||||
if request.GET == {} and request.user.userpreference.show_recent:
|
||||
qs = Recipe.objects \
|
||||
.filter(viewlog__created_by=request.user) \
|
||||
.order_by('-viewlog__created_at') \
|
||||
.all()
|
||||
qs = Recipe.objects.filter(viewlog__created_by=request.user).filter(space=request.user.userpreference.space).order_by('-viewlog__created_at').all()
|
||||
|
||||
recent_list = []
|
||||
for r in qs:
|
||||
@@ -79,119 +77,83 @@ def search(request):
|
||||
else:
|
||||
last_viewed = None
|
||||
|
||||
return render(
|
||||
request,
|
||||
'index.html',
|
||||
{'recipes': table, 'filter': f, 'last_viewed': last_viewed}
|
||||
)
|
||||
return render(request, 'index.html', {'recipes': table, 'filter': f, 'last_viewed': last_viewed})
|
||||
else:
|
||||
return HttpResponseRedirect(reverse('view_no_group') + '?next=' + request.path)
|
||||
if request.user.is_authenticated:
|
||||
return HttpResponseRedirect(reverse('view_no_group'))
|
||||
else:
|
||||
return HttpResponseRedirect(reverse('account_login') + '?next=' + request.path)
|
||||
|
||||
|
||||
def no_groups(request):
|
||||
if not request.user.is_authenticated:
|
||||
return HttpResponseRedirect(reverse('account_login') + '?next=' + request.GET['next'])
|
||||
if request.user.is_authenticated and request.user.groups.count() > 0:
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
return render(request, 'no_groups_info.html')
|
||||
|
||||
|
||||
def no_space(request):
|
||||
return render(request, 'no_space_info.html')
|
||||
|
||||
|
||||
def no_perm(request):
|
||||
return render(request, 'no_perm_info.html')
|
||||
|
||||
|
||||
def recipe_view(request, pk, share=None):
|
||||
recipe = get_object_or_404(Recipe, pk=pk)
|
||||
with scopes_disabled():
|
||||
recipe = get_object_or_404(Recipe, pk=pk)
|
||||
|
||||
if not has_group_permission(request.user, ('guest',)) 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('view_no_group') + '?next=' + request.path)
|
||||
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('account_login') + '?next=' + request.path)
|
||||
|
||||
comments = Comment.objects.filter(recipe=recipe)
|
||||
if not (has_group_permission(request.user, ('guest',)) and recipe.space == request.space) 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'))
|
||||
|
||||
if request.method == "POST":
|
||||
if not request.user.is_authenticated:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('You do not have the required permissions to perform this action!') # noqa: E501
|
||||
)
|
||||
return HttpResponseRedirect(
|
||||
reverse(
|
||||
'view_recipe',
|
||||
kwargs={'pk': recipe.pk, 'share': share}
|
||||
)
|
||||
)
|
||||
comments = Comment.objects.filter(recipe__space=request.space, recipe=recipe)
|
||||
|
||||
comment_form = CommentForm(request.POST, prefix='comment')
|
||||
if comment_form.is_valid():
|
||||
comment = Comment()
|
||||
comment.recipe = recipe
|
||||
comment.text = comment_form.cleaned_data['text']
|
||||
comment.created_by = request.user
|
||||
if request.method == "POST":
|
||||
if not request.user.is_authenticated:
|
||||
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to perform this action!'))
|
||||
return HttpResponseRedirect(reverse('view_recipe', kwargs={'pk': recipe.pk, 'share': share}))
|
||||
|
||||
comment.save()
|
||||
comment_form = CommentForm(request.POST, prefix='comment')
|
||||
if comment_form.is_valid():
|
||||
comment = Comment()
|
||||
comment.recipe = recipe
|
||||
comment.text = comment_form.cleaned_data['text']
|
||||
comment.created_by = request.user
|
||||
comment.save()
|
||||
|
||||
messages.add_message(
|
||||
request, messages.SUCCESS, _('Comment saved!')
|
||||
)
|
||||
messages.add_message(request, messages.SUCCESS, _('Comment saved!'))
|
||||
|
||||
bookmark_form = RecipeBookEntryForm(request.POST, prefix='bookmark')
|
||||
if bookmark_form.is_valid():
|
||||
bookmark = RecipeBookEntry()
|
||||
bookmark.recipe = recipe
|
||||
bookmark.book = bookmark_form.cleaned_data['book']
|
||||
comment_form = CommentForm()
|
||||
|
||||
try:
|
||||
bookmark.save()
|
||||
except IntegrityError as e:
|
||||
if 'UNIQUE constraint' in str(e.args):
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('This recipe is already linked to the book!')
|
||||
)
|
||||
else:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.SUCCESS,
|
||||
_('Bookmark saved!')
|
||||
)
|
||||
user_servings = None
|
||||
if request.user.is_authenticated:
|
||||
user_servings = CookLog.objects.filter(
|
||||
recipe=recipe,
|
||||
created_by=request.user,
|
||||
servings__gt=0,
|
||||
space=request.space,
|
||||
).all().aggregate(Avg('servings'))['servings__avg']
|
||||
|
||||
comment_form = CommentForm()
|
||||
if not user_servings:
|
||||
user_servings = 0
|
||||
|
||||
user_servings = None
|
||||
if request.user.is_authenticated:
|
||||
user_servings = CookLog.objects.filter(
|
||||
recipe=recipe,
|
||||
created_by=request.user,
|
||||
servings__gt=0
|
||||
).all().aggregate(Avg('servings'))['servings__avg']
|
||||
if request.user.is_authenticated:
|
||||
if not ViewLog.objects.filter(recipe=recipe, created_by=request.user, created_at__gt=(timezone.now() - timezone.timedelta(minutes=5)), space=request.space).exists():
|
||||
ViewLog.objects.create(recipe=recipe, created_by=request.user, space=request.space)
|
||||
|
||||
if not user_servings:
|
||||
user_servings = 0
|
||||
|
||||
if request.user.is_authenticated:
|
||||
if not ViewLog.objects \
|
||||
.filter(recipe=recipe) \
|
||||
.filter(created_by=request.user) \
|
||||
.filter(created_at__gt=(
|
||||
timezone.now() - timezone.timedelta(minutes=5))) \
|
||||
.exists():
|
||||
ViewLog.objects.create(recipe=recipe, created_by=request.user)
|
||||
|
||||
return render(request, 'recipe_view.html', {'recipe': recipe, 'comments': comments, 'comment_form': comment_form, 'share': share, 'user_servings': user_servings})
|
||||
return render(request, 'recipe_view.html', {'recipe': recipe, 'comments': comments, 'comment_form': comment_form, 'share': share, 'user_servings': user_servings})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def books(request):
|
||||
book_list = []
|
||||
|
||||
books = RecipeBook.objects.filter(
|
||||
Q(created_by=request.user) | Q(shared=request.user)
|
||||
).distinct().all()
|
||||
recipe_books = RecipeBook.objects.filter(Q(created_by=request.user) | Q(shared=request.user), space=request.space).distinct().all()
|
||||
|
||||
for b in books:
|
||||
for b in recipe_books:
|
||||
book_list.append(
|
||||
{
|
||||
'book': b,
|
||||
@@ -209,32 +171,24 @@ def meal_plan(request):
|
||||
|
||||
@group_required('user')
|
||||
def meal_plan_entry(request, pk):
|
||||
plan = MealPlan.objects.get(pk=pk)
|
||||
plan = MealPlan.objects.filter(space=request.space).get(pk=pk)
|
||||
|
||||
if plan.created_by != request.user and plan.shared != request.user:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('You do not have the required permissions to view this page!')
|
||||
)
|
||||
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to view this page!'))
|
||||
return HttpResponseRedirect(reverse_lazy('index'))
|
||||
|
||||
same_day_plan = MealPlan.objects \
|
||||
.filter(date=plan.date) \
|
||||
.filter(date=plan.date, space=request.space) \
|
||||
.exclude(pk=plan.pk) \
|
||||
.filter(Q(created_by=request.user) | Q(shared=request.user)) \
|
||||
.order_by('meal_type').all()
|
||||
|
||||
return render(
|
||||
request,
|
||||
'meal_plan_entry.html',
|
||||
{'plan': plan, 'same_day_plan': same_day_plan}
|
||||
)
|
||||
return render(request, 'meal_plan_entry.html', {'plan': plan, 'same_day_plan': same_day_plan})
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def latest_shopping_list(request):
|
||||
sl = ShoppingList.objects.filter(Q(created_by=request.user) | Q(shared=request.user)).filter(finished=False).order_by('-created_at').first()
|
||||
sl = ShoppingList.objects.filter(Q(created_by=request.user) | Q(shared=request.user)).filter(finished=False, space=request.space).order_by('-created_at').first()
|
||||
|
||||
if sl:
|
||||
return HttpResponseRedirect(reverse('view_shopping', kwargs={'pk': sl.pk}) + '?edit=true')
|
||||
@@ -251,7 +205,7 @@ def shopping_list(request, pk=None):
|
||||
r = r.replace('[', '').replace(']', '')
|
||||
if re.match(r'^([0-9])+,([0-9])+[.]*([0-9])*$', r):
|
||||
rid, multiplier = r.split(',')
|
||||
if recipe := Recipe.objects.filter(pk=int(rid)).first():
|
||||
if recipe := Recipe.objects.filter(pk=int(rid), space=request.space).first():
|
||||
recipes.append({'recipe': recipe.id, 'multiplier': multiplier})
|
||||
|
||||
edit = True if 'edit' in request.GET and request.GET['edit'] == 'true' else False
|
||||
@@ -317,23 +271,19 @@ def user_settings(request):
|
||||
if (api_token := Token.objects.filter(user=request.user).first()) is None:
|
||||
api_token = Token.objects.create(user=request.user)
|
||||
|
||||
return render(
|
||||
request,
|
||||
'settings.html',
|
||||
{
|
||||
'preference_form': preference_form,
|
||||
'user_name_form': user_name_form,
|
||||
'password_form': password_form,
|
||||
'api_token': api_token
|
||||
}
|
||||
)
|
||||
return render(request, 'settings.html', {
|
||||
'preference_form': preference_form,
|
||||
'user_name_form': user_name_form,
|
||||
'password_form': password_form,
|
||||
'api_token': api_token
|
||||
})
|
||||
|
||||
|
||||
@group_required('guest')
|
||||
def history(request):
|
||||
view_log = ViewLogTable(
|
||||
ViewLog.objects.filter(
|
||||
created_by=request.user
|
||||
created_by=request.user, space=request.space
|
||||
).order_by('-created_at').all()
|
||||
)
|
||||
cook_log = CookLogTable(
|
||||
@@ -341,11 +291,7 @@ def history(request):
|
||||
created_by=request.user
|
||||
).order_by('-created_at').all()
|
||||
)
|
||||
return render(
|
||||
request,
|
||||
'history.html',
|
||||
{'view_log': view_log, 'cook_log': cook_log}
|
||||
)
|
||||
return render(request, 'history.html', {'view_log': view_log, 'cook_log': cook_log})
|
||||
|
||||
|
||||
@group_required('admin')
|
||||
@@ -357,101 +303,43 @@ def system(request):
|
||||
|
||||
secret_key = False if os.getenv('SECRET_KEY') else True
|
||||
|
||||
return render(
|
||||
request,
|
||||
'system.html',
|
||||
{
|
||||
'gunicorn_media': settings.GUNICORN_MEDIA,
|
||||
'debug': settings.DEBUG,
|
||||
'postgres': postgres,
|
||||
'version': VERSION_NUMBER,
|
||||
'ref': BUILD_REF,
|
||||
'secret_key': secret_key
|
||||
}
|
||||
)
|
||||
return render(request, 'system.html', {
|
||||
'gunicorn_media': settings.GUNICORN_MEDIA,
|
||||
'debug': settings.DEBUG,
|
||||
'postgres': postgres,
|
||||
'version': VERSION_NUMBER,
|
||||
'ref': BUILD_REF,
|
||||
'secret_key': secret_key
|
||||
})
|
||||
|
||||
|
||||
def setup(request):
|
||||
if (User.objects.count() > 0
|
||||
or 'django.contrib.auth.backends.RemoteUserBackend' in settings.AUTHENTICATION_BACKENDS): # noqa: E501
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.ERROR,
|
||||
_('The setup page can only be used to create the first user! If you have forgotten your superuser credentials please consult the django documentation on how to reset passwords.') # noqa: E501
|
||||
)
|
||||
return HttpResponseRedirect(reverse('account_login'))
|
||||
with scopes_disabled():
|
||||
if User.objects.count() > 0 or 'django.contrib.auth.backends.RemoteUserBackend' in settings.AUTHENTICATION_BACKENDS:
|
||||
messages.add_message(request, messages.ERROR, _('The setup page can only be used to create the first user! If you have forgotten your superuser credentials please consult the django documentation on how to reset passwords.'))
|
||||
return HttpResponseRedirect(reverse('account_login'))
|
||||
|
||||
if request.method == 'POST':
|
||||
form = UserCreateForm(request.POST)
|
||||
if form.is_valid():
|
||||
if form.cleaned_data['password'] != form.cleaned_data['password_confirm']: # noqa: E501
|
||||
form.add_error('password', _('Passwords dont match!'))
|
||||
else:
|
||||
user = User(
|
||||
username=form.cleaned_data['name'],
|
||||
is_superuser=True,
|
||||
is_staff=True
|
||||
)
|
||||
try:
|
||||
validate_password(form.cleaned_data['password'], user=user)
|
||||
user.set_password(form.cleaned_data['password'])
|
||||
user.save()
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.SUCCESS,
|
||||
_('User has been created, please login!')
|
||||
)
|
||||
return HttpResponseRedirect(reverse('account_login'))
|
||||
except ValidationError as e:
|
||||
for m in e:
|
||||
form.add_error('password', m)
|
||||
else:
|
||||
form = UserCreateForm()
|
||||
|
||||
return render(request, 'setup.html', {'form': form})
|
||||
|
||||
|
||||
def signup(request, token):
|
||||
try:
|
||||
token = UUID(token, version=4)
|
||||
except ValueError:
|
||||
messages.add_message(
|
||||
request, messages.ERROR, _('Malformed Invite Link supplied!')
|
||||
)
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
if link := InviteLink.objects.filter(
|
||||
valid_until__gte=datetime.today(), used_by=None, uuid=token) \
|
||||
.first():
|
||||
if request.method == 'POST':
|
||||
updated_request = request.POST.copy()
|
||||
if link.username != '':
|
||||
updated_request.update({'name': link.username})
|
||||
|
||||
form = UserCreateForm(updated_request)
|
||||
|
||||
form = UserCreateForm(request.POST)
|
||||
if form.is_valid():
|
||||
if form.cleaned_data['password'] != form.cleaned_data['password_confirm']: # noqa: E501
|
||||
if form.cleaned_data['password'] != form.cleaned_data['password_confirm']:
|
||||
form.add_error('password', _('Passwords dont match!'))
|
||||
else:
|
||||
user = User(
|
||||
username=form.cleaned_data['name'],
|
||||
)
|
||||
user = User(username=form.cleaned_data['name'], is_superuser=True, is_staff=True)
|
||||
try:
|
||||
validate_password(
|
||||
form.cleaned_data['password'], user=user
|
||||
)
|
||||
validate_password(form.cleaned_data['password'], user=user)
|
||||
user.set_password(form.cleaned_data['password'])
|
||||
user.save()
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.SUCCESS,
|
||||
_('User has been created, please login!')
|
||||
)
|
||||
|
||||
link.used_by = user
|
||||
link.save()
|
||||
user.groups.add(link.group)
|
||||
user.groups.add(Group.objects.get(name='admin'))
|
||||
|
||||
user.userpreference.space = Space.objects.first()
|
||||
user.userpreference.save()
|
||||
|
||||
for x in Space.objects.all():
|
||||
x.created_by = user
|
||||
x.save()
|
||||
messages.add_message(request, messages.SUCCESS, _('User has been created, please login!'))
|
||||
return HttpResponseRedirect(reverse('account_login'))
|
||||
except ValidationError as e:
|
||||
for m in e:
|
||||
@@ -459,17 +347,56 @@ def signup(request, token):
|
||||
else:
|
||||
form = UserCreateForm()
|
||||
|
||||
if link.username != '':
|
||||
form.fields['name'].initial = link.username
|
||||
form.fields['name'].disabled = True
|
||||
return render(
|
||||
request, 'account/signup.html', {'form': form, 'link': link}
|
||||
)
|
||||
return render(request, 'setup.html', {'form': form})
|
||||
|
||||
messages.add_message(
|
||||
request, messages.ERROR, _('Invite Link not valid or already used!')
|
||||
)
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
def signup(request, token):
|
||||
with scopes_disabled():
|
||||
try:
|
||||
token = UUID(token, version=4)
|
||||
except ValueError:
|
||||
messages.add_message(request, messages.ERROR, _('Malformed Invite Link supplied!'))
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
if link := InviteLink.objects.filter(valid_until__gte=datetime.today(), used_by=None, uuid=token).first():
|
||||
if request.method == 'POST':
|
||||
updated_request = request.POST.copy()
|
||||
if link.username != '':
|
||||
updated_request.update({'name': link.username})
|
||||
|
||||
form = UserCreateForm(updated_request)
|
||||
|
||||
if form.is_valid():
|
||||
if form.cleaned_data['password'] != form.cleaned_data['password_confirm']: # noqa: E501
|
||||
form.add_error('password', _('Passwords dont match!'))
|
||||
else:
|
||||
user = User(username=form.cleaned_data['name'], )
|
||||
try:
|
||||
validate_password(form.cleaned_data['password'], user=user)
|
||||
user.set_password(form.cleaned_data['password'])
|
||||
user.save()
|
||||
messages.add_message(request, messages.SUCCESS, _('User has been created, please login!'))
|
||||
|
||||
link.used_by = user
|
||||
link.save()
|
||||
user.groups.add(link.group)
|
||||
|
||||
user.userpreference.space = link.space
|
||||
user.userpreference.save()
|
||||
return HttpResponseRedirect(reverse('account_login'))
|
||||
except ValidationError as e:
|
||||
for m in e:
|
||||
form.add_error('password', m)
|
||||
else:
|
||||
form = UserCreateForm()
|
||||
|
||||
if link.username != '':
|
||||
form.fields['name'].initial = link.username
|
||||
form.fields['name'].disabled = True
|
||||
return render(request, 'account/signup.html', {'form': form, 'link': link})
|
||||
|
||||
messages.add_message(request, messages.ERROR, _('Invite Link not valid or already used!'))
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
|
||||
def markdown_info(request):
|
||||
|
||||
Reference in New Issue
Block a user