Merge branch 'develop' into HomeAssistantConnector

This commit is contained in:
Mikhail Epifanov 2024-01-17 22:45:02 +01:00 committed by Mikhail Epifanov
commit 5f9d59317b
No known key found for this signature in database
16 changed files with 284 additions and 190 deletions

View File

@ -11,8 +11,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-02-09 18:01+0100\n" "POT-Creation-Date: 2021-02-09 18:01+0100\n"
"PO-Revision-Date: 2023-07-31 14:19+0000\n" "PO-Revision-Date: 2024-01-09 12:07+0000\n"
"Last-Translator: Mára Štěpánek <stepanekm7@gmail.com>\n" "Last-Translator: Jan Kubošek <kuboja@outlook.cz>\n"
"Language-Team: Czech <http://translate.tandoor.dev/projects/tandoor/" "Language-Team: Czech <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/cs/>\n" "recipes-backend/cs/>\n"
"Language: cs\n" "Language: cs\n"
@ -190,7 +190,7 @@ msgid ""
"Leave empty for dropbox and enter only base url for nextcloud " "Leave empty for dropbox and enter only base url for nextcloud "
"(<code>/remote.php/webdav/</code> is added automatically)" "(<code>/remote.php/webdav/</code> is added automatically)"
msgstr "" msgstr ""
"Pro dropbox ponechejte nevyplňené pole. Pro nextcloud použijte pouze " "Pro dropbox ponechejte nevyplné pole. Pro nextcloud použijte pouze "
"základní url (<code>/remote.php/webdav/</code> bude přidán automaticky)." "základní url (<code>/remote.php/webdav/</code> bude přidán automaticky)."
#: .\cookbook\forms.py:263 #: .\cookbook\forms.py:263
@ -529,7 +529,7 @@ msgstr "Dávková úprava receptu"
#: .\cookbook\templates\batch\edit.html:20 #: .\cookbook\templates\batch\edit.html:20
msgid "Add the specified keywords to all recipes containing a word" msgid "Add the specified keywords to all recipes containing a word"
msgstr "Přidat štítek ke všem receptům, které obsahují specifické slovo." msgstr "Přidat štítek ke všem receptům, které obsahují specifické slovo"
#: .\cookbook\templates\batch\monitor.html:6 .\cookbook\views\edit.py:66 #: .\cookbook\templates\batch\monitor.html:6 .\cookbook\views\edit.py:66
msgid "Sync" msgid "Sync"

View File

@ -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),
),
]

View File

@ -1,4 +1,4 @@
# Generated by Django 4.2.7 on 2024-01-17 21:12 # Generated by Django 4.2.7 on 2024-01-17 21:49
import cookbook.models import cookbook.models
from django.conf import settings from django.conf import settings
@ -11,7 +11,7 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('cookbook', '0207_space_logo_color_128_space_logo_color_144_and_more'), ('cookbook', '0208_space_app_name_userpreference_max_owned_spaces'),
] ]
operations = [ operations = [

View File

@ -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')
@ -435,7 +435,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)
@ -460,6 +460,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)

View File

