From 392ee73719be46b99e8cae65ee8041ae29ae34ce Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Tue, 1 Sep 2020 14:57:20 +0200 Subject: [PATCH] basics of invite link creation --- cookbook/forms.py | 9 ++++++ cookbook/migrations/0077_invitelink.py | 29 +++++++++++++++++++ .../migrations/0078_invitelink_used_by.py | 21 ++++++++++++++ cookbook/migrations/0079_invitelink_group.py | 21 ++++++++++++++ cookbook/models.py | 14 ++++++++- cookbook/tables.py | 7 +++++ cookbook/templates/system.html | 17 +++++++++-- cookbook/urls.py | 2 +- cookbook/views/lists.py | 12 ++++++-- cookbook/views/new.py | 22 ++++++++++++-- 10 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 cookbook/migrations/0077_invitelink.py create mode 100644 cookbook/migrations/0078_invitelink_used_by.py create mode 100644 cookbook/migrations/0079_invitelink_group.py diff --git a/cookbook/forms.py b/cookbook/forms.py index 395209b7d..27db9f60b 100644 --- a/cookbook/forms.py +++ b/cookbook/forms.py @@ -271,6 +271,15 @@ class MealPlanForm(forms.ModelForm): widgets = {'recipe': SelectWidget, 'date': DateWidget, 'shared': MultiSelectWidget} +class InviteLinkForm(forms.ModelForm): + class Meta: + model = InviteLink + fields = ('username', 'group', 'valid_until') + help_texts = { + 'username': _('A username is not required, if left blank the new user can choose one.') + } + + class SuperUserForm(forms.Form): name = forms.CharField() password = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'new-password', 'type': 'password'})) diff --git a/cookbook/migrations/0077_invitelink.py b/cookbook/migrations/0077_invitelink.py new file mode 100644 index 000000000..99210c576 --- /dev/null +++ b/cookbook/migrations/0077_invitelink.py @@ -0,0 +1,29 @@ +# Generated by Django 3.0.7 on 2020-09-01 11:31 + +import datetime +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('cookbook', '0076_shoppinglist_entries'), + ] + + operations = [ + migrations.CreateModel( + name='InviteLink', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('uuid', models.UUIDField(default=uuid.uuid4)), + ('username', models.CharField(blank=True, max_length=64)), + ('valid_until', models.DateField(default=datetime.date(2020, 9, 15))), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/cookbook/migrations/0078_invitelink_used_by.py b/cookbook/migrations/0078_invitelink_used_by.py new file mode 100644 index 000000000..1637427c2 --- /dev/null +++ b/cookbook/migrations/0078_invitelink_used_by.py @@ -0,0 +1,21 @@ +# Generated by Django 3.0.7 on 2020-09-01 11:39 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('cookbook', '0077_invitelink'), + ] + + operations = [ + migrations.AddField( + model_name='invitelink', + name='used_by', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='used_by', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/cookbook/migrations/0079_invitelink_group.py b/cookbook/migrations/0079_invitelink_group.py new file mode 100644 index 000000000..eb1725920 --- /dev/null +++ b/cookbook/migrations/0079_invitelink_group.py @@ -0,0 +1,21 @@ +# Generated by Django 3.0.7 on 2020-09-01 12:54 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0011_update_proxy_permissions'), + ('cookbook', '0078_invitelink_used_by'), + ] + + operations = [ + migrations.AddField( + model_name='invitelink', + name='group', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='auth.Group'), + preserve_default=False, + ), + ] diff --git a/cookbook/models.py b/cookbook/models.py index 7f6e7450c..a3a474491 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -1,8 +1,10 @@ import re import uuid +from datetime import date, timedelta + from annoying.fields import AutoOneToOneField from django.contrib import auth -from django.contrib.auth.models import User +from django.contrib.auth.models import User, Group from django.utils.translation import gettext as _ from django.db import models @@ -296,6 +298,16 @@ class ShareLink(models.Model): created_at = models.DateTimeField(auto_now_add=True) +class InviteLink(models.Model): + uuid = models.UUIDField(default=uuid.uuid4) + username = models.CharField(blank=True, max_length=64) + group = models.ForeignKey(Group, on_delete=models.CASCADE) + valid_until = models.DateField(default=date.today()+timedelta(days=14)) + used_by = models.ForeignKey(User, null=True, on_delete=models.CASCADE, related_name='used_by') + created_by = models.ForeignKey(User, on_delete=models.CASCADE) + created_at = models.DateTimeField(auto_now_add=True) + + class CookLog(models.Model): recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE) created_by = models.ForeignKey(User, on_delete=models.CASCADE) diff --git a/cookbook/tables.py b/cookbook/tables.py index 0d04010ab..836018aed 100644 --- a/cookbook/tables.py +++ b/cookbook/tables.py @@ -117,6 +117,13 @@ class ShoppingListTable(tables.Table): fields = ('id', 'created_by', 'created_at') +class InviteLinkTable(tables.Table): + class Meta: + model = InviteLink + template_name = 'generic/table_template.html' + fields = ('id', 'username', 'group', 'valid_until', 'created_by', 'created_at', 'used_by') + + class ViewLogTable(tables.Table): recipe = tables.LinkColumn('view_recipe', args=[A('recipe_id')]) diff --git a/cookbook/templates/system.html b/cookbook/templates/system.html index 50e86e4e2..3ad9cc923 100644 --- a/cookbook/templates/system.html +++ b/cookbook/templates/system.html @@ -16,8 +16,18 @@

