lots of theming related changes
- upload a custom logo for your space - space settings can override user settings for theming - spaces can upload custom CSS overrides - allow users to disable showing the tandoor/space logo - allow changing navigation background color to any color desired - allow switching navigation text color between dark/light (different effects depending on theme)
This commit is contained in:
parent
aa0d6b5a6b
commit
3d8b1d6ccb
@ -60,9 +60,9 @@ admin.site.register(UserSpace, UserSpaceAdmin)
|
|||||||
|
|
||||||
|
|
||||||
class UserPreferenceAdmin(admin.ModelAdmin):
|
class UserPreferenceAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'theme', 'nav_color', 'default_page')
|
list_display = ('name', 'theme', 'default_page')
|
||||||
search_fields = ('user__username',)
|
search_fields = ('user__username',)
|
||||||
list_filter = ('theme', 'nav_color', 'default_page',)
|
list_filter = ('theme', 'default_page',)
|
||||||
date_hierarchy = 'created_at'
|
date_hierarchy = 'created_at'
|
||||||
filter_horizontal = ('plan_share', 'shopping_share',)
|
filter_horizontal = ('plan_share', 'shopping_share',)
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ admin.site.register(UserPreference, UserPreferenceAdmin)
|
|||||||
|
|
||||||
|
|
||||||
class SearchPreferenceAdmin(admin.ModelAdmin):
|
class SearchPreferenceAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'search', 'trigram_threshold', )
|
list_display = ('name', 'search', 'trigram_threshold',)
|
||||||
search_fields = ('user__username',)
|
search_fields = ('user__username',)
|
||||||
list_filter = ('search',)
|
list_filter = ('search',)
|
||||||
|
|
||||||
@ -169,7 +169,7 @@ def delete_unattached_steps(modeladmin, request, queryset):
|
|||||||
|
|
||||||
class StepAdmin(admin.ModelAdmin):
|
class StepAdmin(admin.ModelAdmin):
|
||||||
list_display = ('recipe_and_name', 'order', 'space')
|
list_display = ('recipe_and_name', 'order', 'space')
|
||||||
ordering = ('recipe__name', 'name', 'space', )
|
ordering = ('recipe__name', 'name', 'space',)
|
||||||
search_fields = ('name', 'recipe__name')
|
search_fields = ('name', 'recipe__name')
|
||||||
actions = [delete_unattached_steps]
|
actions = [delete_unattached_steps]
|
||||||
|
|
||||||
@ -198,7 +198,7 @@ def rebuild_index(modeladmin, request, queryset):
|
|||||||
class RecipeAdmin(admin.ModelAdmin):
|
class RecipeAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'internal', 'created_by', 'storage', 'space')
|
list_display = ('name', 'internal', 'created_by', 'storage', 'space')
|
||||||
search_fields = ('name', 'created_by__username')
|
search_fields = ('name', 'created_by__username')
|
||||||
ordering = ('name', 'created_by__username', )
|
ordering = ('name', 'created_by__username',)
|
||||||
list_filter = ('internal',)
|
list_filter = ('internal',)
|
||||||
date_hierarchy = 'created_at'
|
date_hierarchy = 'created_at'
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ admin.site.register(Recipe, RecipeAdmin)
|
|||||||
|
|
||||||
class UnitAdmin(admin.ModelAdmin):
|
class UnitAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'space')
|
list_display = ('name', 'space')
|
||||||
ordering = ('name', 'space', )
|
ordering = ('name', 'space',)
|
||||||
search_fields = ('name',)
|
search_fields = ('name',)
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,64 +33,6 @@ class DateWidget(forms.DateInput):
|
|||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class UserPreferenceForm(forms.ModelForm):
|
|
||||||
prefix = 'preference'
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
space = kwargs.pop('space')
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.fields['plan_share'].queryset = User.objects.filter(userspace__space=space).all()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = UserPreference
|
|
||||||
fields = (
|
|
||||||
'default_unit', 'use_fractions', 'use_kj', 'theme', 'nav_color',
|
|
||||||
'sticky_navbar', 'default_page', 'plan_share', 'ingredient_decimals', 'comments', 'left_handed', 'show_step_ingredients',
|
|
||||||
)
|
|
||||||
|
|
||||||
labels = {
|
|
||||||
'default_unit': _('Default unit'),
|
|
||||||
'use_fractions': _('Use fractions'),
|
|
||||||
'use_kj': _('Use KJ'),
|
|
||||||
'theme': _('Theme'),
|
|
||||||
'nav_color': _('Navbar color'),
|
|
||||||
'sticky_navbar': _('Sticky navbar'),
|
|
||||||
'default_page': _('Default page'),
|
|
||||||
'plan_share': _('Plan sharing'),
|
|
||||||
'ingredient_decimals': _('Ingredient decimal places'),
|
|
||||||
'shopping_auto_sync': _('Shopping list auto sync period'),
|
|
||||||
'comments': _('Comments'),
|
|
||||||
'left_handed': _('Left-handed mode'),
|
|
||||||
'show_step_ingredients': _('Show step ingredients table')
|
|
||||||
}
|
|
||||||
|
|
||||||
help_texts = {
|
|
||||||
'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'),
|
|
||||||
'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'),
|
|
||||||
'use_fractions': _(
|
|
||||||
'Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'),
|
|
||||||
'use_kj': _('Display nutritional energy amounts in joules instead of calories'),
|
|
||||||
'plan_share': _('Users with whom newly created meal plans should be shared by default.'),
|
|
||||||
'shopping_share': _('Users with whom to share shopping lists.'),
|
|
||||||
'ingredient_decimals': _('Number of decimals to round ingredients.'),
|
|
||||||
'comments': _('If you want to be able to create and see comments underneath recipes.'),
|
|
||||||
'shopping_auto_sync': _(
|
|
||||||
'Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but might use a little bit '
|
|
||||||
'of mobile data. If lower than instance limit it is reset when saving.'
|
|
||||||
),
|
|
||||||
'sticky_navbar': _('Makes the navbar stick to the top of the page.'),
|
|
||||||
'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'),
|
|
||||||
'mealplan_autoexclude_onhand': _('Exclude ingredients that are on hand.'),
|
|
||||||
'left_handed': _('Will optimize the UI for use with your left hand.'),
|
|
||||||
'show_step_ingredients': _('Add ingredients table next to recipe steps. Applies at creation time for manually created and URL imported recipes. Individual steps can be overridden in the edit recipe view.')
|
|
||||||
}
|
|
||||||
|
|
||||||
widgets = {
|
|
||||||
'plan_share': MultiSelectWidget,
|
|
||||||
'shopping_share': MultiSelectWidget,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class UserNameForm(forms.ModelForm):
|
class UserNameForm(forms.ModelForm):
|
||||||
prefix = 'name'
|
prefix = 'name'
|
||||||
|
|
||||||
|
@ -0,0 +1,128 @@
|
|||||||
|
# Generated by Django 4.2.7 on 2024-01-01 18:44
|
||||||
|
import django
|
||||||
|
from django.db import migrations, models
|
||||||
|
from django_scopes import scopes_disabled
|
||||||
|
|
||||||
|
TANDOOR = 'TANDOOR'
|
||||||
|
TANDOOR_DARK = 'TANDOOR_DARK'
|
||||||
|
BOOTSTRAP = 'BOOTSTRAP'
|
||||||
|
DARKLY = 'DARKLY'
|
||||||
|
FLATLY = 'FLATLY'
|
||||||
|
SUPERHERO = 'SUPERHERO'
|
||||||
|
|
||||||
|
PRIMARY = 'PRIMARY'
|
||||||
|
SECONDARY = 'SECONDARY'
|
||||||
|
SUCCESS = 'SUCCESS'
|
||||||
|
INFO = 'INFO'
|
||||||
|
WARNING = 'WARNING'
|
||||||
|
DANGER = 'DANGER'
|
||||||
|
LIGHT = 'LIGHT'
|
||||||
|
DARK = 'DARK'
|
||||||
|
|
||||||
|
|
||||||
|
# ['light', 'warning', 'info', 'success'] --> light (theming_tags L45)
|
||||||
|
def get_nav_bg_color(theme, nav_color):
|
||||||
|
if theme == TANDOOR: # primary not actually primary color but override existed before update, same for dark
|
||||||
|
return {PRIMARY: '#ddbf86', SECONDARY: '#b55e4f', SUCCESS: '#82aa8b', INFO: '#385f84', WARNING: '#eaaa21', DANGER: '#a7240e', LIGHT: '#cfd5cd', DARK: '#221e1e'}[nav_color]
|
||||||
|
if theme == TANDOOR_DARK:
|
||||||
|
return {PRIMARY: '#ddbf86', SECONDARY: '#b55e4f', SUCCESS: '#82aa8b', INFO: '#385f84', WARNING: '#eaaa21', DANGER: '#a7240e', LIGHT: '#cfd5cd', DARK: '#221e1e'}[nav_color]
|
||||||
|
if theme == BOOTSTRAP:
|
||||||
|
return {PRIMARY: '#007bff', SECONDARY: '#6c757d', SUCCESS: '#28a745', INFO: '#17a2b8', WARNING: '#ffc107', DANGER: '#dc3545', LIGHT: '#f8f9fa', DARK: '#343a40'}[nav_color]
|
||||||
|
if theme == DARKLY:
|
||||||
|
return {PRIMARY: '#375a7f', SECONDARY: '#444', SUCCESS: '#00bc8c', INFO: '#3498DB', WARNING: '#F39C12', DANGER: '#E74C3C', LIGHT: '#999', DARK: '#303030'}[nav_color]
|
||||||
|
if theme == FLATLY:
|
||||||
|
return {PRIMARY: '#2C3E50', SECONDARY: '#95a5a6', SUCCESS: '#18BC9C', INFO: '#3498DB', WARNING: '#F39C12', DANGER: '#E74C3C', LIGHT: '#ecf0f1', DARK: '#7b8a8b'}[nav_color]
|
||||||
|
if theme == SUPERHERO:
|
||||||
|
return {PRIMARY: '#DF691A', SECONDARY: '#4E5D6C', SUCCESS: '#5cb85c', INFO: '#5bc0de', WARNING: '#f0ad4e', DANGER: '#d9534f', LIGHT: '#abb6c2', DARK: '#4E5D6C'}[nav_color]
|
||||||
|
|
||||||
|
|
||||||
|
def get_nav_text_color(theme, nav_color):
|
||||||
|
if theme == TANDOOR:
|
||||||
|
return {PRIMARY: DARK, SECONDARY: DARK, SUCCESS: DARK, INFO: DARK, WARNING: DARK, DANGER: DARK, LIGHT: DARK, DARK: DARK}[nav_color]
|
||||||
|
if theme == TANDOOR_DARK:
|
||||||
|
return {PRIMARY: DARK, SECONDARY: DARK, SUCCESS: DARK, INFO: DARK, WARNING: DARK, DANGER: DARK, LIGHT: DARK, DARK: DARK}[nav_color]
|
||||||
|
if theme == BOOTSTRAP:
|
||||||
|
return {PRIMARY: DARK, SECONDARY: DARK, SUCCESS: DARK, INFO: DARK, WARNING: DARK, DANGER: DARK, LIGHT: DARK, DARK: DARK}[nav_color]
|
||||||
|
if theme == DARKLY:
|
||||||
|
return {PRIMARY: DARK, SECONDARY: DARK, SUCCESS: DARK, INFO: DARK, WARNING: DARK, DANGER: DARK, LIGHT: DARK, DARK: DARK}[nav_color]
|
||||||
|
if theme == FLATLY:
|
||||||
|
return {PRIMARY: DARK, SECONDARY: DARK, SUCCESS: LIGHT, INFO: LIGHT, WARNING: LIGHT, DANGER: DARK, LIGHT: LIGHT, DARK: DARK}[nav_color]
|
||||||
|
if theme == SUPERHERO:
|
||||||
|
return {PRIMARY: DARK, SECONDARY: DARK, SUCCESS: LIGHT, INFO: LIGHT, WARNING: LIGHT, DANGER: DARK, LIGHT: LIGHT, DARK: DARK}[nav_color]
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_colors(apps, schema_editor):
|
||||||
|
with scopes_disabled():
|
||||||
|
# in case any food had a non digit fdc ID before this migration, remove it
|
||||||
|
UserPreference = apps.get_model('cookbook', 'UserPreference')
|
||||||
|
|
||||||
|
update_ups = []
|
||||||
|
for up in UserPreference.objects.all():
|
||||||
|
if up.theme != TANDOOR or up.nav_color != PRIMARY:
|
||||||
|
up.nav_bg_color = get_nav_bg_color(up.theme, up.nav_color)
|
||||||
|
up.nav_text_color = get_nav_text_color(up.theme, up.nav_color)
|
||||||
|
up.nav_show_logo = (up.theme == TANDOOR or up.theme == TANDOOR_DARK)
|
||||||
|
update_ups.append(up)
|
||||||
|
|
||||||
|
UserPreference.objects.bulk_update(update_ups, ['nav_bg_color', 'nav_text_color', 'nav_show_logo'])
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('cookbook', '0205_alter_food_fdc_id_alter_propertytype_fdc_id'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='userpreference',
|
||||||
|
old_name='sticky_navbar',
|
||||||
|
new_name='nav_sticky',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userpreference',
|
||||||
|
name='nav_bg_color',
|
||||||
|
field=models.CharField(default='#ddbf86', max_length=8),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userpreference',
|
||||||
|
name='nav_text_color',
|
||||||
|
field=models.CharField(choices=[('LIGHT', 'Light'), ('DARK', 'Dark')], default='DARK', max_length=16),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userpreference',
|
||||||
|
name='nav_show_logo',
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
migrations.RunPython(get_current_colors),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='userpreference',
|
||||||
|
name='nav_color',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='space',
|
||||||
|
name='nav_bg_color',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=8),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='space',
|
||||||
|
name='nav_logo',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='space_nav_logo', to='cookbook.userfile'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='space',
|
||||||
|
name='nav_text_color',
|
||||||
|
field=models.CharField(choices=[('BLANK', '-------'), ('LIGHT', 'Light'), ('DARK', 'Dark')], default='BLANK', max_length=16),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='space',
|
||||||
|
name='space_theme',
|
||||||
|
field=models.CharField(choices=[('BLANK', '-------'), ('TANDOOR', 'Tandoor'), ('BOOTSTRAP', 'Bootstrap'), ('DARKLY', 'Darkly'), ('FLATLY', 'Flatly'), ('SUPERHERO', 'Superhero'), ('TANDOOR_DARK', 'Tandoor Dark (INCOMPLETE)')],
|
||||||
|
default='BLANK',
|
||||||
|
max_length=128),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='space',
|
||||||
|
name='custom_space_theme',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='space_theme', to='cookbook.userfile'),
|
||||||
|
),
|
||||||
|
]
|
@ -251,8 +251,44 @@ class FoodInheritField(models.Model, PermissionModelMixin):
|
|||||||
|
|
||||||
|
|
||||||
class Space(ExportModelOperationsMixin('space'), models.Model):
|
class Space(ExportModelOperationsMixin('space'), models.Model):
|
||||||
|
# TODO remove redundant theming constants
|
||||||
|
# Themes
|
||||||
|
BLANK = 'BLANK'
|
||||||
|
TANDOOR = 'TANDOOR'
|
||||||
|
TANDOOR_DARK = 'TANDOOR_DARK'
|
||||||
|
BOOTSTRAP = 'BOOTSTRAP'
|
||||||
|
DARKLY = 'DARKLY'
|
||||||
|
FLATLY = 'FLATLY'
|
||||||
|
SUPERHERO = 'SUPERHERO'
|
||||||
|
|
||||||
|
THEMES = (
|
||||||
|
(BLANK, '-------'),
|
||||||
|
(TANDOOR, 'Tandoor'),
|
||||||
|
(BOOTSTRAP, 'Bootstrap'),
|
||||||
|
(DARKLY, 'Darkly'),
|
||||||
|
(FLATLY, 'Flatly'),
|
||||||
|
(SUPERHERO, 'Superhero'),
|
||||||
|
(TANDOOR_DARK, 'Tandoor Dark (INCOMPLETE)'),
|
||||||
|
)
|
||||||
|
|
||||||
|
LIGHT = 'LIGHT'
|
||||||
|
DARK = 'DARK'
|
||||||
|
|
||||||
|
NAV_TEXT_COLORS = (
|
||||||
|
(BLANK, '-------'),
|
||||||
|
(LIGHT, 'Light'),
|
||||||
|
(DARK, 'Dark')
|
||||||
|
)
|
||||||
|
|
||||||
name = models.CharField(max_length=128, default='Default')
|
name = models.CharField(max_length=128, default='Default')
|
||||||
|
|
||||||
image = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_image')
|
image = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_image')
|
||||||
|
space_theme = models.CharField(choices=THEMES, max_length=128, default=TANDOOR)
|
||||||
|
custom_space_theme = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_theme')
|
||||||
|
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_text_color = models.CharField(max_length=16, choices=NAV_TEXT_COLORS, default=DARK)
|
||||||
|
|
||||||
created_by = models.ForeignKey(User, on_delete=models.PROTECT, null=True)
|
created_by = models.ForeignKey(User, on_delete=models.PROTECT, null=True)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
message = models.CharField(max_length=512, default='', blank=True)
|
message = models.CharField(max_length=512, default='', blank=True)
|
||||||
@ -338,22 +374,10 @@ class UserPreference(models.Model, PermissionModelMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Nav colors
|
# Nav colors
|
||||||
PRIMARY = 'PRIMARY'
|
|
||||||
SECONDARY = 'SECONDARY'
|
|
||||||
SUCCESS = 'SUCCESS'
|
|
||||||
INFO = 'INFO'
|
|
||||||
WARNING = 'WARNING'
|
|
||||||
DANGER = 'DANGER'
|
|
||||||
LIGHT = 'LIGHT'
|
LIGHT = 'LIGHT'
|
||||||
DARK = 'DARK'
|
DARK = 'DARK'
|
||||||
|
|
||||||
COLORS = (
|
NAV_TEXT_COLORS = (
|
||||||
(PRIMARY, 'Primary'),
|
|
||||||
(SECONDARY, 'Secondary'),
|
|
||||||
(SUCCESS, 'Success'),
|
|
||||||
(INFO, 'Info'),
|
|
||||||
(WARNING, 'Warning'),
|
|
||||||
(DANGER, 'Danger'),
|
|
||||||
(LIGHT, 'Light'),
|
(LIGHT, 'Light'),
|
||||||
(DARK, 'Dark')
|
(DARK, 'Dark')
|
||||||
)
|
)
|
||||||
@ -371,8 +395,13 @@ class UserPreference(models.Model, PermissionModelMixin):
|
|||||||
|
|
||||||
user = AutoOneToOneField(User, on_delete=models.CASCADE, primary_key=True)
|
user = AutoOneToOneField(User, on_delete=models.CASCADE, primary_key=True)
|
||||||
image = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='user_image')
|
image = models.ForeignKey("UserFile", on_delete=models.SET_NULL, null=True, blank=True, related_name='user_image')
|
||||||
|
|
||||||
theme = models.CharField(choices=THEMES, max_length=128, default=TANDOOR)
|
theme = models.CharField(choices=THEMES, max_length=128, default=TANDOOR)
|
||||||
nav_color = models.CharField(choices=COLORS, max_length=128, default=PRIMARY)
|
nav_bg_color = models.CharField(max_length=8, default='#ddbf86')
|
||||||
|
nav_text_color = models.CharField(max_length=16, choices=NAV_TEXT_COLORS, default=DARK)
|
||||||
|
nav_show_logo = models.BooleanField(default=True)
|
||||||
|
nav_sticky = models.BooleanField(default=STICKY_NAV_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)
|
||||||
@ -382,7 +411,6 @@ class UserPreference(models.Model, PermissionModelMixin):
|
|||||||
ingredient_decimals = models.IntegerField(default=2)
|
ingredient_decimals = models.IntegerField(default=2)
|
||||||
comments = models.BooleanField(default=COMMENT_PREF_DEFAULT)
|
comments = models.BooleanField(default=COMMENT_PREF_DEFAULT)
|
||||||
shopping_auto_sync = models.IntegerField(default=5)
|
shopping_auto_sync = models.IntegerField(default=5)
|
||||||
sticky_navbar = models.BooleanField(default=STICKY_NAV_PREF_DEFAULT)
|
|
||||||
mealplan_autoadd_shopping = models.BooleanField(default=False)
|
mealplan_autoadd_shopping = models.BooleanField(default=False)
|
||||||
mealplan_autoexclude_onhand = models.BooleanField(default=True)
|
mealplan_autoexclude_onhand = models.BooleanField(default=True)
|
||||||
mealplan_autoinclude_related = models.BooleanField(default=True)
|
mealplan_autoinclude_related = models.BooleanField(default=True)
|
||||||
|
@ -212,7 +212,7 @@ class UserFileSerializer(serializers.ModelSerializer):
|
|||||||
Image.open(obj.file.file.file)
|
Image.open(obj.file.file.file)
|
||||||
return self.context['request'].build_absolute_uri(obj.file.url)
|
return self.context['request'].build_absolute_uri(obj.file.url)
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc()
|
# traceback.print_exc()
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def check_file_limit(self, validated_data):
|
def check_file_limit(self, validated_data):
|
||||||
@ -260,7 +260,7 @@ class UserFileViewSerializer(serializers.ModelSerializer):
|
|||||||
Image.open(obj.file.file.file)
|
Image.open(obj.file.file.file)
|
||||||
return self.context['request'].build_absolute_uri(obj.file.url)
|
return self.context['request'].build_absolute_uri(obj.file.url)
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc()
|
# traceback.print_exc()
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
@ -281,6 +281,8 @@ class SpaceSerializer(WritableNestedModelSerializer):
|
|||||||
file_size_mb = serializers.SerializerMethodField('get_file_size_mb')
|
file_size_mb = serializers.SerializerMethodField('get_file_size_mb')
|
||||||
food_inherit = FoodInheritFieldSerializer(many=True)
|
food_inherit = FoodInheritFieldSerializer(many=True)
|
||||||
image = UserFileViewSerializer(required=False, many=False, allow_null=True)
|
image = UserFileViewSerializer(required=False, many=False, allow_null=True)
|
||||||
|
nav_logo = UserFileViewSerializer(required=False, many=False, allow_null=True)
|
||||||
|
custom_space_theme = UserFileViewSerializer(required=False, many=False, allow_null=True)
|
||||||
|
|
||||||
def get_user_count(self, obj):
|
def get_user_count(self, obj):
|
||||||
return UserSpace.objects.filter(space=obj).count()
|
return UserSpace.objects.filter(space=obj).count()
|
||||||
@ -302,7 +304,7 @@ class SpaceSerializer(WritableNestedModelSerializer):
|
|||||||
fields = (
|
fields = (
|
||||||
'id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users',
|
'id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users',
|
||||||
'allow_sharing', 'demo', 'food_inherit', 'user_count', 'recipe_count', 'file_size_mb',
|
'allow_sharing', 'demo', 'food_inherit', 'user_count', 'recipe_count', 'file_size_mb',
|
||||||
'image', 'use_plural',)
|
'image', 'nav_logo', 'space_theme', 'custom_space_theme', 'nav_bg_color', 'nav_text_color', 'use_plural',)
|
||||||
read_only_fields = (
|
read_only_fields = (
|
||||||
'id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing',
|
'id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing',
|
||||||
'demo',)
|
'demo',)
|
||||||
@ -372,8 +374,8 @@ class UserPreferenceSerializer(WritableNestedModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = UserPreference
|
model = UserPreference
|
||||||
fields = (
|
fields = (
|
||||||
'user', 'image', 'theme', 'nav_color', 'default_unit', 'default_page', 'use_fractions', 'use_kj',
|
'user', 'image', 'theme', 'nav_bg_color', 'nav_text_color', 'nav_show_logo', 'default_unit', 'default_page', 'use_fractions', 'use_kj',
|
||||||
'plan_share', 'sticky_navbar',
|
'plan_share', 'nav_sticky',
|
||||||
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping',
|
'ingredient_decimals', 'comments', 'shopping_auto_sync', 'mealplan_autoadd_shopping',
|
||||||
'food_inherit_default', 'default_delay',
|
'food_inherit_default', 'default_delay',
|
||||||
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days',
|
'mealplan_autoinclude_related', 'mealplan_autoexclude_onhand', 'shopping_share', 'shopping_recent_days',
|
||||||
|
34
cookbook/static/themes/tandoor.min.css
vendored
34
cookbook/static/themes/tandoor.min.css
vendored
@ -4457,28 +4457,28 @@ input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].bt
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-brand, .navbar-light .navbar-brand:focus, .navbar-light .navbar-brand:hover {
|
.navbar-light .navbar-brand, .navbar-light .navbar-brand:focus, .navbar-light .navbar-brand:hover {
|
||||||
color: rgba(46, 46, 46, .9)
|
color: rgba(0, 0, 0, .9)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-nav .nav-link {
|
.navbar-light .navbar-nav .nav-link {
|
||||||
color: rgba(46, 46, 46, .5)
|
color: rgba(0, 0, 0, .5)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-nav .nav-link:focus, .navbar-light .navbar-nav .nav-link:hover {
|
.navbar-light .navbar-nav .nav-link:focus, .navbar-light .navbar-nav .nav-link:hover {
|
||||||
color: rgba(46, 46, 46, .7)
|
color: rgba(0, 0, 0, .7)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-nav .nav-link.disabled {
|
.navbar-light .navbar-nav .nav-link.disabled {
|
||||||
color: rgba(46, 46, 46, .3)
|
color: rgba(0, 0, 0, .3)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-nav .active > .nav-link, .navbar-light .navbar-nav .nav-link.active, .navbar-light .navbar-nav .nav-link.show, .navbar-light .navbar-nav .show > .nav-link {
|
.navbar-light .navbar-nav .active > .nav-link, .navbar-light .navbar-nav .nav-link.active, .navbar-light .navbar-nav .nav-link.show, .navbar-light .navbar-nav .show > .nav-link {
|
||||||
color: rgba(46, 46, 46, .9)
|
color: rgba(0, 0, 0, .9)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-toggler {
|
.navbar-light .navbar-toggler {
|
||||||
color: rgba(46, 46, 46, .5);
|
color: rgba(0, 0, 0, .5);
|
||||||
border-color: rgba(46, 46, 46, .1)
|
border-color: rgba(0, 0, 0, .1)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-toggler-icon {
|
.navbar-light .navbar-toggler-icon {
|
||||||
@ -4486,11 +4486,7 @@ input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].bt
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-text {
|
.navbar-light .navbar-text {
|
||||||
color: rgba(46, 46, 46, .5)
|
color: rgba(0, 0, 0, .5)
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-light .navbar-text a, .navbar-light .navbar-text a:focus, .navbar-light .navbar-text a:hover {
|
|
||||||
color: rgba(46, 46, 46, .9)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-brand, .navbar-dark .navbar-brand:focus, .navbar-dark .navbar-brand:hover {
|
.navbar-dark .navbar-brand, .navbar-dark .navbar-brand:focus, .navbar-dark .navbar-brand:hover {
|
||||||
@ -4498,24 +4494,24 @@ input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].bt
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-nav .nav-link {
|
.navbar-dark .navbar-nav .nav-link {
|
||||||
color: hsla(0, 0%, 18%, .5)
|
color: rgba(255, 255, 255, .5)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-nav .nav-link:focus, .navbar-dark .navbar-nav .nav-link:hover {
|
.navbar-dark .navbar-nav .nav-link:focus, .navbar-dark .navbar-nav .nav-link:hover {
|
||||||
color: hsla(0, 0%, 18%, .75)
|
color: rgba(255, 255, 255, .75)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-nav .nav-link.disabled {
|
.navbar-dark .navbar-nav .nav-link.disabled {
|
||||||
color: hsla(0, 0%, 18%, .25)
|
color: rgba(255, 255, 255, .25)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-nav .active > .nav-link, .navbar-dark .navbar-nav .nav-link.active, .navbar-dark .navbar-nav .nav-link.show, .navbar-dark .navbar-nav .show > .nav-link {
|
.navbar-dark .navbar-nav .active > .nav-link, .navbar-dark .navbar-nav .nav-link.active, .navbar-dark .navbar-nav .nav-link.show, .navbar-dark .navbar-nav .show > .nav-link {
|
||||||
color: #2e2e2e
|
color: #FFF
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-toggler {
|
.navbar-dark .navbar-toggler {
|
||||||
color: rgba(46, 46, 46, 0.5);
|
color: rgba(255, 255, 255, .5);
|
||||||
border-color: rgba(46, 46, 46, 0.5);
|
border-color: rgba(255, 255, 255, .1)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-toggler-icon {
|
.navbar-dark .navbar-toggler-icon {
|
||||||
@ -4523,7 +4519,7 @@ input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].bt
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-text {
|
.navbar-dark .navbar-text {
|
||||||
color: hsla(0, 0%, 18%, .5)
|
color: rgba(255, 255, 255, .5)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-text a, .navbar-dark .navbar-text a:focus, .navbar-dark .navbar-text a:hover {
|
.navbar-dark .navbar-text a, .navbar-dark .navbar-text a:focus, .navbar-dark .navbar-text a:hover {
|
||||||
|
32
cookbook/static/themes/tandoor_dark.min.css
vendored
32
cookbook/static/themes/tandoor_dark.min.css
vendored
@ -4460,28 +4460,28 @@ input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].bt
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-brand, .navbar-light .navbar-brand:focus, .navbar-light .navbar-brand:hover {
|
.navbar-light .navbar-brand, .navbar-light .navbar-brand:focus, .navbar-light .navbar-brand:hover {
|
||||||
color: rgba(46, 46, 46, .9)
|
color: rgba(0, 0, 0, .9)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-nav .nav-link {
|
.navbar-light .navbar-nav .nav-link {
|
||||||
color: rgba(46, 46, 46, .5)
|
color: rgba(0, 0, 0, .5)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-nav .nav-link:focus, .navbar-light .navbar-nav .nav-link:hover {
|
.navbar-light .navbar-nav .nav-link:focus, .navbar-light .navbar-nav .nav-link:hover {
|
||||||
color: rgba(46, 46, 46, .7)
|
color: rgba(0, 0, 0, .7)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-nav .nav-link.disabled {
|
.navbar-light .navbar-nav .nav-link.disabled {
|
||||||
color: rgba(46, 46, 46, .3)
|
color: rgba(0, 0, 0, .3)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-nav .active > .nav-link, .navbar-light .navbar-nav .nav-link.active, .navbar-light .navbar-nav .nav-link.show, .navbar-light .navbar-nav .show > .nav-link {
|
.navbar-light .navbar-nav .active > .nav-link, .navbar-light .navbar-nav .nav-link.active, .navbar-light .navbar-nav .nav-link.show, .navbar-light .navbar-nav .show > .nav-link {
|
||||||
color: rgba(46, 46, 46, .9)
|
color: rgba(0, 0, 0, .9)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-toggler {
|
.navbar-light .navbar-toggler {
|
||||||
color: rgba(46, 46, 46, .5);
|
color: rgba(0, 0, 0, .5);
|
||||||
border-color: rgba(46, 46, 46, .1)
|
border-color: rgba(0, 0, 0, .1)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-toggler-icon {
|
.navbar-light .navbar-toggler-icon {
|
||||||
@ -4489,11 +4489,11 @@ input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].bt
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-text {
|
.navbar-light .navbar-text {
|
||||||
color: rgba(46, 46, 46, .5)
|
color: rgba(0, 0, 0, .5)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-light .navbar-text a, .navbar-light .navbar-text a:focus, .navbar-light .navbar-text a:hover {
|
.navbar-light .navbar-text a, .navbar-light .navbar-text a:focus, .navbar-light .navbar-text a:hover {
|
||||||
color: rgba(46, 46, 46, .9)
|
color: rgba(0, 0, 0, .9)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-brand, .navbar-dark .navbar-brand:focus, .navbar-dark .navbar-brand:hover {
|
.navbar-dark .navbar-brand, .navbar-dark .navbar-brand:focus, .navbar-dark .navbar-brand:hover {
|
||||||
@ -4501,24 +4501,24 @@ input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].bt
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-nav .nav-link {
|
.navbar-dark .navbar-nav .nav-link {
|
||||||
color: hsla(0, 0%, 5%, .5)
|
color: rgba(255, 255, 255, .5)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-nav .nav-link:focus, .navbar-dark .navbar-nav .nav-link:hover {
|
.navbar-dark .navbar-nav .nav-link:focus, .navbar-dark .navbar-nav .nav-link:hover {
|
||||||
color: hsla(0, 0%, 5%, .75)
|
color: rgba(255, 255, 255, .75)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-nav .nav-link.disabled {
|
.navbar-dark .navbar-nav .nav-link.disabled {
|
||||||
color: hsla(0, 0%, 5%, .25)
|
color: rgba(255, 255, 255, .25)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-nav .active > .nav-link, .navbar-dark .navbar-nav .nav-link.active, .navbar-dark .navbar-nav .nav-link.show, .navbar-dark .navbar-nav .show > .nav-link {
|
.navbar-dark .navbar-nav .active > .nav-link, .navbar-dark .navbar-nav .nav-link.active, .navbar-dark .navbar-nav .nav-link.show, .navbar-dark .navbar-nav .show > .nav-link {
|
||||||
color: #1E1E1E
|
color: #FFF
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-toggler {
|
.navbar-dark .navbar-toggler {
|
||||||
color: rgba(46, 46, 46, 0.5);
|
color: rgba(255, 255, 255, .5);
|
||||||
border-color: rgba(46, 46, 46, 0.5);
|
border-color: rgba(255, 255, 255, .1)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-toggler-icon {
|
.navbar-dark .navbar-toggler-icon {
|
||||||
@ -4526,7 +4526,7 @@ input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].bt
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-text {
|
.navbar-dark .navbar-text {
|
||||||
color: hsla(0, 0%, 18%, .5)
|
color: rgba(255, 255, 255, .5)
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-dark .navbar-text a, .navbar-dark .navbar-text a:focus, .navbar-dark .navbar-text a:hover {
|
.navbar-dark .navbar-text a, .navbar-dark .navbar-text a:focus, .navbar-dark .navbar-text a:hover {
|
||||||
|
@ -33,6 +33,11 @@
|
|||||||
|
|
||||||
<!-- Bootstrap 4 -->
|
<!-- Bootstrap 4 -->
|
||||||
<link id="id_main_css" href="{% theme_url request %}" rel="stylesheet">
|
<link id="id_main_css" href="{% theme_url request %}" rel="stylesheet">
|
||||||
|
{% if request.user.is_authenticated and request.space.custom_space_theme %}
|
||||||
|
<link id="id_custom_css" href="{% custom_theme request %}" rel="stylesheet">
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<link href="{% static 'css/app.min.css' %}" rel="stylesheet">
|
<link href="{% static 'css/app.min.css' %}" rel="stylesheet">
|
||||||
<script src="{% static 'js/jquery-3.5.1.min.js' %}"></script>
|
<script src="{% static 'js/jquery-3.5.1.min.js' %}"></script>
|
||||||
|
|
||||||
@ -74,12 +79,12 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg {% nav_color request %}"
|
<nav class="navbar navbar-expand-lg {% nav_text_color request %}"
|
||||||
id="id_main_nav"
|
id="id_main_nav"
|
||||||
style="{% sticky_nav request %}">
|
style="{% sticky_nav request %}; background-color: {% nav_bg_color request %}">
|
||||||
|
|
||||||
{% if not request.user.userpreference.left_handed %}
|
{% if not request.user.userpreference.left_handed %}
|
||||||
{% if not request.user.is_authenticated or request.user.userpreference.theme == request.user.userpreference.TANDOOR %}
|
{% if not request.user.is_authenticated or request.user.userpreference.nav_show_logo %}
|
||||||
<a class="navbar-brand p-0 me-2 justify-content-center" href="{% base_path request 'base' %}"
|
<a class="navbar-brand p-0 me-2 justify-content-center" href="{% base_path request 'base' %}"
|
||||||
aria-label="Tandoor">
|
aria-label="Tandoor">
|
||||||
<img class="brand-icon" src="{% logo_url request %}" alt="Logo">
|
<img class="brand-icon" src="{% logo_url request %}" alt="Logo">
|
||||||
@ -93,7 +98,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
{% if request.user.userpreference.left_handed %}
|
{% if request.user.userpreference.left_handed %}
|
||||||
{% if not request.user.is_authenticated or request.user.userpreference.theme == request.user.userpreference.TANDOOR %}
|
{% if not request.user.is_authenticated or request.user.userpreference.nav_show_logo %}
|
||||||
<a class="navbar-brand p-0 me-2 justify-content-center" href="{% base_path request 'base' %}"
|
<a class="navbar-brand p-0 me-2 justify-content-center" href="{% base_path request 'base' %}"
|
||||||
aria-label="Tandoor">
|
aria-label="Tandoor">
|
||||||
<img class="brand-icon" src="{% logo_url request %}" alt="Logo">
|
<img class="brand-icon" src="{% logo_url request %}" alt="Logo">
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
from django import template
|
from django import template
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
|
from django_scopes import scopes_disabled
|
||||||
|
|
||||||
from cookbook.models import UserPreference
|
from cookbook.models import UserPreference, UserFile, Space
|
||||||
from recipes.settings import STICKY_NAV_PREF_DEFAULT
|
from recipes.settings import STICKY_NAV_PREF_DEFAULT
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
@ -19,35 +20,58 @@ def theme_url(request):
|
|||||||
UserPreference.TANDOOR: 'themes/tandoor.min.css',
|
UserPreference.TANDOOR: 'themes/tandoor.min.css',
|
||||||
UserPreference.TANDOOR_DARK: 'themes/tandoor_dark.min.css',
|
UserPreference.TANDOOR_DARK: 'themes/tandoor_dark.min.css',
|
||||||
}
|
}
|
||||||
if request.user.userpreference.theme in themes:
|
# if request.space.custom_space_theme:
|
||||||
return static(themes[request.user.userpreference.theme])
|
# return request.space.custom_space_theme.file.url
|
||||||
|
|
||||||
|
if request.space.space_theme in themes:
|
||||||
|
return static(themes[request.space.space_theme])
|
||||||
else:
|
else:
|
||||||
raise AttributeError
|
if request.user.userpreference.theme in themes:
|
||||||
|
return static(themes[request.user.userpreference.theme])
|
||||||
|
else:
|
||||||
|
raise AttributeError
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def custom_theme(request):
|
||||||
|
if request.user.is_authenticated and request.space.custom_space_theme:
|
||||||
|
return request.space.custom_space_theme.file.url
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def logo_url(request):
|
def logo_url(request):
|
||||||
if request.user.is_authenticated and getattr(getattr(request, "space", {}), 'image', None):
|
if request.user.is_authenticated and getattr(getattr(request, "space", {}), 'nav_logo', None):
|
||||||
return request.space.image.file.url
|
return request.space.nav_logo.file.url
|
||||||
else:
|
else:
|
||||||
return static('assets/brand_logo.png')
|
return static('assets/brand_logo.png')
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def nav_color(request):
|
def nav_bg_color(request):
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
return 'navbar-light bg-primary'
|
return '#ddbf86'
|
||||||
|
|
||||||
if request.user.userpreference.nav_color.lower() in ['light', 'warning', 'info', 'success']:
|
|
||||||
return f'navbar-light bg-{request.user.userpreference.nav_color.lower()}'
|
|
||||||
else:
|
else:
|
||||||
return f'navbar-dark bg-{request.user.userpreference.nav_color.lower()}'
|
if request.space.nav_bg_color:
|
||||||
|
return request.space.nav_bg_color
|
||||||
|
else:
|
||||||
|
return request.user.userpreference.nav_bg_color
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def nav_text_color(request):
|
||||||
|
type_mapping = {Space.DARK: 'navbar-light', Space.LIGHT: 'navbar-dark'} # inverted since navbar-dark means the background
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
return 'navbar-dark'
|
||||||
|
else:
|
||||||
|
if request.space.nav_text_color != Space.BLANK:
|
||||||
|
return type_mapping[request.space.nav_text_color]
|
||||||
|
else:
|
||||||
|
return type_mapping[request.user.userpreference.nav_text_color]
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def sticky_nav(request):
|
def sticky_nav(request):
|
||||||
if (not request.user.is_authenticated and STICKY_NAV_PREF_DEFAULT) or \
|
if (not request.user.is_authenticated and STICKY_NAV_PREF_DEFAULT) or (request.user.is_authenticated and request.user.userpreference.nav_sticky):
|
||||||
(request.user.is_authenticated and request.user.userpreference.sticky_navbar): # noqa: E501
|
|
||||||
return 'position: sticky; top: 0; left: 0; z-index: 1000;'
|
return 'position: sticky; top: 0; left: 0; z-index: 1000;'
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
@ -1340,7 +1340,7 @@ class CustomAuthToken(ObtainAuthToken):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class RecipeUrlImportView(ObtainAuthToken):
|
class RecipeUrlImportView(APIView):
|
||||||
throttle_classes = [RecipeImportThrottle]
|
throttle_classes = [RecipeImportThrottle]
|
||||||
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
|
permission_classes = [CustomIsUser & CustomTokenHasReadWriteScope]
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
|
|
||||||
<div class="row mt-2">
|
<b-row class="mt-2">
|
||||||
<div class="col col-12">
|
<b-col cols="12">
|
||||||
<div v-if="space !== undefined">
|
<div v-if="space !== undefined">
|
||||||
<h6><i class="fas fa-book"></i> {{ $t('Recipes') }}</h6>
|
<h6><i class="fas fa-book"></i> {{ $t('Recipes') }}</h6>
|
||||||
<b-progress height="1.5rem" :max="space.max_recipes" variant="success" :striped="true">
|
<b-progress height="1.5rem" :max="space.max_recipes" variant="success" :striped="true">
|
||||||
@ -32,13 +32,13 @@
|
|||||||
</b-progress>
|
</b-progress>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</b-col>
|
||||||
</div>
|
</b-row>
|
||||||
|
|
||||||
<div class="row mt-4">
|
<b-row class="mt-4">
|
||||||
<div class="col col-12">
|
<b-col cols="12">
|
||||||
<div v-if="user_spaces !== undefined">
|
<div v-if="user_spaces !== undefined">
|
||||||
<h4 class="mt-2"><i class="fas fa-users"></i> {{ $t('Users') }}</h4>
|
<h4><i class="fas fa-users"></i> {{ $t('Users') }}</h4>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -51,14 +51,14 @@
|
|||||||
<td>{{ us.user.display_name }}</td>
|
<td>{{ us.user.display_name }}</td>
|
||||||
<td>
|
<td>
|
||||||
<generic-multiselect
|
<generic-multiselect
|
||||||
class="input-group-text m-0 p-0"
|
class="input-group-text m-0 p-0"
|
||||||
@change="us.groups = $event.val; updateUserSpace(us)"
|
@change="us.groups = $event.val; updateUserSpace(us)"
|
||||||
label="name"
|
label="name"
|
||||||
:initial_selection="us.groups"
|
:initial_selection="us.groups"
|
||||||
:model="Models.GROUP"
|
:model="Models.GROUP"
|
||||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||||
:limit="10"
|
:limit="10"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@ -67,12 +67,12 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</b-col>
|
||||||
</div>
|
</b-row>
|
||||||
|
|
||||||
|
|
||||||
<div class="row mt-2">
|
<b-row class="mt-2">
|
||||||
<div class="col col-12">
|
<b-col cols="12">
|
||||||
<div v-if="invite_links !== undefined">
|
<div v-if="invite_links !== undefined">
|
||||||
<h4 class="mt-2"><i class="fas fa-users"></i> {{ $t('Invites') }}</h4>
|
<h4 class="mt-2"><i class="fas fa-users"></i> {{ $t('Invites') }}</h4>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
@ -90,14 +90,14 @@
|
|||||||
<td>{{ il.email }}</td>
|
<td>{{ il.email }}</td>
|
||||||
<td>
|
<td>
|
||||||
<generic-multiselect
|
<generic-multiselect
|
||||||
class="input-group-text m-0 p-0"
|
class="input-group-text m-0 p-0"
|
||||||
@change="il.group = $event.val;"
|
@change="il.group = $event.val;"
|
||||||
label="name"
|
label="name"
|
||||||
:initial_single_selection="il.group"
|
:initial_single_selection="il.group"
|
||||||
:model="Models.GROUP"
|
:model="Models.GROUP"
|
||||||
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
|
||||||
:limit="10"
|
:limit="10"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td><input type="date" v-model="il.valid_until" class="form-control"></td>
|
<td><input type="date" v-model="il.valid_until" class="form-control"></td>
|
||||||
@ -131,43 +131,102 @@
|
|||||||
</table>
|
</table>
|
||||||
<b-button variant="primary" @click="show_invite_create = true">{{ $t('Create') }}</b-button>
|
<b-button variant="primary" @click="show_invite_create = true">{{ $t('Create') }}</b-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</b-col>
|
||||||
</div>
|
</b-row>
|
||||||
|
|
||||||
<div class="row mt-4" v-if="space !== undefined">
|
<b-row class="mt-4" v-if="space !== undefined">
|
||||||
<div class="col col-12">
|
<b-col cols="12">
|
||||||
<h4 class="mt-2"><i class="fas fa-cogs"></i> {{ $t('Settings') }}</h4>
|
<h4>{{ $t('Cosmetic') }}</h4>
|
||||||
|
<b-alert variant="warning" show>{{ $t('Space_Cosmetic_Settings') }}</b-alert>
|
||||||
|
|
||||||
<label>{{ $t('Message') }}</label>
|
<b-form-group :label="$t('Image')" :description="$t('CustomImageHelp')">
|
||||||
<b-form-textarea v-model="space.message"></b-form-textarea>
|
<generic-multiselect :initial_single_selection="space.image"
|
||||||
|
:model="Models.USERFILE"
|
||||||
|
:multiple="false"
|
||||||
|
@change="space.image = $event.val;"></generic-multiselect>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
<label>{{ $t('Image') }}</label>
|
<b-form-group :label="$t('Logo')" :description="$t('CustomNavLogoHelp')">
|
||||||
<generic-multiselect :initial_single_selection="space.image"
|
<generic-multiselect :initial_single_selection="space.nav_logo"
|
||||||
:model="Models.USERFILE"
|
:model="Models.USERFILE"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
@change="space.image = $event.val;"></generic-multiselect>
|
@change="space.nav_logo = $event.val;"></generic-multiselect>
|
||||||
<br/>
|
</b-form-group>
|
||||||
|
|
||||||
<label>{{ $t('FoodInherit') }}</label>
|
<b-form-group :label="$t('Theme')">
|
||||||
<generic-multiselect :initial_selection="space.food_inherit"
|
<b-form-select v-model="space.space_theme">
|
||||||
:model="Models.FOOD_INHERIT_FIELDS"
|
<b-form-select-option value="BLANK">----</b-form-select-option>
|
||||||
@change="space.food_inherit = $event.val;">
|
<b-form-select-option value="TANDOOR">Tandoor</b-form-select-option>
|
||||||
</generic-multiselect>
|
<b-form-select-option value="TANDOOR_DARK">Tandoor Dark (Beta)</b-form-select-option>
|
||||||
<span class="text-muted small">{{ $t('food_inherit_info') }}</span><br/>
|
<b-form-select-option value="BOOTSTRAP">Bootstrap</b-form-select-option>
|
||||||
|
<b-form-select-option value="DARKLY">Darkly</b-form-select-option>
|
||||||
|
<b-form-select-option value="FLATLY">Flatly</b-form-select-option>
|
||||||
|
<b-form-select-option value="SUPERHERO">Superhero</b-form-select-option>
|
||||||
|
</b-form-select>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
<a class="btn btn-success" @click="updateSpace()">{{ $t('Update') }}</a><br/>
|
|
||||||
<a class="btn btn-warning mt-1" @click="resetInheritance()">{{ $t('reset_food_inheritance') }}</a><br/>
|
|
||||||
<span class="text-muted small">{{ $t('reset_food_inheritance_info') }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<b-form-group :label="$t('CustomTheme')" :description="$t('CustomThemeHelp')">
|
||||||
<div class="col-md-12">
|
<generic-multiselect :initial_single_selection="space.custom_space_theme"
|
||||||
|
:model="Models.USERFILE"
|
||||||
|
:multiple="false"
|
||||||
|
@change="space.custom_space_theme = $event.val;"></generic-multiselect>
|
||||||
|
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :label="$t('Nav_Color')" :description="$t('Nav_Color_Help')">
|
||||||
|
<b-input-group>
|
||||||
|
<b-form-input type="color" v-model="space.nav_bg_color"></b-form-input>
|
||||||
|
<b-input-group-append>
|
||||||
|
<b-button @click="space.nav_bg_color = ''">{{ $t('Reset') }}</b-button>
|
||||||
|
</b-input-group-append>
|
||||||
|
</b-input-group>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :label="$t('Nav_Text_Mode')" :description="$t('Nav_Text_Mode_Help')">
|
||||||
|
<b-form-select v-model="space.nav_text_color">
|
||||||
|
<b-form-select-option value="BLANK">----</b-form-select-option>
|
||||||
|
<b-form-select-option value="LIGHT">Light</b-form-select-option>
|
||||||
|
<b-form-select-option value="DARK">Dark</b-form-select-option>
|
||||||
|
</b-form-select>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-button variant="success" @click="updateSpace()">{{ $t('Update') }}</b-button>
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
|
|
||||||
|
<b-row class="mt-4" v-if="space !== undefined">
|
||||||
|
<b-col cols="12">
|
||||||
|
<h4><i class="fas fa-cogs"></i> {{ $t('Settings') }}</h4>
|
||||||
|
|
||||||
|
<b-form-group :label="$t('Message')">
|
||||||
|
<b-form-textarea v-model="space.message"></b-form-textarea>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :label="$t('FoodInherit')" :description="$t('food_inherit_info')">
|
||||||
|
<generic-multiselect :initial_selection="space.food_inherit"
|
||||||
|
:model="Models.FOOD_INHERIT_FIELDS"
|
||||||
|
@change="space.food_inherit = $event.val;">
|
||||||
|
</generic-multiselect>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :description="$t('reset_food_inheritance_info')">
|
||||||
|
<b-button-group class="mt-2">
|
||||||
|
<b-button variant="success" @click="updateSpace()">{{ $t('Update') }}</b-button>
|
||||||
|
<b-button variant="warning" @click="resetInheritance()">{{ $t('reset_food_inheritance') }}</b-button>
|
||||||
|
</b-button-group>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
</b-col>
|
||||||
|
</b-row>
|
||||||
|
|
||||||
|
<b-row class="mt-4">
|
||||||
|
<b-col cols="12">
|
||||||
<h4>{{ $t('Open_Data_Import') }}</h4>
|
<h4>{{ $t('Open_Data_Import') }}</h4>
|
||||||
<open-data-import-component></open-data-import-component>
|
<open-data-import-component></open-data-import-component>
|
||||||
</div>
|
</b-col>
|
||||||
|
|
||||||
</div>
|
</b-row>
|
||||||
|
|
||||||
|
|
||||||
<div class="row mt-4">
|
<div class="row mt-4">
|
||||||
|
@ -52,31 +52,41 @@
|
|||||||
<b-form-group :label="$t('Theme')">
|
<b-form-group :label="$t('Theme')">
|
||||||
<b-form-select v-model="user_preferences.theme" @change="updateSettings(true);">
|
<b-form-select v-model="user_preferences.theme" @change="updateSettings(true);">
|
||||||
<b-form-select-option value="TANDOOR">Tandoor</b-form-select-option>
|
<b-form-select-option value="TANDOOR">Tandoor</b-form-select-option>
|
||||||
|
<b-form-select-option value="TANDOOR_DARK">Tandoor Dark (Beta)</b-form-select-option>
|
||||||
<b-form-select-option value="BOOTSTRAP">Bootstrap</b-form-select-option>
|
<b-form-select-option value="BOOTSTRAP">Bootstrap</b-form-select-option>
|
||||||
<b-form-select-option value="DARKLY">Darkly</b-form-select-option>
|
<b-form-select-option value="DARKLY">Darkly</b-form-select-option>
|
||||||
<b-form-select-option value="FLATLY">Flatly</b-form-select-option>
|
<b-form-select-option value="FLATLY">Flatly</b-form-select-option>
|
||||||
<b-form-select-option value="SUPERHERO">Superhero</b-form-select-option>
|
<b-form-select-option value="SUPERHERO">Superhero</b-form-select-option>
|
||||||
<b-form-select-option value="TANDOOR_DARK">Tandoor Dark (INCOMPLETE)</b-form-select-option>
|
|
||||||
</b-form-select>
|
</b-form-select>
|
||||||
|
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
<b-form-group :description="$t('Sticky_Nav_Help')">
|
|
||||||
<b-form-checkbox v-model="user_preferences.sticky_navbar" @change="updateSettings(true);">
|
|
||||||
{{ $t('Sticky_Nav') }}
|
|
||||||
</b-form-checkbox>
|
|
||||||
</b-form-group>
|
|
||||||
<b-form-group :label="$t('Nav_Color')" :description="$t('Nav_Color_Help')">
|
<b-form-group :label="$t('Nav_Color')" :description="$t('Nav_Color_Help')">
|
||||||
<b-form-select v-model="user_preferences.nav_color" @change="updateSettings(true);">
|
<b-input-group>
|
||||||
<b-form-select-option value="PRIMARY">Primary</b-form-select-option>
|
<b-form-input type="color" v-model="user_preferences.nav_bg_color" @change="updateSettings(true);"></b-form-input>
|
||||||
<b-form-select-option value="SECONDARY">Secondary</b-form-select-option>
|
<b-input-group-append>
|
||||||
<b-form-select-option value="SUCCESS">Success</b-form-select-option>
|
<b-button @click="user_preferences.nav_bg_color = '#ddbf86'; updateSettings(true);">{{ $t('Reset') }}</b-button>
|
||||||
<b-form-select-option value="INFO">Info</b-form-select-option>
|
</b-input-group-append>
|
||||||
<b-form-select-option value="WARNING">Warning</b-form-select-option>
|
</b-input-group>
|
||||||
<b-form-select-option value="DANGER">Danger</b-form-select-option>
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :label="$t('Nav_Text_Mode')" :description="$t('Nav_Text_Mode_Help')">
|
||||||
|
<b-form-select v-model="user_preferences.nav_text_color" @change="updateSettings(true);">
|
||||||
<b-form-select-option value="LIGHT">Light</b-form-select-option>
|
<b-form-select-option value="LIGHT">Light</b-form-select-option>
|
||||||
<b-form-select-option value="DARK">Dark</b-form-select-option>
|
<b-form-select-option value="DARK">Dark</b-form-select-option>
|
||||||
</b-form-select>
|
</b-form-select>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :description="$t('Show_Logo_Help')">
|
||||||
|
<b-form-checkbox v-model="user_preferences.nav_show_logo" @change="updateSettings(true);">
|
||||||
|
{{ $t('Show_Logo') }}
|
||||||
|
</b-form-checkbox>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
<b-form-group :description="$t('Sticky_Nav_Help')">
|
||||||
|
<b-form-checkbox v-model="user_preferences.nav_sticky" @change="updateSettings(true);">
|
||||||
|
{{ $t('Sticky_Nav') }}
|
||||||
|
</b-form-checkbox>
|
||||||
</b-form-group>
|
</b-form-group>
|
||||||
|
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user