@ -686,9 +686,8 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
properties = validated_data.pop('properties', None) properties = validated_data.pop('properties', None)
obj, created = Food.objects.get_or_create( obj, created = Food.objects.get_or_create(name=name, plural_name=plural_name, space=space, properties_food_unit=properties_food_unit,
name=name, plural_name=plural_name, space=space, properties_food_unit=properties_food_unit, defaults=validated_data)
defaults=validated_data)
if properties and len(properties) > 0: if properties and len(properties) > 0:
for p in properties: for p in properties:
@ -1276,9 +1275,8 @@ class InviteLinkSerializer(WritableNestedModelSerializer):
if obj.email: if obj.email:
try: try:
if InviteLink.objects.filter( if InviteLink.objects.filter(space=self.context['request'].space,
space=self.context['request'].space, created_at__gte=datetime.now() - timedelta(hours=4)).count() < 20:
created_at__gte=datetime.now() - timedelta(hours=4)).count() < 20:
message = _('Hello') + '!\n\n' + _('You have been invited by ') + escape( message = _('Hello') + '!\n\n' + _('You have been invited by ') + escape(
self.context['request'].user.get_user_display_name()) self.context['request'].user.get_user_display_name())
message += _(' to join their Tandoor Recipes space ') + escape( message += _(' to join their Tandoor Recipes space ') + escape(
@ -1433,15 +1431,12 @@ class RecipeExportSerializer(WritableNestedModelSerializer):
class RecipeShoppingUpdateSerializer(serializers.ModelSerializer): class RecipeShoppingUpdateSerializer(serializers.ModelSerializer):
list_recipe = serializers.IntegerField( list_recipe = serializers.IntegerField(write_only=True, allow_null=True, required=False,
write_only=True, allow_null=True, required=False, help_text=_("Existing shopping list to update"))
help_text=_("Existing shopping list to update")) ingredients = serializers.IntegerField(write_only=True, allow_null=True, required=False, help_text=_(
ingredients = serializers.IntegerField( "List of ingredient IDs from the recipe to add, if not provided all ingredients will be added."))
write_only=True, allow_null=True, required=False, help_text=_( servings = serializers.IntegerField(default=1, write_only=True, allow_null=True, required=False, help_text=_(
"List of ingredient IDs from the recipe to add, if not provided all ingredients will be added.")) "Providing a list_recipe ID and servings of 0 will delete that shopping list."))
servings = serializers.IntegerField(
default=1, write_only=True, allow_null=True, required=False, help_text=_(
"Providing a list_recipe ID and servings of 0 will delete that shopping list."))
class Meta: class Meta:
model = Recipe model = Recipe
@ -1449,15 +1444,12 @@ class RecipeShoppingUpdateSerializer(serializers.ModelSerializer):
class FoodShoppingUpdateSerializer(serializers.ModelSerializer): class FoodShoppingUpdateSerializer(serializers.ModelSerializer):
amount = serializers.IntegerField( amount = serializers.IntegerField(write_only=True, allow_null=True, required=False,
write_only=True, allow_null=True, required=False, help_text=_("Amount of food to add to the shopping list"))
help_text=_("Amount of food to add to the shopping list")) unit = serializers.IntegerField(write_only=True, allow_null=True, required=False,
unit = serializers.IntegerField( help_text=_("ID of unit to use for the shopping list"))
write_only=True, allow_null=True, required=False, delete = serializers.ChoiceField(choices=['true'], write_only=True, allow_null=True, allow_blank=True,
help_text=_("ID of unit to use for the shopping list")) help_text=_("When set to true will delete all food from active shopping lists."))
delete = serializers.ChoiceField(
choices=['true'], write_only=True, allow_null=True, allow_blank=True,
help_text=_("When set to true will delete all food from active shopping lists."))
class Meta: class Meta:
model = Recipe model = Recipe

View File

@ -1,55 +0,0 @@
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load i18n %}
{% load django_tables2 %}
{% block title %}{% trans 'List' %}{% endblock %}
{% block content %}
{% if request.resolver_match.url_name in 'list_storage,list_recipe_import,list_sync_log' %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'data_sync' %}">{% trans 'External Recipes' %}</a></li>
<li class="breadcrumb-item active" aria-current="page">{{ title }}</li>
</ol>
</nav>
{% endif %}
<div class="table-container">
<span class="col col-md-9">
<h3 style="margin-bottom: 2vh">{{ title }} {% trans 'List' %}</h3>
</span>
{% if filter %}
<br/>
<br/>
<form action="." method="get">
{% csrf_token %}
{{ filter.form|crispy }}
<button type="submit" class="btn btn-success">{% trans 'Filter' %}</button>
</form>
{% endif %}
{% if import_btn %}
<a href="{% url 'data_batch_import' %}" class="btn btn-warning">{% trans 'Import all' %}</a>
<br/>
<br/>
{% endif %}
{% for table in tables %}
<span class="col col-md-9">
<h3 style="margin-bottom: 2vh">{{ table.attrs.table_name }} {% trans 'List' %}
{% if table.attrs.create_url %}
<a href="{% url table.attrs.create_url %}"> <i class="fas fa-plus-circle"></i> </a>
{% endif %}
</h3>
</span>
{% render_table table %}
{% endfor %}
</div>
{% endblock content %}

View File

@ -10,17 +10,17 @@ register = template.Library()
@register.simple_tag @register.simple_tag
def theme_values(request): def theme_values(request):
return get_theming_values(request)
def get_theming_values(request):
space = None space = None
if space in request and 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():
space = Space.objects.filter(id=UNAUTHENTICATED_THEME_FROM_SPACE).first() space = Space.objects.filter(id=UNAUTHENTICATED_THEME_FROM_SPACE).first()
return get_theming_values(space, request.user)
def get_theming_values(space, user):
themes = { themes = {
UserPreference.BOOTSTRAP: 'themes/bootstrap.min.css', UserPreference.BOOTSTRAP: 'themes/bootstrap.min.css',
UserPreference.FLATLY: 'themes/flatly.min.css', UserPreference.FLATLY: 'themes/flatly.min.css',
@ -46,31 +46,34 @@ def get_theming_values(space, user):
'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 user.is_authenticated: if request.user.userpreference.theme in themes:
if user.userpreference.theme in themes: tv['theme'] = static(themes[request.user.userpreference.theme])
tv['theme'] = static(themes[user.userpreference.theme]) if request.user.userpreference.nav_bg_color:
if user.userpreference.nav_bg_color: tv['nav_bg_color'] = request.user.userpreference.nav_bg_color
tv['nav_bg_color'] = user.userpreference.nav_bg_color if request.user.userpreference.nav_text_color and request.user.userpreference.nav_text_color in nav_text_type_mapping:
if user.userpreference.nav_text_color and user.userpreference.nav_text_color in nav_text_type_mapping: tv['nav_text_class'] = nav_text_type_mapping[request.user.userpreference.nav_text_color]
tv['nav_text_class'] = nav_text_type_mapping[user.userpreference.nav_text_color] if not request.user.userpreference.nav_sticky:
if not user.userpreference.nav_sticky:
tv['sticky_nav'] = '' tv['sticky_nav'] = ''
if space: if space:
for logo in list(tv.keys()): for logo in list(tv.keys()):
if logo.startswith('logo_color_') and getattr(space, logo, None): if logo.startswith('logo_color_') and getattr(space, logo, None):
tv[logo] = getattr(space, logo).file.url tv[logo] = getattr(space, logo).file.url
if space.custom_space_theme:
tv['custom_theme'] = space.custom_space_theme.file.url if space.custom_space_theme:
if space.space_theme in themes: tv['custom_theme'] = space.custom_space_theme.file.url
tv['theme'] = static(themes[space.space_theme]) if space.space_theme in themes:
if space.nav_logo: tv['theme'] = static(themes[space.space_theme])
tv['nav_logo'] = space.nav_logo.file.url if space.nav_logo:
if space.nav_bg_color: tv['nav_logo'] = space.nav_logo.file.url
tv['nav_bg_color'] = space.nav_bg_color if space.nav_bg_color:
if space.nav_text_color and space.nav_text_color in nav_text_type_mapping: tv['nav_bg_color'] = space.nav_bg_color
tv['nav_text_class'] = nav_text_type_mapping[space.nav_text_color] 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]
if space.app_name:
tv['app_name'] = space.app_name
return tv return tv

View File

@ -1,36 +1,68 @@
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_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
def test_theming_function(space_1, u1_s1): def test_theming_function(space_1, u1_s1):
user = auth.get_user(u1_s1)
# uf = UserFile.objects.create(name='test', space=space_1, created_by=user) #TODO add file tests # uf = UserFile.objects.create(name='test', space=space_1, created_by=user) #TODO add file tests
user = auth.get_user(u1_s1)
request = RequestFactory()
request.user = auth.get_user(u1_s1)
request.space = space_1
assert get_theming_values(space_1, user)['theme'] == static('themes/tandoor.min.css') # defaults apply without setting anything (user preference is automatically created with these defaults)
assert get_theming_values(space_1, user)['nav_bg_color'] == '#ddbf86' assert get_theming_values(request)['theme'] == static('themes/tandoor.min.css')
assert get_theming_values(space_1, user)['nav_text_class'] == 'navbar-light' assert get_theming_values(request)['nav_bg_color'] == '#ddbf86'
assert get_theming_values(space_1, user)['nav_logo'] == static('assets/brand_logo.png') assert get_theming_values(request)['nav_text_class'] == 'navbar-light'
assert get_theming_values(space_1, user)['sticky_nav'] == 'position: sticky; top: 0; left: 0; z-index: 1000;' 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)['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()
assert get_theming_values(space_1, user)['theme'] == static('themes/tandoor_dark.min.css') request = RequestFactory()
assert get_theming_values(space_1, user)['nav_bg_color'] == '#ffffff' request.user = auth.get_user(u1_s1)
assert get_theming_values(space_1, user)['nav_text_class'] == 'navbar-dark' request.space = space_1
assert get_theming_values(space_1, user)['sticky_nav'] == ''
# 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)['nav_bg_color'] == '#ffffff'
assert get_theming_values(request)['nav_text_class'] == 'navbar-dark'
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()
assert get_theming_values(space_1, user)['theme'] == static('themes/bootstrap.min.css') request = RequestFactory()
assert get_theming_values(space_1, user)['nav_bg_color'] == '#000000' request.user = auth.get_user(u1_s1)
assert get_theming_values(space_1, user)['nav_text_class'] == 'navbar-light' 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)['nav_bg_color'] == '#000000'
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')

View File

@ -51,7 +51,7 @@ router.register(r'shopping-list-recipe', api.ShoppingListRecipeViewSet)
router.register(r'space', api.SpaceViewSet) router.register(r'space', api.SpaceViewSet)
router.register(r'step', api.StepViewSet) router.register(r'step', api.StepViewSet)
router.register(r'storage', api.StorageViewSet) router.register(r'storage', api.StorageViewSet)
router.register(r'home-assistant-config', api.ConnectorConfigConfigViewSet) router.register(r'connector-config', api.ConnectorConfigConfigViewSet)
router.register(r'supermarket', api.SupermarketViewSet) router.register(r'supermarket', api.SupermarketViewSet)
router.register(r'supermarket-category', api.SupermarketCategoryViewSet) router.register(r'supermarket-category', api.SupermarketCategoryViewSet)
router.register(r'supermarket-category-relation', api.SupermarketCategoryRelationViewSet) router.register(r'supermarket-category-relation', api.SupermarketCategoryRelationViewSet)

View File

@ -87,12 +87,11 @@ def invite_link(request):
InviteLink.objects.filter(valid_until__gte=datetime.today(), used_by=None, space=request.space).all()) InviteLink.objects.filter(valid_until__gte=datetime.today(), used_by=None, space=request.space).all())
RequestConfig(request, paginate={'per_page': 25}).configure(table) RequestConfig(request, paginate={'per_page': 25}).configure(table)
return render( return render(request, 'generic/list_template.html', {
request, 'generic/list_template.html', { 'title': _("Invite Links"),
'title': _("Invite Links"), 'table': table,
'table': table, 'create_url': 'new_invite_link'
'create_url': 'new_invite_link' })
})
@group_required('user') @group_required('user')
@ -212,7 +211,7 @@ def custom_filter(request):
def user_file(request): def user_file(request):
try: try:
current_file_size_mb = UserFile.objects.filter(space=request.space).aggregate(Sum('file_size_kb'))[ current_file_size_mb = UserFile.objects.filter(space=request.space).aggregate(Sum('file_size_kb'))[
'file_size_kb__sum'] / 1000 'file_size_kb__sum'] / 1000
except TypeError: except TypeError:
current_file_size_mb = 0 current_file_size_mb = 0

