diff --git a/cookbook/models.py b/cookbook/models.py
index a0a2cbf39..a8302f696 100644
--- a/cookbook/models.py
+++ b/cookbook/models.py
@@ -983,6 +983,8 @@ class RecipeBook(ExportModelOperationsMixin('book'), models.Model, PermissionMod
shared = models.ManyToManyField(User, blank=True, related_name='shared_with')
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
filter = models.ForeignKey('cookbook.CustomFilter', null=True, blank=True, on_delete=models.SET_NULL)
+ order = models.IntegerField(default=0)
+
space = models.ForeignKey(Space, on_delete=models.CASCADE)
objects = ScopedManager(space='space')
diff --git a/cookbook/serializer.py b/cookbook/serializer.py
index 4c7e2dfb8..873880d61 100644
--- a/cookbook/serializer.py
+++ b/cookbook/serializer.py
@@ -979,7 +979,7 @@ class RecipeBookSerializer(SpacedModelSerializer, WritableNestedModelSerializer)
class Meta:
model = RecipeBook
- fields = ('id', 'name', 'description', 'shared', 'created_by', 'filter')
+ fields = ('id', 'name', 'description', 'shared', 'created_by', 'filter', 'order')
read_only_fields = ('created_by',)
diff --git a/cookbook/templates/recipe_view.html b/cookbook/templates/recipe_view.html
index 2ec2b1179..c0f62d475 100644
--- a/cookbook/templates/recipe_view.html
+++ b/cookbook/templates/recipe_view.html
@@ -79,6 +79,7 @@
window.CUSTOM_LOCALE = '{{ request.LANGUAGE_CODE }}'
window.RECIPE_ID = {{recipe.pk}};
+ window.RECIPE_SERVINGS = '{{ servings }}'
window.SHARE_UID = '{{ share }}';
window.USER_PREF = {
'use_fractions': {% if request.user.userpreference.use_fractions %} true {% else %} false {% endif %},
diff --git a/cookbook/views/api.py b/cookbook/views/api.py
index e4c91b2ba..1b1bd29ab 100644
--- a/cookbook/views/api.py
+++ b/cookbook/views/api.py
@@ -662,8 +662,16 @@ class RecipeBookViewSet(viewsets.ModelViewSet, StandardFilterMixin):
permission_classes = [(CustomIsOwner | CustomIsShared) & CustomTokenHasReadWriteScope]
def get_queryset(self):
+ order_field = self.request.GET.get('order_field')
+ order_direction = self.request.GET.get('order_direction')
+
+ if not order_field:
+ order_field = 'id'
+
+ ordering = f"{'' if order_direction == 'asc' else '-'}{order_field}"
+
self.queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(
- space=self.request.space).distinct()
+ space=self.request.space).distinct().order_by(ordering)
return super().get_queryset()
diff --git a/cookbook/views/views.py b/cookbook/views/views.py
index 3ed22b4b1..55474494d 100644
--- a/cookbook/views/views.py
+++ b/cookbook/views/views.py
@@ -157,7 +157,11 @@ def recipe_view(request, pk, share=None):
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)
- return render(request, 'recipe_view.html', {'recipe': recipe, 'comments': comments, 'comment_form': comment_form, 'share': share, })
+ if request.method == "GET":
+ servings = request.GET.get("servings")
+ return render(request, 'recipe_view.html',
+ {'recipe': recipe, 'comments': comments, 'comment_form': comment_form, 'share': share, 'servings': servings })
+
@group_required('user')
diff --git a/requirements.txt b/requirements.txt
index 796a27253..fd8765989 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,7 +5,7 @@ django-autocomplete-light==3.9.7
django-cleanup==8.0.0
django-crispy-forms==2.0
crispy-bootstrap4==2022.1
-django-tables2==2.5.3
+django-tables2==2.7.0
djangorestframework==3.14.0
drf-writable-nested==0.7.0
django-oauth-toolkit==2.3.0
@@ -40,8 +40,8 @@ django-storages==1.14.2
boto3==1.28.75
django-prometheus==2.2.0
django-hCaptcha==0.2.0
-python-ldap==3.4.3
-django-auth-ldap==4.4.0
+python-ldap==3.4.4
+django-auth-ldap==4.6.0
pytest-factoryboy==2.6.0
pyppeteer==1.0.2
validators==0.20.0
diff --git a/vue/package.json b/vue/package.json
index 3e069f81c..c42298311 100644
--- a/vue/package.json
+++ b/vue/package.json
@@ -13,11 +13,11 @@
"@codemirror/commands": "^6.3.2",
"@codemirror/lang-markdown": "^6.2.3",
"@codemirror/state": "^6.3.3",
- "@codemirror/view": "^6.22.2",
+ "@codemirror/view": "^6.23.1",
"@popperjs/core": "^2.11.7",
"@vue/cli": "^5.0.8",
"@vue/composition-api": "1.7.1",
- "axios": "^1.6.0",
+ "axios": "^1.6.7",
"babel": "^6.23.0",
"babel-core": "^6.26.3",
"babel-loader": "^9.1.0",
@@ -26,7 +26,7 @@
"lodash": "^4.17.21",
"mavon-editor": "^2.10.4",
"moment": "^2.29.4",
- "pinia": "^2.0.30",
+ "pinia": "^2.1.7",
"prismjs": "^1.29.0",
"string-similarity": "^4.0.4",
"vue": "^2.6.14",
@@ -61,9 +61,9 @@
"babel-eslint": "^10.1.0",
"eslint": "^8.46.0",
"eslint-plugin-vue": "^8.7.1",
- "typescript": "~5.1.6",
+ "typescript": "~5.3.3",
"vue-cli-plugin-i18n": "^2.3.2",
- "webpack-bundle-tracker": "1.8.1",
+ "webpack-bundle-tracker": "3.0.1",
"workbox-background-sync": "^7.0.0",
"workbox-expiration": "^6.5.4",
"workbox-navigation-preload": "^7.0.0",
diff --git a/vue/src/apps/CookbookView/CookbookView.vue b/vue/src/apps/CookbookView/CookbookView.vue
index bc922d7e6..9ad465d53 100644
--- a/vue/src/apps/CookbookView/CookbookView.vue
+++ b/vue/src/apps/CookbookView/CookbookView.vue
@@ -11,50 +11,90 @@