From ba473123ba59175e1b59ffc60fec4954720e3a36 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Thu, 14 Jul 2022 11:28:13 +0200 Subject: [PATCH] added ability to use invite link more than once --- .../migrations/0180_invitelink_reusable.py | 18 ++++++++++++++++++ cookbook/models.py | 7 +++---- cookbook/serializer.py | 14 +++++++------- cookbook/views/views.py | 5 +++-- vue/src/locales/en.json | 2 +- vue/src/utils/models.js | 10 +++++++++- vue/src/utils/openapi/api.ts | 18 ++++++++++++++++++ 7 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 cookbook/migrations/0180_invitelink_reusable.py diff --git a/cookbook/migrations/0180_invitelink_reusable.py b/cookbook/migrations/0180_invitelink_reusable.py new file mode 100644 index 000000000..021035de2 --- /dev/null +++ b/cookbook/migrations/0180_invitelink_reusable.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.6 on 2022-07-14 09:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0179_recipe_private_recipe_shared'), + ] + + operations = [ + migrations.AddField( + model_name='invitelink', + name='reusable', + field=models.BooleanField(default=False), + ), + ] diff --git a/cookbook/models.py b/cookbook/models.py index aa1e2800a..6114e007c 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -601,7 +601,7 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin): # remove all inherited fields from food trough = Food.inherit_fields.through trough.objects.all().delete() - + # food is going to inherit attributes if len(inherit) > 0: # ManyToMany cannot be updated through an UPDATE operation @@ -1008,9 +1008,8 @@ class InviteLink(ExportModelOperationsMixin('invite_link'), models.Model, Permis email = models.EmailField(blank=True) group = models.ForeignKey(Group, on_delete=models.CASCADE) valid_until = models.DateField(default=default_valid_until) - used_by = models.ForeignKey( - User, null=True, on_delete=models.CASCADE, related_name='used_by' - ) + used_by = models.ForeignKey(User, null=True, on_delete=models.CASCADE, related_name='used_by') + reusable = models.BooleanField(default=False) created_by = models.ForeignKey(User, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) diff --git a/cookbook/serializer.py b/cookbook/serializer.py index 14b7e4428..ef3d2d5a9 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -821,7 +821,7 @@ class RecipeBookEntrySerializer(serializers.ModelSerializer): book = validated_data['book'] recipe = validated_data['recipe'] if not book.get_owner() == self.context['request'].user and not self.context[ - 'request'].user in book.get_shared(): + 'request'].user in book.get_shared(): raise NotFound(detail=None, code=None) obj, created = RecipeBookEntry.objects.get_or_create(book=book, recipe=recipe) return obj @@ -877,11 +877,11 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer): value = value.quantize( Decimal(1)) if value == value.to_integral() else value.normalize() # strips trailing zero return ( - obj.name - or getattr(obj.mealplan, 'title', None) - or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)]) - or obj.recipe.name - ) + f' ({value:.2g})' + obj.name + or getattr(obj.mealplan, 'title', None) + or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)]) + or obj.recipe.name + ) + f' ({value:.2g})' def update(self, instance, validated_data): # TODO remove once old shopping list @@ -1105,7 +1105,7 @@ class InviteLinkSerializer(WritableNestedModelSerializer): class Meta: model = InviteLink fields = ( - 'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'created_by', 'created_at',) + 'id', 'uuid', 'email', 'group', 'valid_until', 'used_by', 'reusable', 'created_by', 'created_at',) read_only_fields = ('id', 'uuid', 'created_by', 'created_at',) diff --git a/cookbook/views/views.py b/cookbook/views/views.py index 8dbfd6718..dad7bd299 100644 --- a/cookbook/views/views.py +++ b/cookbook/views/views.py @@ -431,8 +431,9 @@ def invite_link(request, token): if link := InviteLink.objects.filter(valid_until__gte=datetime.today(), used_by=None, uuid=token).first(): if request.user.is_authenticated and not request.user.userspace_set.filter(space=link.space).exists(): - link.used_by = request.user - link.save() + if not link.reusable: + link.used_by = request.user + link.save() user_space = UserSpace.objects.create(user=request.user, space=link.space, active=False) diff --git a/vue/src/locales/en.json b/vue/src/locales/en.json index 37df9e91e..3b3c34ad3 100644 --- a/vue/src/locales/en.json +++ b/vue/src/locales/en.json @@ -71,7 +71,7 @@ "Private_Recipe": "Private Recipe", "Private_Recipe_Help": "Recipe is only shown to you and people its shared with.", - + "reusable_help_text": "Should the invite link be usable for more than one user.", "Add_Step": "Add Step", "Keywords": "Keywords", "Books": "Books", diff --git a/vue/src/utils/models.js b/vue/src/utils/models.js index 53b720978..813fd12a1 100644 --- a/vue/src/utils/models.js +++ b/vue/src/utils/models.js @@ -669,7 +669,7 @@ export class Models { apiName: "InviteLink", paginated: false, create: { - params: [["email", "group", "valid_until"]], + params: [["email", "group", "valid_until", "reusable"]], form: { email: { form_field: true, @@ -694,6 +694,14 @@ export class Models { label: "Valid Until", placeholder: "", }, + reusable: { + form_field: true, + type: "checkbox", + field: "reusable", + label: "Reusable", + help_text: "reusable_help_text", + placeholder: "", + }, }, }, } diff --git a/vue/src/utils/openapi/api.ts b/vue/src/utils/openapi/api.ts index aa979840f..62c7b3f82 100644 --- a/vue/src/utils/openapi/api.ts +++ b/vue/src/utils/openapi/api.ts @@ -1357,6 +1357,12 @@ export interface InviteLink { * @memberof InviteLink */ used_by?: number | null; + /** + * + * @type {boolean} + * @memberof InviteLink + */ + reusable?: boolean; /** * * @type {string} @@ -1893,6 +1899,18 @@ export interface Recipe { * @memberof Recipe */ last_cooked?: string; + /** + * + * @type {boolean} + * @memberof Recipe + */ + _private?: boolean; + /** + * + * @type {Array} + * @memberof Recipe + */ + shared?: Array; } /** *