-

{% trans 'Backup & Restore' %}

- {% trans 'Download Backup' %} +
+
+

{% trans 'Invite Links' %}

+ {% trans 'Show Links' %} + +
+
+

{% trans 'Backup & Restore' %}

+ {% trans 'Download Backup' %} + +
+


@@ -59,7 +69,8 @@ {% trans 'Warning' %}{% else %}{% trans 'Ok' %}{% endif %} {% if secret_key %} {% blocktrans %} - You do not have a SECRET_KEY configured in your .env file. Django defaulted to the standard key + You do not have a SECRET_KEY configured in your .env file. Django defaulted to the + standard key provided with the installation which is publicly know and insecure! Please set SECRET_KEY int the .env configuration file. {% endblocktrans %} diff --git a/cookbook/urls.py b/cookbook/urls.py index ecf8ac202..7f6354b2a 100644 --- a/cookbook/urls.py +++ b/cookbook/urls.py @@ -91,7 +91,7 @@ urlpatterns = [ ] -generic_models = (Recipe, RecipeImport, Storage, RecipeBook, MealPlan, SyncLog, Sync, Comment, RecipeBookEntry, Keyword, Food, ShoppingList) +generic_models = (Recipe, RecipeImport, Storage, RecipeBook, MealPlan, SyncLog, Sync, Comment, RecipeBookEntry, Keyword, Food, ShoppingList, InviteLink) for m in generic_models: py_name = get_model_name(m) diff --git a/cookbook/views/lists.py b/cookbook/views/lists.py index 8ae428e71..574e1ce74 100644 --- a/cookbook/views/lists.py +++ b/cookbook/views/lists.py @@ -6,8 +6,8 @@ from django_tables2 import RequestConfig from cookbook.filters import IngredientFilter from cookbook.helper.permission_helper import group_required -from cookbook.models import Keyword, SyncLog, RecipeImport, Storage, Food, ShoppingList -from cookbook.tables import KeywordTable, ImportLogTable, RecipeImportTable, StorageTable, IngredientTable, ShoppingListTable +from cookbook.models import Keyword, SyncLog, RecipeImport, Storage, Food, ShoppingList, InviteLink +from cookbook.tables import KeywordTable, ImportLogTable, RecipeImportTable, StorageTable, IngredientTable, ShoppingListTable, InviteLinkTable @group_required('user') @@ -59,3 +59,11 @@ def storage(request): RequestConfig(request, paginate={'per_page': 25}).configure(table) return render(request, 'generic/list_template.html', {'title': _("Storage Backend"), 'table': table, 'create_url': 'new_storage'}) + + +@group_required('admin') +def invite_link(request): + table = InviteLinkTable(InviteLink.objects.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'}) diff --git a/cookbook/views/new.py b/cookbook/views/new.py index f2fdd925b..464fbe7cc 100644 --- a/cookbook/views/new.py +++ b/cookbook/views/new.py @@ -9,9 +9,9 @@ from django.utils.translation import gettext as _ from django.views.generic import CreateView from cookbook.forms import ImportRecipeForm, RecipeImport, KeywordForm, Storage, StorageForm, InternalRecipeForm, \ - RecipeBookForm, MealPlanForm + RecipeBookForm, MealPlanForm, InviteLinkForm from cookbook.helper.permission_helper import GroupRequiredMixin, group_required -from cookbook.models import Keyword, Recipe, RecipeBook, MealPlan, ShareLink, MealType, Step +from cookbook.models import Keyword, Recipe, RecipeBook, MealPlan, ShareLink, MealType, Step, InviteLink class RecipeCreate(GroupRequiredMixin, CreateView): @@ -162,3 +162,21 @@ class MealPlanCreate(GroupRequiredMixin, CreateView): context['default_recipe'] = Recipe.objects.get(pk=int(recipe)) return context + + +class InviteLinkCreate(GroupRequiredMixin, CreateView): + groups_required = ['admin'] + template_name = "generic/new_template.html" + model = InviteLink + form_class = InviteLinkForm + + def form_valid(self, form): + obj = form.save(commit=False) + obj.created_by = self.request.user + obj.save() + return HttpResponseRedirect(reverse('list_invite_link')) + + def get_context_data(self, **kwargs): + context = super(InviteLinkCreate, self).get_context_data(**kwargs) + context['title'] = _("Invite Link") + return context