diff --git a/.env.template b/.env.template
index 5f4793fdd..745a5fd52 100644
--- a/.env.template
+++ b/.env.template
@@ -68,7 +68,8 @@ GUNICORN_MEDIA=0
# EMAIL_HOST_PASSWORD=
# EMAIL_USE_TLS=0
# EMAIL_USE_SSL=0
-# ACCOUNT_EMAIL_SUBJECT_PREFIX
+# DEFAULT_FROM_EMAIL= # email sender address (default 'webmaster@localhost')
+# ACCOUNT_EMAIL_SUBJECT_PREFIX= # prefix used for account related emails (default "[Tandoor Recipes] ")
# allow authentication via reverse proxy (e.g. authelia), leave off if you dont know what you are doing
# see docs for more information https://vabene1111.github.io/recipes/features/authentication/
diff --git a/cookbook/forms.py b/cookbook/forms.py
index 7c40a1486..bf4675433 100644
--- a/cookbook/forms.py
+++ b/cookbook/forms.py
@@ -1,6 +1,8 @@
from django import forms
+from django.core.exceptions import ValidationError
from django.forms import widgets
from django.utils.translation import gettext_lazy as _
+from django_scopes import scopes_disabled
from django_scopes.forms import SafeModelChoiceField, SafeModelMultipleChoiceField
from emoji_picker.widgets import EmojiPickerTextInput
@@ -42,10 +44,15 @@ class UserPreferenceForm(forms.ModelForm):
)
help_texts = {
- 'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'), # noqa: E501
+ 'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'),
+ # noqa: E501
'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'), # noqa: E501
- 'use_fractions': _('Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'), # noqa: E501
- 'plan_share': _('Users with whom newly created meal plan/shopping list entries should be shared by default.'), # noqa: E501
+ 'use_fractions': _(
+ 'Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'),
+ # noqa: E501
+ 'plan_share': _(
+ 'Users with whom newly created meal plan/shopping list entries should be shared by default.'),
+ # noqa: E501
'show_recent': _('Show recently viewed recipes on search page.'), # noqa: E501
'ingredient_decimals': _('Number of decimals to round ingredients.'), # noqa: E501
'comments': _('If you want to be able to create and see comments underneath recipes.'), # noqa: E501
@@ -69,7 +76,8 @@ class UserNameForm(forms.ModelForm):
fields = ('first_name', 'last_name')
help_texts = {
- 'first_name': _('Both fields are optional. If none are given the username will be displayed instead') # noqa: E501
+ 'first_name': _('Both fields are optional. If none are given the username will be displayed instead')
+ # noqa: E501
}
@@ -128,7 +136,9 @@ class ImportExportBase(forms.Form):
class ImportForm(ImportExportBase):
files = forms.FileField(required=True, widget=forms.ClearableFileInput(attrs={'multiple': True}))
- duplicates = forms.BooleanField(help_text=_('To prevent duplicates recipes with the same name as existing ones are ignored. Check this box to import everything.'), required=False)
+ duplicates = forms.BooleanField(help_text=_(
+ 'To prevent duplicates recipes with the same name as existing ones are ignored. Check this box to import everything.'),
+ required=False)
class ExportForm(ImportExportBase):
@@ -251,7 +261,8 @@ class StorageForm(forms.ModelForm):
fields = ('name', 'method', 'username', 'password', 'token', 'url', 'path')
help_texts = {
- 'url': _('Leave empty for dropbox and enter only base url for nextcloud (/remote.php/webdav/ is added automatically)'),
+ 'url': _(
+ 'Leave empty for dropbox and enter only base url for nextcloud (/remote.php/webdav/ is added automatically)'),
}
@@ -366,7 +377,8 @@ class MealPlanForm(forms.ModelForm):
help_texts = {
'shared': _('You can list default users to share recipes with in the settings.'), # noqa: E501
- 'note': _('You can use markdown to format this field. See the docs here') # noqa: E501
+ 'note': _('You can use markdown to format this field. See the docs here')
+ # noqa: E501
}
widgets = {
@@ -387,17 +399,50 @@ class InviteLinkForm(forms.ModelForm):
super().__init__(*args, **kwargs)
self.fields['space'].queryset = Space.objects.filter(created_by=user).all()
+ def clean_email(self):
+ email = self.cleaned_data['email']
+ with scopes_disabled():
+ if User.objects.filter(email=email).exists():
+ raise ValidationError(_('Email address already taken!'))
+
+ return email
+
+ def clean_username(self):
+ username = self.cleaned_data['username']
+ with scopes_disabled():
+ if User.objects.filter(username=username).exists() or InviteLink.objects.filter(username=username).exists():
+ raise ValidationError(_('Username already taken!'))
+ return username
+
class Meta:
model = InviteLink
- fields = ('username', 'group', 'valid_until', 'space')
+ fields = ('username', 'email', 'group', 'valid_until', 'space')
help_texts = {
- 'username': _('A username is not required, if left blank the new user can choose one.') # noqa: E501
+ 'username': _('A username is not required, if left blank the new user can choose one.'),
+ 'email': _('An email address is not required but if present the invite link will be send to the user.')
}
field_classes = {
'space': SafeModelChoiceField,
}
+class SpaceCreateForm(forms.Form):
+ prefix = 'create'
+ name = forms.CharField()
+
+ def clean_name(self):
+ name = self.cleaned_data['name']
+ with scopes_disabled():
+ if Space.objects.filter(name=name).exists():
+ raise ValidationError(_('Name already taken.'))
+ return name
+
+
+class SpaceJoinForm(forms.Form):
+ prefix = 'join'
+ token = forms.CharField()
+
+
class UserCreateForm(forms.Form):
name = forms.CharField(label='Username')
password = forms.CharField(
diff --git a/cookbook/helper/AllAuthCustomAdapter.py b/cookbook/helper/AllAuthCustomAdapter.py
index 1823265f0..47629283c 100644
--- a/cookbook/helper/AllAuthCustomAdapter.py
+++ b/cookbook/helper/AllAuthCustomAdapter.py
@@ -16,7 +16,7 @@ class AllAuthCustomAdapter(DefaultAccountAdapter):
# disable password reset for now
def send_mail(self, template_prefix, email, context):
- if settings.EMAIL_HOST != '':
+ if settings.EMAIL_HOST == '':
super(AllAuthCustomAdapter, self).send_mail(template_prefix, email, context)
else:
pass
diff --git a/cookbook/helper/scope_middleware.py b/cookbook/helper/scope_middleware.py
index 6b5191df0..c7de19dd4 100644
--- a/cookbook/helper/scope_middleware.py
+++ b/cookbook/helper/scope_middleware.py
@@ -16,6 +16,9 @@ class ScopeMiddleware:
with scopes_disabled():
return self.get_response(request)
+ if request.path.startswith('/signup/'):
+ return self.get_response(request)
+
with scopes_disabled():
if request.user.userpreference.space is None and not reverse('account_logout') in request.path:
return views.no_space(request)
diff --git a/cookbook/migrations/0122_auto_20210527_1712.py b/cookbook/migrations/0122_auto_20210527_1712.py
new file mode 100644
index 000000000..796225ca5
--- /dev/null
+++ b/cookbook/migrations/0122_auto_20210527_1712.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.3 on 2021-05-27 15:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('cookbook', '0121_auto_20210518_1638'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='space',
+ name='allow_files',
+ field=models.BooleanField(default=True),
+ ),
+ migrations.AddField(
+ model_name='space',
+ name='max_users',
+ field=models.IntegerField(default=0),
+ ),
+ ]
diff --git a/cookbook/migrations/0123_invitelink_email.py b/cookbook/migrations/0123_invitelink_email.py
new file mode 100644
index 000000000..96d59b4e4
--- /dev/null
+++ b/cookbook/migrations/0123_invitelink_email.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.3 on 2021-05-28 12:34
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('cookbook', '0122_auto_20210527_1712'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='invitelink',
+ name='email',
+ field=models.EmailField(blank=True, max_length=254),
+ ),
+ ]
diff --git a/cookbook/models.py b/cookbook/models.py
index 7bbda1b30..44ca3609a 100644
--- a/cookbook/models.py
+++ b/cookbook/models.py
@@ -62,6 +62,8 @@ class Space(models.Model):
created_by = models.ForeignKey(User, on_delete=models.PROTECT, null=True)
message = models.CharField(max_length=512, default='', blank=True)
max_recipes = models.IntegerField(default=0)
+ allow_files = models.BooleanField(default=True)
+ max_users = models.IntegerField(default=0)
def __str__(self):
return self.name
@@ -607,6 +609,7 @@ def default_valid_until():
class InviteLink(models.Model, PermissionModelMixin):
uuid = models.UUIDField(default=uuid.uuid4)
username = models.CharField(blank=True, max_length=64)
+ 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(
diff --git a/cookbook/templates/account/password_reset.html b/cookbook/templates/account/password_reset.html
index 74c6012b2..514a5aeef 100644
--- a/cookbook/templates/account/password_reset.html
+++ b/cookbook/templates/account/password_reset.html
@@ -19,7 +19,7 @@
{% csrf_token %}
{{ form | crispy}}
- {% trans "Sign In" %}
+ {% trans "Sign In" %}
{% trans "Sign Up" %}
diff --git a/cookbook/templates/account/signup.html b/cookbook/templates/account/signup.html
index e774cfeb1..88e6a8ea0 100644
--- a/cookbook/templates/account/signup.html
+++ b/cookbook/templates/account/signup.html
@@ -11,8 +11,9 @@
{% trans 'Already have an account?' %} {% trans "Sign In" %}
+ {% endblock %} \ No newline at end of file diff --git a/cookbook/templates/account/signup_closed.html b/cookbook/templates/account/signup_closed.html new file mode 100644 index 000000000..2f48ea4d6 --- /dev/null +++ b/cookbook/templates/account/signup_closed.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} + +{% load i18n %} + +{% block head_title %}{% trans "Sign Up Closed" %}{% endblock %} + +{% block content %} +{% trans "We are sorry, but the sign up is currently closed." %}
+ + {% trans "Sign In" %} + {% trans "Reset Password" %} +{% endblock %} \ No newline at end of file diff --git a/cookbook/templates/base.html b/cookbook/templates/base.html index b0b49cfb1..3ef3b4a8c 100644 --- a/cookbook/templates/base.html +++ b/cookbook/templates/base.html @@ -144,6 +144,9 @@ class="fas fa-user-cog fa-fw"> {% trans 'Settings' %} {% trans 'History' %} + {% if request.user == request.space.created_by %} + {% trans 'Space Settings' %} + {% endif %} {% if user.is_superuser %} -{% trans 'To join an existing space either enter your invite token or click on the invite link the space owner send you.' %}
+ + + +{% trans 'Start your own recipe space and invite other users to it.' %}
+ +| + {{ u.user.username }} + | ++ {% trans 'Remove' %} + | +
{% trans 'There are no members in your space yet!' %}
+ {% endif %} +