View File

@ -31,6 +31,7 @@ from cookbook.helper.permission_helper import (group_required, has_group_permiss
from cookbook.models import (Comment, CookLog, InviteLink, SearchFields, SearchPreference, from cookbook.models import (Comment, CookLog, InviteLink, SearchFields, SearchPreference,
ShareLink, Space, UserSpace, ViewLog) ShareLink, Space, UserSpace, ViewLog)
from cookbook.tables import CookLogTable, ViewLogTable from cookbook.tables import CookLogTable, ViewLogTable
from cookbook.templatetags.theming_tags import get_theming_values
from cookbook.version_info import VERSION_INFO from cookbook.version_info import VERSION_INFO
from recipes.settings import PLUGINS, BASE_DIR from recipes.settings import PLUGINS, BASE_DIR
@ -78,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,
@ -481,29 +487,24 @@ def report_share_abuse(request, token):
def web_manifest(request): def web_manifest(request):
theme_values = get_theming_values(request)
icons = [ icons = [
{"src": static("/assets/logo_color.svg"), "sizes": "any"}, {"src": theme_values['logo_color_svg'], "sizes": "any"},
{"src": static("/assets/logo_color144.png"), "type": "image/png", "sizes": "144x144"}, {"src": theme_values['logo_color_144'], "type": "image/png", "sizes": "144x144"},
{"src": static("/assets/logo_color512.png"), "type": "image/png", "sizes": "512x512"} {"src": theme_values['logo_color_512'], "type": "image/png", "sizes": "512x512"}
] ]
if request.user.is_authenticated and getattr(request.space, 'logo_color_svg') and getattr(request.space, 'logo_color_144') and getattr(request.space, 'logo_color_512'):
icons = [
{"src": request.space.logo_color_svg.file.url, "sizes": "any"},
{"src": request.space.logo_color_144.file.url, "type": "image/png", "sizes": "144x144"},
{"src": request.space.logo_color_512.file.url, "type": "image/png", "sizes": "512x512"}
]
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",
"background_color": "#ffcb76", "background_color": theme_values['nav_bg_color'],
"display": "standalone", "display": "standalone",
"scope": ".", "scope": ".",
"theme_color": "#ffcb76", "theme_color": theme_values['nav_bg_color'],
"shortcuts": [ "shortcuts": [
{ {
"name": _("Plan"), "name": _("Plan"),

View File

@ -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

View File

@ -57,7 +57,8 @@ 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)))
UNAUTHENTICATED_THEME_FROM_SPACE = 2 #int(os.getenv('UNAUTHENTICATED_THEME_FROM_SPACE', 0)) 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))
# minimum interval that users can set for automatic sync of shopping lists # minimum interval that users can set for automatic sync of shopping lists
SHOPPING_MIN_AUTOSYNC_INTERVAL = int( SHOPPING_MIN_AUTOSYNC_INTERVAL = int(

View File

@ -16,14 +16,14 @@
"file_upload_disabled": "Nahrávání souborů není povoleno pro Váš prostor.", "file_upload_disabled": "Nahrávání souborů není povoleno pro Váš prostor.",
"warning_space_delete": "Můžete smazat váš prostor včetně všech receptů, nákupních seznamů, jídelníčků a všeho ostatního, co jste vytvořili. Tuto akci nemůžete vzít zpět! Jste si jisti, že chcete pokračovat?", "warning_space_delete": "Můžete smazat váš prostor včetně všech receptů, nákupních seznamů, jídelníčků a všeho ostatního, co jste vytvořili. Tuto akci nemůžete vzít zpět! Jste si jisti, že chcete pokračovat?",
"food_inherit_info": "Pole potravin, která budou standardně zděděna.", "food_inherit_info": "Pole potravin, která budou standardně zděděna.",
"step_time_minutes": "Nastavte čas v minutách", "step_time_minutes": "Délka kroku v minutách",
"confirm_delete": "Jste si jisti že chcete odstranit tento {objekt}?", "confirm_delete": "Jste si jisti že chcete odstranit tento {objekt}?",
"import_running": "Probíhá import, čekejte prosím!", "import_running": "Probíhá import, čekejte prosím!",
"all_fields_optional": "Všechna pole jsou nepviná a mohou být ponechána prázdná.", "all_fields_optional": "Všechna pole jsou nepviná a mohou být ponechána prázdná.",
"convert_internal": "Převést na interní recept", "convert_internal": "Převést na interní recept",
"show_only_internal": "Zobrazit pouze interní recepty", "show_only_internal": "Zobrazit pouze interní recepty",
"show_split_screen": "Rozdělené zobrazení", "show_split_screen": "Rozdělené zobrazení",
"Log_Recipe_Cooking": "", "Log_Recipe_Cooking": "Záznam vaření receptu",
"External_Recipe_Image": "Externí obrázek receptu", "External_Recipe_Image": "Externí obrázek receptu",
"Add_to_Shopping": "Přidat k nákupu", "Add_to_Shopping": "Přidat k nákupu",
"Add_to_Plan": "Přidat do jídelníčku", "Add_to_Plan": "Přidat do jídelníčku",
@ -35,8 +35,8 @@
"Hide_as_header": "Skryj jako nadpis", "Hide_as_header": "Skryj jako nadpis",
"Add_nutrition_recipe": "Přidat nutriční hodnoty", "Add_nutrition_recipe": "Přidat nutriční hodnoty",
"Remove_nutrition_recipe": "Smazat nutriční hodnoty", "Remove_nutrition_recipe": "Smazat nutriční hodnoty",
"Copy_template_reference": "", "Copy_template_reference": "Zkopírovat šablonu odkazu",
"Save_and_View": "Uložit & Zobrazit", "Save_and_View": "Uložit a zobrazit",
"Manage_Books": "Spravovat kuchařky", "Manage_Books": "Spravovat kuchařky",
"Meal_Plan": "Jídelníček", "Meal_Plan": "Jídelníček",
"Select_Book": "Vyber kuchařku", "Select_Book": "Vyber kuchařku",
@ -44,19 +44,19 @@
"Recipe_Image": "Obrázek k receptu", "Recipe_Image": "Obrázek k receptu",
"Import_finished": "Import dokončen", "Import_finished": "Import dokončen",
"View_Recipes": "Zobrazit recepty", "View_Recipes": "Zobrazit recepty",
"Log_Cooking": "", "Log_Cooking": "Zaznamenat vaření",
"New_Recipe": "Nový recept", "New_Recipe": "Nový recept",
"Url_Import": "Import pomocí URL odkazu", "Url_Import": "Import pomocí URL odkazu",
"Reset_Search": "Zrušit filtry vyhledávání", "Reset_Search": "Zrušit filtry vyhledávání",
"Recently_Viewed": "Naposledy prohlížené", "Recently_Viewed": "Naposledy prohlížené",
"Load_More": "Načíst další", "Load_More": "Načíst další",
"New_Keyword": "Nové klíčové slovo", "New_Keyword": "Nový štítek",
"Delete_Keyword": "Smazat klíčové slovo", "Delete_Keyword": "Smazat štítek",
"Edit_Keyword": "Upravit klíčové slovo", "Edit_Keyword": "Upravit štítek",
"Edit_Recipe": "Upravit recept", "Edit_Recipe": "Upravit recept",
"Move_Keyword": "Přesunout klíčové slovo", "Move_Keyword": "Přesunout štítek",
"Merge_Keyword": "Sloučit klíčové slovo", "Merge_Keyword": "Sloučit štítek",
"Hide_Keywords": "Skrýt klíčové slovo", "Hide_Keywords": "Skrýt štítek",
"Hide_Recipes": "Skrýt recept", "Hide_Recipes": "Skrýt recept",
"Move_Up": "Nahoru", "Move_Up": "Nahoru",
"Move_Down": "Dolů", "Move_Down": "Dolů",
@ -76,14 +76,14 @@
"Private_Recipe_Help": "Recept můžete zobrazit pouze vy a lidé, se kterými jej sdílíte.", "Private_Recipe_Help": "Recept můžete zobrazit pouze vy a lidé, se kterými jej sdílíte.",
"reusable_help_text": "Má-li pozvánka platit pro více než jednoho uživatele.", "reusable_help_text": "Má-li pozvánka platit pro více než jednoho uživatele.",
"Add_Step": "Přidat krok", "Add_Step": "Přidat krok",
"Keywords": "Klíčová slova", "Keywords": "Štítky",
"Books": "Kuchařky", "Books": "Kuchařky",
"Proteins": "Proteiny", "Proteins": "Proteiny",
"Fats": "Tuky", "Fats": "Tuky",
"Carbohydrates": "Sacharidy", "Carbohydrates": "Sacharidy",
"Calories": "Kalorie", "Calories": "Kalorie",
"Energy": "Energie", "Energy": "Energie",
"Nutrition": "", "Nutrition": "Výživové hodnoty",
"Date": "Datum", "Date": "Datum",
"Share": "Sdílet", "Share": "Sdílet",
"Automation": "Automatizace", "Automation": "Automatizace",
@ -153,7 +153,7 @@
"Hide_Food": "Skrýt potravinu", "Hide_Food": "Skrýt potravinu",
"Food_Alias": "Přezdívka potraviny", "Food_Alias": "Přezdívka potraviny",
"Unit_Alias": "Přezdívka jednotky", "Unit_Alias": "Přezdívka jednotky",
"Keyword_Alias": "Přezdívka klíčového slova", "Keyword_Alias": "Přezdívka štítku",
"Delete_Food": "Smazat potravinu", "Delete_Food": "Smazat potravinu",
"No_ID": "ID nenalezeno, odstranění není možné.", "No_ID": "ID nenalezeno, odstranění není možné.",
"Meal_Plan_Days": "Budoucí jídelníčky", "Meal_Plan_Days": "Budoucí jídelníčky",
@ -162,7 +162,7 @@
"Food": "Potravina", "Food": "Potravina",
"Original_Text": "Původní text", "Original_Text": "Původní text",
"Recipe_Book": "Kuchařka", "Recipe_Book": "Kuchařka",
"del_confirmation_tree": "Jste si jistí, že chcete odstranit {source} se všemi pořazenými ?", "del_confirmation_tree": "Jste si jistí, že chcete odstranit {source} i se všemi potomky?",
"delete_title": "Smazat {type}", "delete_title": "Smazat {type}",
"create_title": "Nový {type}", "create_title": "Nový {type}",
"edit_title": "Upravit {type}", "edit_title": "Upravit {type}",
@ -170,7 +170,7 @@
"Type": "Typ", "Type": "Typ",
"Description": "Popis", "Description": "Popis",
"Recipe": "Recept", "Recipe": "Recept",
"tree_root": "", "tree_root": "Kořen stromu",
"Icon": "Ikona", "Icon": "Ikona",
"Unit": "Jednotka", "Unit": "Jednotka",
"Decimals": "Desetinná místa", "Decimals": "Desetinná místa",
@ -179,7 +179,7 @@
"New_Unit": "Nová jednotka", "New_Unit": "Nová jednotka",
"Create_New_Shopping Category": "Vytvořit novou nákupní kategorii", "Create_New_Shopping Category": "Vytvořit novou nákupní kategorii",
"Create_New_Food": "Přidat novou potravinu", "Create_New_Food": "Přidat novou potravinu",
"Create_New_Keyword": "Přidat nové klíčové slovo", "Create_New_Keyword": "Přidat nový štítek",
"Create_New_Unit": "Přidat novou jednotku", "Create_New_Unit": "Přidat novou jednotku",
"Create_New_Meal_Type": "Přidat nový druh jídla", "Create_New_Meal_Type": "Přidat nový druh jídla",
"Create_New_Shopping_Category": "Přidat novou nákupní kategorii", "Create_New_Shopping_Category": "Přidat novou nákupní kategorii",
@ -254,7 +254,7 @@
"SupermarketCategoriesOnly": "Pouze kategorie obchodu", "SupermarketCategoriesOnly": "Pouze kategorie obchodu",
"MoveCategory": "Přesunout do: ", "MoveCategory": "Přesunout do: ",
"CountMore": "...+{count} víc", "CountMore": "...+{count} víc",
"IgnoreThis": "Nikdy automaticky nepřídávat {food} na nákupní seznam", "IgnoreThis": "Nikdy nepřidávat automaticky {food} na nákupní seznam",
"DelayFor": "Odložit na {hours} hodin", "DelayFor": "Odložit na {hours} hodin",
"Warning": "Varování", "Warning": "Varování",
"NoCategory": "Není vybrána žádná kategorie.", "NoCategory": "Není vybrána žádná kategorie.",
@ -271,7 +271,7 @@
"default_delay": "Výchozí doba odložení v hodinách", "default_delay": "Výchozí doba odložení v hodinách",
"plan_share_desc": "Nové položky v jídelníčku budou automaticky sdíleny s vybranými uživateli.", "plan_share_desc": "Nové položky v jídelníčku budou automaticky sdíleny s vybranými uživateli.",
"shopping_share_desc": "Uživatelé uvidí všechny položky na vašem nákupním seznamu. Abyste viděli položky na jejich seznamu, musí si přidat vás.", "shopping_share_desc": "Uživatelé uvidí všechny položky na vašem nákupním seznamu. Abyste viděli položky na jejich seznamu, musí si přidat vás.",
"shopping_auto_sync_desc": "Nastavením 0 dojde k vypnutí automatické synchronizace. Při prohlížení nákupního seznamu je vždy po uplynutí nastaveného počtu vteřin aktualizován o změny, které mohli provést jiní uživatelé. To je užitečné, pokud nakupujete ve více lidech, ale může používat více dat.", "shopping_auto_sync_desc": "Zadáním hodnoty 0 se automatická synchronizace vypne. Při prohlížení nákupního seznamu se seznam aktualizuje po stanovených sekundách, aby se synchronizovaly změny, které mohl provést někdo jiný. To je užitečné při nakupování s více lidmi, ale spotřebovávají se při tom mobilní data.",
"mealplan_autoadd_shopping_desc": "Automaticky podle jídelníčku přidat ingredience na nákupní seznam.", "mealplan_autoadd_shopping_desc": "Automaticky podle jídelníčku přidat ingredience na nákupní seznam.",
"mealplan_autoexclude_onhand_desc": "Nepřidávat ingredience, které jsou k dispozici, na nákupní seznam, když je vytvořen podle jídelníčku (manuálně nebo automaticky).", "mealplan_autoexclude_onhand_desc": "Nepřidávat ingredience, které jsou k dispozici, na nákupní seznam, když je vytvořen podle jídelníčku (manuálně nebo automaticky).",
"mealplan_autoinclude_related_desc": "Když je nákupní seznam vytvořen podle jídelníčku, přidat i položky z přidružených receptů.", "mealplan_autoinclude_related_desc": "Když je nákupní seznam vytvořen podle jídelníčku, přidat i položky z přidružených receptů.",
@ -280,7 +280,7 @@
"Coming_Soon": "Již brzy", "Coming_Soon": "Již brzy",
"Auto_Planner": "Automatický plánovač", "Auto_Planner": "Automatický plánovač",
"New_Cookbook": "Nová kuchařka", "New_Cookbook": "Nová kuchařka",
"Hide_Keyword": "Skrýt klíčová slova", "Hide_Keyword": "Skrýt štítky",
"Hour": "Hodina", "Hour": "Hodina",
"Hours": "Hodiny", "Hours": "Hodiny",
"Day": "Den", "Day": "Den",
@ -328,7 +328,7 @@
"OnHand_help": "Potravina je v inventáři a nebude automaticky přidána na nákupní seznam. Status \"k dipozici\" je sdílen s nakupujícími uživateli.", "OnHand_help": "Potravina je v inventáři a nebude automaticky přidána na nákupní seznam. Status \"k dipozici\" je sdílen s nakupujícími uživateli.",
"ignore_shopping_help": "Nikdy nepřidávat potravinu na nákupní seznam (např. voda)", "ignore_shopping_help": "Nikdy nepřidávat potravinu na nákupní seznam (např. voda)",
"shopping_category_help": "Obchody mohou být seřazeny a třízeny pomocí nákupních kategorií podle rozvržení uliček a regálů.", "shopping_category_help": "Obchody mohou být seřazeny a třízeny pomocí nákupních kategorií podle rozvržení uliček a regálů.",
"food_recipe_help": "", "food_recipe_help": "Zde uvedený recept bude připojen k jakémukoli jinému receptu, který používá tuto potravinu",
"Foods": "Potraviny", "Foods": "Potraviny",
"Account": "Účet", "Account": "Účet",
"Cosmetic": "Zobrazení", "Cosmetic": "Zobrazení",
@ -338,7 +338,7 @@
"simple_mode": "Jednoduchý režim", "simple_mode": "Jednoduchý režim",
"advanced": "Pokročilé", "advanced": "Pokročilé",
"fields": "Pole", "fields": "Pole",
"show_keywords": "Zobrazit klíčová slova", "show_keywords": "Zobrazit štítky",
"show_foods": "Zobrazit potraviny", "show_foods": "Zobrazit potraviny",
"show_books": "Zobrazit kuchařky", "show_books": "Zobrazit kuchařky",
"show_rating": "Zobrazit hodnocení", "show_rating": "Zobrazit hodnocení",
@ -359,8 +359,8 @@
"times_cooked": "Kolkrát vařeno", "times_cooked": "Kolkrát vařeno",
"date_created": "Datum vytvoření", "date_created": "Datum vytvoření",
"show_sortby": "Zobrazit Seřazeno podle", "show_sortby": "Zobrazit Seřazeno podle",
"search_rank": "", "search_rank": "Skóre shody",
"make_now": "", "make_now": "Udělat hned",
"recipe_filter": "Filtrovat recepty", "recipe_filter": "Filtrovat recepty",
"book_filter_help": "Zahrnout i recepty z filtru stejně jako manuálně přidané.", "book_filter_help": "Zahrnout i recepty z filtru stejně jako manuálně přidané.",
"review_shopping": "Zkontrolovat nákupní položky před uložením", "review_shopping": "Zkontrolovat nákupní položky před uložením",
@ -392,9 +392,9 @@
"search_create_help_text": "Vytvořit nový recept přímo v Tandoor.", "search_create_help_text": "Vytvořit nový recept přímo v Tandoor.",
"warning_duplicate_filter": "Varování: Kvůli technickým omezení může použití několika filtrů se stejnou kombinací (a/nebo/ne) přinést neočekávaný výsledek.", "warning_duplicate_filter": "Varování: Kvůli technickým omezení může použití několika filtrů se stejnou kombinací (a/nebo/ne) přinést neočekávaný výsledek.",
"reset_children": "Resetovat propsání podřízených", "reset_children": "Resetovat propsání podřízených",
"reset_children_help": "", "reset_children_help": "Přepíše všechny potomky hodnotami dle nastavení propisovaných polí. Pokud není nastaveno pole Propisovaná pole podřízených, tak bude nastaveno pole Propsat hodnoty polí na aktuální hodnotu.",
"reset_food_inheritance": "Resetovat propisování", "reset_food_inheritance": "Resetovat propisování",
"reset_food_inheritance_info": "", "reset_food_inheritance_info": "Obnoví u všech potravin propisovaná pole na výchozí hodnotu a nastavit daná pole dle nadřazené položky.",
"substitute_help": "Při hledání podle ingrediencí, které jsou k dispozici, jsou zvažovány náhrady.", "substitute_help": "Při hledání podle ingrediencí, které jsou k dispozici, jsou zvažovány náhrady.",
"substitute_siblings_help": "Všechny potraviny, které sdílejí nadřazenou položku jsou považovány za náhrady.", "substitute_siblings_help": "Všechny potraviny, které sdílejí nadřazenou položku jsou považovány za náhrady.",
"substitute_children_help": "Všechny potraviny, které jsou podřízeny této, jsou považovány za náhražky.", "substitute_children_help": "Všechny potraviny, které jsou podřízeny této, jsou považovány za náhražky.",
@ -425,8 +425,8 @@
"Social_Authentication": "Přihlašování pomocí účtů sociálních sítí", "Social_Authentication": "Přihlašování pomocí účtů sociálních sítí",
"Random Recipes": "Náhodné recepty", "Random Recipes": "Náhodné recepty",
"parameter_count": "Parametr {count}", "parameter_count": "Parametr {count}",
"select_keyword": "Vybrat klíčové slovo", "select_keyword": "Vybrat štítek",
"add_keyword": "Přidat klíčové slovo", "add_keyword": "Přidat štítek",
"select_file": "Vybrat soubor", "select_file": "Vybrat soubor",
"select_recipe": "Vybrat recept", "select_recipe": "Vybrat recept",
"select_unit": "Vybrat jednotku", "select_unit": "Vybrat jednotku",
@ -439,7 +439,7 @@
"Username": "Uživatelské jméno", "Username": "Uživatelské jméno",
"First_name": "Jméno", "First_name": "Jméno",
"Last_name": "Příjmení", "Last_name": "Příjmení",
"Keyword": "Klíčové slovo", "Keyword": "Štítek",
"Advanced": "Rozšířené", "Advanced": "Rozšířené",
"Page": "Stránka", "Page": "Stránka",
"Single": "Jednoduchý", "Single": "Jednoduchý",
@ -479,10 +479,10 @@
"Create Recipe": "Vytvořit recept", "Create Recipe": "Vytvořit recept",
"Import Recipe": "Importovat recept", "Import Recipe": "Importovat recept",
"per_serving": "na porci", "per_serving": "na porci",
"open_data_help_text": "Projekt Tandoor Open Data nabízí komunitou poskytnutá data pro Tandoor. Toto pole je automaticky vyplněno při importu a může být později upraveno.", "open_data_help_text": "Projekt Tandoor Open Data nabízí komunitou poskytnutá otevřená data pro Tandoor. Toto pole se vyplní automaticky při importu a umožňuje budoucí aktualizace.",
"Data_Import_Info": "Rozšiřte svůj prostor o seznamy potravin, jednotek a další spravované komunitou, a vylepšete tak svoji sbírku receptů.", "Data_Import_Info": "Rozšiřte svůj prostor o seznamy potravin, jednotek a dalších položek spravovaných komunitou, a vylepšete tak svoji sbírku receptů.",
"Update_Existing_Data": "Aktualizovat existující data", "Update_Existing_Data": "Aktualizovat existující data",
"Use_Metric": "Používat metrické jednotky", "Use_Metric": "Použít metrické jednotky",
"Learn_More": "Zjistit víc", "Learn_More": "Zjistit víc",
"converted_unit": "Převedená jendotka", "converted_unit": "Převedená jendotka",
"converted_amount": "Převedené množství", "converted_amount": "Převedené množství",
@ -490,9 +490,67 @@
"base_amount": "Základní množství", "base_amount": "Základní množství",
"Datatype": "Datový typ", "Datatype": "Datový typ",
"Number of Objects": "Počet Objektů", "Number of Objects": "Počet Objektů",
"Property": "Vlastnost", "Property": "Nutriční vlastnost",
"Conversion": "Převod", "Conversion": "Převod",
"Properties": "Vlastnosti", "Properties": "Nutriční vlastnosti",
"recipe_property_info": "Můžete také přidávat vlastnosti k Vašim potravinám. Hodnoty budou automaticky přepočteny na základě Vašeho receptu!", "recipe_property_info": "Nutriční hodnoty se automaticky dopočtou podle receptu, pokud zadáte nutriční hodnoty přímo potravinám!",
"total": "celkem" "total": "celkem",
"CustomTheme": "Vlastní téma",
"CustomThemeHelp": "Přepsat styly vybraného motivu nahráním vlastního souboru CSS.",
"CustomLogoHelp": "Nahrajte čtvercové obrázky různých velikostí pro úpravu loga v záložce prohlížeče a v nainstalované webové aplikaci.",
"err_importing_recipe": "Během importu receptu došlo k chybě!",
"Open_Data_Slug": "Identifikátor pro otevřená data",
"Open_Data_Import": "Import otevřených dat",
"FDC_Search": "Vyhledávání v FDC",
"property_type_fdc_hint": "Data z databáze FDC mohou automaticky čerpat pouze typy vlastností se zadaným FDC ID",
"StartDate": "Počáteční datum",
"EndDate": "Konečné datum",
"Welcome": "Vítejte",
"Property_Editor": "Editovat nutriční vlastnosti",
"FDC_ID": "FDC ID",
"FDC_ID_help": "ID v databázi FDC",
"CustomImageHelp": "Nahrajte obrázek, který se zobrazí v přehledu prostoru.",
"CustomNavLogoHelp": "Nahrajte obrázek, který se má zobrazit jako logo v navigačním panelu.",
"CustomLogos": "Vlastní loga",
"OrderInformation": "Položky jsou seřazeny podle čísel od malých po velké.",
"kg": "kilogram [kg] (metrický systém, hmotnost)",
"g": "gram [g] (metrický systém, hmotnost)",
"ounce": "unce [oz] (imperiální systém, hmotnost)",
"pound": "libra (hmotnost)",
"Properties_Food_Unit": "Jednotka nutriční vlastnosti",
"Properties_Food_Amount": "Množství nutriční vlastnosti",
"tsp": "lžička [tsp] (US, objem)",
"imperial_tsp": "lžička imperiální [imp tbsp] (UK, objem)",
"Transpose_Words": "Transponovat slova",
"show_step_ingredients_setting": "Zobrazit ingredience u jednotlivých kroků receptu",
"Logo": "Logo",
"Show_Logo": "Zobrazit logo",
"show_step_ingredients_setting_help": "Zobrazí tabulku ingrediencí vedle kroků receptu. Nastavení se aplikuje při vytváření receptu a následně je možné volbu změnit při úpravě receptu.",
"show_step_ingredients": "Zobrazit ingredience u kroku",
"hide_step_ingredients": "Skrýt ingredience u kroku",
"Show_Logo_Help": "Zobrazit logo Tandoor nebo logo prostoru na navigačním panelu.",
"Nav_Text_Mode_Help": "Pro každé téma se chová jinak.",
"Space_Cosmetic_Settings": "Některá kosmetická nastavení mohou měnit správci prostoru a budou mít přednost před nastavením klienta pro daný prostor.",
"Nav_Text_Mode": "Textový režim navigace",
"show_ingredients_table": "Zobrazit tabulku složek vedle textu kroku",
"pint": "pinta [pt] (US, objem)",
"quart": "quart [qt] (US, objem)",
"imperial_fluid_ounce": "tekutá unce imperiální [imp fl oz] (UK, objem)",
"imperial_pint": "pinta imperiální [imp pt] (UK, objem)",
"imperial_quart": "quart imperiální [imp qt] (UK, objem)",
"gallon": "galon [gal] (US, objem)",
"tbsp": "lžíce [tbsp] (US, objem)",
"imperial_gallon": "galon imperiální [imp gal] (UK, objem)",
"imperial_tbsp": "lžíce imperiální [imp tbsp] (UK, objem)",
"Choose_Category": "Vyberte kategorii",
"Back": "Zpět",
"Food_Replace": "Nahrazení v potravině",
"Unit_Replace": "Nahrazení v jednotce",
"Name_Replace": "Nahrazení v názvu",
"ml": "mililitr [ml] (metrický systém, objem)",
"l": "litr [l] (metrický systém, objem)",
"fluid_ounce": "tekutá unce [fl oz] (US, objem)",
"make_now_count": "Nejvyšší počet chybějících ingrediencí",
"Alignment": "Zarovnání",
"Never_Unit": "Není jednotkou"
} }

View File

@ -536,5 +536,24 @@
"Unit_Replace": "Einheit Ersetzen", "Unit_Replace": "Einheit Ersetzen",
"quart": "\"Quart\" [qt] (US, Volumen)", "quart": "\"Quart\" [qt] (US, Volumen)",
"imperial_quart": "Engl. \"Quart\" [imp qt] (UK, Volumen)", "imperial_quart": "Engl. \"Quart\" [imp qt] (UK, Volumen)",
"err_importing_recipe": "Beim Importieren des Rezeptes ist ein Fehler aufgetreten!" "err_importing_recipe": "Beim Importieren des Rezeptes ist ein Fehler aufgetreten!",
"property_type_fdc_hint": "Nur Nährwerte mit einer FDC ID können automatisch Daten aus der FDC Datenbank beziehen",
"Property_Editor": "Nährwerte bearbeiten",
"CustomTheme": "Benutzerdefiniertes Theme",
"CustomThemeHelp": "Überschreiben Sie die Stile des ausgewählten Themes, indem Sie eine eigene CSS-Datei hochladen.",
"CustomLogoHelp": "Laden Sie quadratische Bilder in verschiedenen Größen hoch, um das Logo im Browsertab und der installierten Webanwendung zu ändern.",
"Show_Logo_Help": "Zeigen Sie das Tandoor- oder Space-Logo in der Navigationsleiste an.",
"Space_Cosmetic_Settings": "Einige optische Einstellungen können von Administratoren des Bereichs geändert werden und setzen die Client-Einstellungen für diesen Bereich außer Kraft.",
"Properties_Food_Amount": "Nährwertangaben",
"Properties_Food_Unit": "Nährwert Einheit",
"FDC_Search": "FDC Suche",
"Logo": "Logo",
"Show_Logo": "Logo anzeigen",
"Nav_Text_Mode": "Navigation Textmodus",
"Nav_Text_Mode_Help": "Verhält sich bei jedem Theme anders.",
"FDC_ID": "FDC ID",
"FDC_ID_help": "FDC Datenbank ID",
"CustomImageHelp": "Laden Sie ein Bild hoch, das in der Space-Übersicht angezeigt werden soll.",
"CustomNavLogoHelp": "Laden Sie ein Bild hoch, das als Logo für die Navigationsleiste verwendet werden soll.",
"CustomLogos": "Individuelle Logos"
} }