added ability to use invite link more than once
This commit is contained in:
18
cookbook/migrations/0180_invitelink_reusable.py
Normal file
18
cookbook/migrations/0180_invitelink_reusable.py
Normal file
@ -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),
|
||||||
|
),
|
||||||
|
]
|
@ -601,7 +601,7 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin):
|
|||||||
# remove all inherited fields from food
|
# remove all inherited fields from food
|
||||||
trough = Food.inherit_fields.through
|
trough = Food.inherit_fields.through
|
||||||
trough.objects.all().delete()
|
trough.objects.all().delete()
|
||||||
|
|
||||||
# food is going to inherit attributes
|
# food is going to inherit attributes
|
||||||
if len(inherit) > 0:
|
if len(inherit) > 0:
|
||||||
# ManyToMany cannot be updated through an UPDATE operation
|
# 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)
|
email = models.EmailField(blank=True)
|
||||||
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||||
valid_until = models.DateField(default=default_valid_until)
|
valid_until = models.DateField(default=default_valid_until)
|
||||||
used_by = models.ForeignKey(
|
used_by = models.ForeignKey(User, null=True, on_delete=models.CASCADE, related_name='used_by')
|
||||||
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_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
@ -821,7 +821,7 @@ class RecipeBookEntrySerializer(serializers.ModelSerializer):
|
|||||||
book = validated_data['book']
|
book = validated_data['book']
|
||||||
recipe = validated_data['recipe']
|
recipe = validated_data['recipe']
|
||||||
if not book.get_owner() == self.context['request'].user and not self.context[
|
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)
|
raise NotFound(detail=None, code=None)
|
||||||
obj, created = RecipeBookEntry.objects.get_or_create(book=book, recipe=recipe)
|
obj, created = RecipeBookEntry.objects.get_or_create(book=book, recipe=recipe)
|
||||||
return obj
|
return obj
|
||||||
@ -877,11 +877,11 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer):
|
|||||||
value = value.quantize(
|
value = value.quantize(
|
||||||
Decimal(1)) if value == value.to_integral() else value.normalize() # strips trailing zero
|
Decimal(1)) if value == value.to_integral() else value.normalize() # strips trailing zero
|
||||||
return (
|
return (
|
||||||
obj.name
|
obj.name
|
||||||
or getattr(obj.mealplan, 'title', None)
|
or getattr(obj.mealplan, 'title', None)
|
||||||
or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)])
|
or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)])
|
||||||
or obj.recipe.name
|
or obj.recipe.name
|
||||||
) + f' ({value:.2g})'
|
) + f' ({value:.2g})'
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
# TODO remove once old shopping list
|
# TODO remove once old shopping list
|
||||||
@ -1105,7 +1105,7 @@ class InviteLinkSerializer(WritableNestedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = InviteLink
|
model = InviteLink
|
||||||
fields = (
|
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',)
|
read_only_fields = ('id', 'uuid', 'created_by', 'created_at',)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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 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():
|
if request.user.is_authenticated and not request.user.userspace_set.filter(space=link.space).exists():
|
||||||
link.used_by = request.user
|
if not link.reusable:
|
||||||
link.save()
|
link.used_by = request.user
|
||||||
|
link.save()
|
||||||
|
|
||||||
user_space = UserSpace.objects.create(user=request.user, space=link.space, active=False)
|
user_space = UserSpace.objects.create(user=request.user, space=link.space, active=False)
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
|
|
||||||
"Private_Recipe": "Private Recipe",
|
"Private_Recipe": "Private Recipe",
|
||||||
"Private_Recipe_Help": "Recipe is only shown to you and people its shared with.",
|
"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",
|
"Add_Step": "Add Step",
|
||||||
"Keywords": "Keywords",
|
"Keywords": "Keywords",
|
||||||
"Books": "Books",
|
"Books": "Books",
|
||||||
|
@ -669,7 +669,7 @@ export class Models {
|
|||||||
apiName: "InviteLink",
|
apiName: "InviteLink",
|
||||||
paginated: false,
|
paginated: false,
|
||||||
create: {
|
create: {
|
||||||
params: [["email", "group", "valid_until"]],
|
params: [["email", "group", "valid_until", "reusable"]],
|
||||||
form: {
|
form: {
|
||||||
email: {
|
email: {
|
||||||
form_field: true,
|
form_field: true,
|
||||||
@ -694,6 +694,14 @@ export class Models {
|
|||||||
label: "Valid Until",
|
label: "Valid Until",
|
||||||
placeholder: "",
|
placeholder: "",
|
||||||
},
|
},
|
||||||
|
reusable: {
|
||||||
|
form_field: true,
|
||||||
|
type: "checkbox",
|
||||||
|
field: "reusable",
|
||||||
|
label: "Reusable",
|
||||||
|
help_text: "reusable_help_text",
|
||||||
|
placeholder: "",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1357,6 +1357,12 @@ export interface InviteLink {
|
|||||||
* @memberof InviteLink
|
* @memberof InviteLink
|
||||||
*/
|
*/
|
||||||
used_by?: number | null;
|
used_by?: number | null;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof InviteLink
|
||||||
|
*/
|
||||||
|
reusable?: boolean;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@ -1893,6 +1899,18 @@ export interface Recipe {
|
|||||||
* @memberof Recipe
|
* @memberof Recipe
|
||||||
*/
|
*/
|
||||||
last_cooked?: string;
|
last_cooked?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof Recipe
|
||||||
|
*/
|
||||||
|
_private?: boolean;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Array<CustomFilterShared>}
|
||||||
|
* @memberof Recipe
|
||||||
|
*/
|
||||||
|
shared?: Array<CustomFilterShared>;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
Reference in New Issue
Block a user