Merge branch 'develop' into feature/shopping-ui
This commit is contained in:
commit
31de43196a
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 4.2.7 on 2024-01-14 23:06
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('cookbook', '0207_space_logo_color_128_space_logo_color_144_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='space',
|
||||||
|
name='app_name',
|
||||||
|
field=models.CharField(blank=True, max_length=40, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userpreference',
|
||||||
|
name='max_owned_spaces',
|
||||||
|
field=models.IntegerField(default=100),
|
||||||
|
),
|
||||||
|
]
|
@ -24,7 +24,7 @@ from PIL import Image
|
|||||||
from treebeard.mp_tree import MP_Node, MP_NodeManager
|
from treebeard.mp_tree import MP_Node, MP_NodeManager
|
||||||
|
|
||||||
from recipes.settings import (COMMENT_PREF_DEFAULT, FRACTION_PREF_DEFAULT, KJ_PREF_DEFAULT,
|
from recipes.settings import (COMMENT_PREF_DEFAULT, FRACTION_PREF_DEFAULT, KJ_PREF_DEFAULT,
|
||||||
SORT_TREE_BY_NAME, STICKY_NAV_PREF_DEFAULT)
|
SORT_TREE_BY_NAME, STICKY_NAV_PREF_DEFAULT, MAX_OWNED_SPACES_PREF_DEFAULT)
|
||||||
|
|
||||||
|
|
||||||
def get_user_display_name(self):
|
def get_user_display_name(self):
|
||||||
@ -288,7 +288,7 @@ class Space(ExportModelOperationsMixin('space'), models.Model):
|
|||||||
nav_logo = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_nav_logo')
|
nav_logo = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_nav_logo')
|
||||||
nav_bg_color = models.CharField(max_length=8, default='', blank=True, )
|
nav_bg_color = models.CharField(max_length=8, default='', blank=True, )
|
||||||
nav_text_color = models.CharField(max_length=16, choices=NAV_TEXT_COLORS, default=BLANK)
|
nav_text_color = models.CharField(max_length=16, choices=NAV_TEXT_COLORS, default=BLANK)
|
||||||
|
app_name = models.CharField(max_length=40, null=True, blank=True, )
|
||||||
logo_color_32 = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_32')
|
logo_color_32 = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_32')
|
||||||
logo_color_128 = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_128')
|
logo_color_128 = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_128')
|
||||||
logo_color_144 = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_144')
|
logo_color_144 = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_logo_color_144')
|
||||||
@ -409,7 +409,7 @@ class UserPreference(models.Model, PermissionModelMixin):
|
|||||||
nav_text_color = models.CharField(max_length=16, choices=NAV_TEXT_COLORS, default=DARK)
|
nav_text_color = models.CharField(max_length=16, choices=NAV_TEXT_COLORS, default=DARK)
|
||||||
nav_show_logo = models.BooleanField(default=True)
|
nav_show_logo = models.BooleanField(default=True)
|
||||||
nav_sticky = models.BooleanField(default=STICKY_NAV_PREF_DEFAULT)
|
nav_sticky = models.BooleanField(default=STICKY_NAV_PREF_DEFAULT)
|
||||||
|
max_owned_spaces = models.IntegerField(default=MAX_OWNED_SPACES_PREF_DEFAULT)
|
||||||
default_unit = models.CharField(max_length=32, default='g')
|
default_unit = models.CharField(max_length=32, default='g')
|
||||||
use_fractions = models.BooleanField(default=FRACTION_PREF_DEFAULT)
|
use_fractions = models.BooleanField(default=FRACTION_PREF_DEFAULT)
|
||||||
use_kj = models.BooleanField(default=KJ_PREF_DEFAULT)
|
use_kj = models.BooleanField(default=KJ_PREF_DEFAULT)
|
||||||
@ -434,6 +434,15 @@ class UserPreference(models.Model, PermissionModelMixin):
|
|||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
objects = ScopedManager(space='space')
|
objects = ScopedManager(space='space')
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.pk:
|
||||||
|
self.max_owned_spaces = MAX_OWNED_SPACES_PREF_DEFAULT
|
||||||
|
self.comments = COMMENT_PREF_DEFAULT
|
||||||
|
self.nav_sticky = STICKY_NAV_PREF_DEFAULT
|
||||||
|
self.use_kj = KJ_PREF_DEFAULT
|
||||||
|
self.use_fractions = FRACTION_PREF_DEFAULT
|
||||||
|
|
||||||
|
return super().save(*args, **kwargs)
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.user)
|
return str(self.user)
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ def theme_values(request):
|
|||||||
|
|
||||||
def get_theming_values(request):
|
def get_theming_values(request):
|
||||||
space = None
|
space = None
|
||||||
if request.space:
|
if getattr(request,'space',None):
|
||||||
space = request.space
|
space = request.space
|
||||||
if not request.user.is_authenticated and UNAUTHENTICATED_THEME_FROM_SPACE > 0:
|
if not request.user.is_authenticated and UNAUTHENTICATED_THEME_FROM_SPACE > 0:
|
||||||
with scopes_disabled():
|
with scopes_disabled():
|
||||||
@ -46,6 +46,7 @@ def get_theming_values(request):
|
|||||||
'nav_bg_color': '#ddbf86',
|
'nav_bg_color': '#ddbf86',
|
||||||
'nav_text_class': 'navbar-light',
|
'nav_text_class': 'navbar-light',
|
||||||
'sticky_nav': 'position: sticky; top: 0; left: 0; z-index: 1000;',
|
'sticky_nav': 'position: sticky; top: 0; left: 0; z-index: 1000;',
|
||||||
|
'app_name': 'Tandoor Recipes',
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
@ -73,4 +74,6 @@ def get_theming_values(request):
|
|||||||
tv['nav_bg_color'] = space.nav_bg_color
|
tv['nav_bg_color'] = space.nav_bg_color
|
||||||
if space.nav_text_color and space.nav_text_color in nav_text_type_mapping:
|
if space.nav_text_color and space.nav_text_color in nav_text_type_mapping:
|
||||||
tv['nav_text_class'] = nav_text_type_mapping[space.nav_text_color]
|
tv['nav_text_class'] = nav_text_type_mapping[space.nav_text_color]
|
||||||
|
if space.app_name:
|
||||||
|
tv['app_name'] = space.app_name
|
||||||
return tv
|
return tv
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
from django.test import RequestFactory
|
from django.test import RequestFactory
|
||||||
|
from django_scopes import scopes_disabled
|
||||||
|
|
||||||
from cookbook.models import Space, UserPreference, UserFile
|
from cookbook.models import Space, UserPreference, UserFile
|
||||||
from cookbook.templatetags.theming_tags import theme_values, get_theming_values
|
from cookbook.templatetags.theming_tags import theme_values, get_theming_values
|
||||||
@ -14,28 +15,54 @@ def test_theming_function(space_1, u1_s1):
|
|||||||
request.user = auth.get_user(u1_s1)
|
request.user = auth.get_user(u1_s1)
|
||||||
request.space = space_1
|
request.space = space_1
|
||||||
|
|
||||||
|
# defaults apply without setting anything (user preference is automatically created with these defaults)
|
||||||
assert get_theming_values(request)['theme'] == static('themes/tandoor.min.css')
|
assert get_theming_values(request)['theme'] == static('themes/tandoor.min.css')
|
||||||
assert get_theming_values(request)['nav_bg_color'] == '#ddbf86'
|
assert get_theming_values(request)['nav_bg_color'] == '#ddbf86'
|
||||||
assert get_theming_values(request)['nav_text_class'] == 'navbar-light'
|
assert get_theming_values(request)['nav_text_class'] == 'navbar-light'
|
||||||
assert get_theming_values(request)['nav_logo'] == static('assets/brand_logo.png')
|
assert get_theming_values(request)['nav_logo'] == static('assets/brand_logo.png')
|
||||||
assert get_theming_values(request)['sticky_nav'] == 'position: sticky; top: 0; left: 0; z-index: 1000;'
|
assert get_theming_values(request)['sticky_nav'] == 'position: sticky; top: 0; left: 0; z-index: 1000;'
|
||||||
|
assert get_theming_values(request)['app_name'] == 'Tandoor Recipes'
|
||||||
|
|
||||||
user.userpreference.theme = UserPreference.TANDOOR_DARK
|
with scopes_disabled():
|
||||||
user.userpreference.nav_bg_color = '#ffffff'
|
up = UserPreference.objects.filter(user=request.user).first()
|
||||||
user.userpreference.nav_text_color = UserPreference.LIGHT
|
up.theme = UserPreference.TANDOOR_DARK
|
||||||
user.userpreference.nav_sticky = False
|
up.nav_bg_color = '#ffffff'
|
||||||
user.userpreference.save()
|
up.nav_text_color = UserPreference.LIGHT
|
||||||
|
up.nav_sticky = False
|
||||||
|
up.save()
|
||||||
|
|
||||||
|
request = RequestFactory()
|
||||||
|
request.user = auth.get_user(u1_s1)
|
||||||
|
request.space = space_1
|
||||||
|
|
||||||
|
# user values apply if only those are present
|
||||||
assert get_theming_values(request)['theme'] == static('themes/tandoor_dark.min.css')
|
assert get_theming_values(request)['theme'] == static('themes/tandoor_dark.min.css')
|
||||||
assert get_theming_values(request)['nav_bg_color'] == '#ffffff'
|
assert get_theming_values(request)['nav_bg_color'] == '#ffffff'
|
||||||
assert get_theming_values(request)['nav_text_class'] == 'navbar-dark'
|
assert get_theming_values(request)['nav_text_class'] == 'navbar-dark'
|
||||||
assert get_theming_values(request)['sticky_nav'] == ''
|
assert get_theming_values(request)['sticky_nav'] == ''
|
||||||
|
assert get_theming_values(request)['app_name'] == 'Tandoor Recipes'
|
||||||
|
|
||||||
space_1.space_theme = Space.BOOTSTRAP
|
space_1.space_theme = Space.BOOTSTRAP
|
||||||
space_1.nav_bg_color = '#000000'
|
space_1.nav_bg_color = '#000000'
|
||||||
space_1.nav_text_color = UserPreference.DARK
|
space_1.nav_text_color = UserPreference.DARK
|
||||||
|
space_1.app_name = 'test_app_name'
|
||||||
space_1.save()
|
space_1.save()
|
||||||
|
|
||||||
|
request = RequestFactory()
|
||||||
|
request.user = auth.get_user(u1_s1)
|
||||||
|
request.space = space_1
|
||||||
|
|
||||||
|
# space settings apply when set
|
||||||
assert get_theming_values(request)['theme'] == static('themes/bootstrap.min.css')
|
assert get_theming_values(request)['theme'] == static('themes/bootstrap.min.css')
|
||||||
assert get_theming_values(request)['nav_bg_color'] == '#000000'
|
assert get_theming_values(request)['nav_bg_color'] == '#000000'
|
||||||
assert get_theming_values(request)['nav_text_class'] == 'navbar-light'
|
assert get_theming_values(request)['nav_text_class'] == 'navbar-light'
|
||||||
|
assert get_theming_values(request)['app_name'] == 'test_app_name'
|
||||||
|
|
||||||
|
user.userspace_set.all().delete()
|
||||||
|
request = RequestFactory()
|
||||||
|
request.user = auth.get_user(u1_s1)
|
||||||
|
|
||||||
|
# default user settings should apply when user has no space
|
||||||
|
assert get_theming_values(request)['nav_bg_color'] == '#ffffff'
|
||||||
|
assert get_theming_values(request)['nav_text_class'] == 'navbar-dark'
|
||||||
|
assert get_theming_values(request)['nav_logo'] == static('assets/brand_logo.png')
|
||||||
|
@ -79,6 +79,11 @@ def space_overview(request):
|
|||||||
messages.add_message(request, messages.WARNING, _('This feature is not available in the demo version!'))
|
messages.add_message(request, messages.WARNING, _('This feature is not available in the demo version!'))
|
||||||
else:
|
else:
|
||||||
if create_form.is_valid():
|
if create_form.is_valid():
|
||||||
|
if Space.objects.filter(created_by=request.user).count() >= request.user.userpreference.max_owned_spaces:
|
||||||
|
messages.add_message(request, messages.ERROR,
|
||||||
|
_('You have the reached the maximum amount of spaces that can be owned by you.') + f' ({request.user.userpreference.max_owned_spaces})')
|
||||||
|
return HttpResponseRedirect(reverse('view_space_overview'))
|
||||||
|
|
||||||
created_space = Space.objects.create(
|
created_space = Space.objects.create(
|
||||||
name=create_form.cleaned_data['name'],
|
name=create_form.cleaned_data['name'],
|
||||||
created_by=request.user,
|
created_by=request.user,
|
||||||
@ -491,8 +496,8 @@ def web_manifest(request):
|
|||||||
]
|
]
|
||||||
|
|
||||||
manifest_info = {
|
manifest_info = {
|
||||||
"name": "Tandoor Recipes",
|
"name": theme_values['app_name'],
|
||||||
"short_name": "Tandoor",
|
"short_name": theme_values['app_name'],
|
||||||
"description": _("Manage recipes, shopping list, meal plans and more."),
|
"description": _("Manage recipes, shopping list, meal plans and more."),
|
||||||
"icons": icons,
|
"icons": icons,
|
||||||
"start_url": "./search",
|
"start_url": "./search",
|
||||||
|
@ -524,6 +524,18 @@ The default value for the user preference 'sticky navigation' (always show navba
|
|||||||
STICKY_NAV_PREF_DEFAULT=1
|
STICKY_NAV_PREF_DEFAULT=1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Max owned spaces
|
||||||
|
|
||||||
|
> default `100` - options: `0-X`
|
||||||
|
|
||||||
|
The default for the number of spaces a user can own. By setting to 0 space creation for users will be disabled.
|
||||||
|
Superusers can always bypass this limit.
|
||||||
|
|
||||||
|
```
|
||||||
|
MAX_OWNED_SPACES_PREF_DEFAULT=100
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Cosmetic / Preferences
|
### Cosmetic / Preferences
|
||||||
|
|
||||||
#### Timezone
|
#### Timezone
|
||||||
|
@ -57,6 +57,7 @@ COMMENT_PREF_DEFAULT = bool(int(os.getenv('COMMENT_PREF_DEFAULT', True)))
|
|||||||
FRACTION_PREF_DEFAULT = bool(int(os.getenv('FRACTION_PREF_DEFAULT', False)))
|
FRACTION_PREF_DEFAULT = bool(int(os.getenv('FRACTION_PREF_DEFAULT', False)))
|
||||||
KJ_PREF_DEFAULT = bool(int(os.getenv('KJ_PREF_DEFAULT', False)))
|
KJ_PREF_DEFAULT = bool(int(os.getenv('KJ_PREF_DEFAULT', False)))
|
||||||
STICKY_NAV_PREF_DEFAULT = bool(int(os.getenv('STICKY_NAV_PREF_DEFAULT', True)))
|
STICKY_NAV_PREF_DEFAULT = bool(int(os.getenv('STICKY_NAV_PREF_DEFAULT', True)))
|
||||||
|
MAX_OWNED_SPACES_PREF_DEFAULT = int(os.getenv('MAX_OWNED_SPACES_PREF_DEFAULT', 100))
|
||||||
UNAUTHENTICATED_THEME_FROM_SPACE = int(os.getenv('UNAUTHENTICATED_THEME_FROM_SPACE', 0))
|
UNAUTHENTICATED_THEME_FROM_SPACE = int(os.getenv('UNAUTHENTICATED_THEME_FROM_SPACE', 0))
|
||||||
|
|
||||||
# minimum interval that users can set for automatic sync of shopping lists
|
# minimum interval that users can set for automatic sync of shopping lists
|
||||||
|
Loading…
Reference in New Issue
Block a user